Add subscribe features
This commit is contained in:
parent
fd5b7c14fa
commit
92a9a23eb6
|
@ -56,7 +56,7 @@ class StatusesIndex < Chewy::Index
|
|||
field :id, type: 'long'
|
||||
field :account_id, type: 'long'
|
||||
|
||||
field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.media_attachments.map(&:description)).concat(status.preloadable_poll ? status.preloadable_poll.options : []).join("\n\n") } do
|
||||
field :text, type: 'text', value: ->(status) { status.index_text } do
|
||||
field :stemmed, type: 'text', analyzer: 'content'
|
||||
end
|
||||
|
||||
|
|
43
app/controllers/api/v1/account_subscribes_controller.rb
Normal file
43
app/controllers/api/v1/account_subscribes_controller.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::AccountSubscribesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:follows' }, only: [:index, :show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:follows' }, except: [:index, :show]
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_account_subscribe, except: [:index, :create]
|
||||
|
||||
def index
|
||||
@account_subscribes = AccountSubscribe.where(account: current_account).all
|
||||
render json: @account_subscribes, each_serializer: REST::AccountSubscribeSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @account_subscribe, serializer: REST::AccountSubscribeSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@account_subscribe = AccountSubscribe.create!(account_subscribe_params.merge(account: current_account))
|
||||
render json: @account_subscribe, serializer: REST::AccountSubscribeSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@account_subscribe.update!(account_subscribe_params)
|
||||
render json: @account_subscribe, serializer: REST::AccountSubscribeSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@account_subscribe.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account_subscribe
|
||||
@account_subscribe = AccountSubscribe.where(account: current_account).find(params[:id])
|
||||
end
|
||||
|
||||
def account_subscribe_params
|
||||
params.permit(:acct)
|
||||
end
|
||||
end
|
34
app/controllers/api/v1/domain_subscribes_controller.rb
Normal file
34
app/controllers/api/v1/domain_subscribes_controller.rb
Normal file
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::DomainSubscribesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:follows' }, only: :index
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:follows' }, except: :index
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_domain_subscribe, except: [:index, :create]
|
||||
|
||||
def index
|
||||
@domain_subscribes = DomainSubscribe.where(account: current_account).all
|
||||
render json: @domain_subscribes, each_serializer: REST::DomainSubscribeSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@domain_subscribe = DomainSubscribe.create!(domain_subscribe_params.merge(account: current_account))
|
||||
render json: @domain_subscribe, serializer: REST::DomainSubscribeSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@domain_subscribe.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_domain_subscribe
|
||||
@domain_subscribe = DomainSubscribe.where(account: current_account).find(params[:id])
|
||||
end
|
||||
|
||||
def domain_subscribe_params
|
||||
params.permit(:domain, :list_id, :exclude_reblog)
|
||||
end
|
||||
end
|
43
app/controllers/api/v1/follow_tags_controller.rb
Normal file
43
app/controllers/api/v1/follow_tags_controller.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FollowTagsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:follows' }, only: [:index, :show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:follows' }, except: [:index, :show]
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_follow_tag, except: [:index, :create]
|
||||
|
||||
def index
|
||||
@follow_tags = FollowTag.where(account: current_account).all
|
||||
render json: @follow_tags, each_serializer: REST::FollowTagSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @follow_tag, serializer: REST::FollowTagSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@follow_tag = FollowTag.create!(follow_tag_params.merge(account: current_account))
|
||||
render json: @follow_tag, serializer: REST::FollowTagSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@follow_tag.update!(follow_tag_params)
|
||||
render json: @follow_tag, serializer: REST::FollowTagSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@follow_tag.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_follow_tag
|
||||
@follow_tag = FollowTag.where(account: current_account).find(params[:id])
|
||||
end
|
||||
|
||||
def follow_tag_params
|
||||
params.permit(:name)
|
||||
end
|
||||
end
|
49
app/controllers/api/v1/keyword_subscribes_controller.rb
Normal file
49
app/controllers/api/v1/keyword_subscribes_controller.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::KeywordSubscribesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:follows' }, only: [:index, :show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:follows' }, except: [:index, :show]
|
||||
before_action :require_user!
|
||||
before_action :set_keyword_subscribes, only: :index
|
||||
before_action :set_keyword_subscribe, only: [:show, :update, :destroy]
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: @keyword_subscribes, each_serializer: REST::KeywordSubscribesSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@keyword_subscribe = current_account.keyword_subscribes.create!(resource_params)
|
||||
render json: @keyword_subscribe, serializer: REST::KeywordSubscribesSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @keyword_subscribe, serializer: REST::KeywordSubscribesSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@keyword_subscribe.update!(resource_params)
|
||||
render json: @keyword_subscribe, serializer: REST::KeywordSubscribesSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@keyword_subscribe.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_keyword_subscribes
|
||||
@keyword_subscribes = current_account.keyword_subscribes
|
||||
end
|
||||
|
||||
def set_keyword_subscribe
|
||||
@keyword_subscribe = current_account.keyword_subscribes.find(params[:id])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.permit(:name, :keyword, :exclude_keyword, :ignorecase, :regexp, :ignore_block, :disabled, :list_id)
|
||||
end
|
||||
end
|
55
app/controllers/settings/account_subscribes_controller.rb
Normal file
55
app/controllers/settings/account_subscribes_controller.rb
Normal file
|
@ -0,0 +1,55 @@
|
|||
# frozen_string_literal: true
|
||||
class Settings::AccountSubscribesController < Settings::BaseController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_account_subscribings, only: :index
|
||||
|
||||
class AccountInput
|
||||
include ActiveModel::Model
|
||||
include ActiveModel::Attributes
|
||||
|
||||
attribute :acct, :string
|
||||
end
|
||||
|
||||
def index
|
||||
@account_input = AccountInput.new
|
||||
end
|
||||
|
||||
def create
|
||||
acct = account_subscribe_params[:acct].strip
|
||||
acct = acct[1..-1] if acct.start_with?("@")
|
||||
|
||||
begin
|
||||
target_account = AccountSubscribeService.new.call(current_account, acct)
|
||||
rescue
|
||||
target_account = nil
|
||||
end
|
||||
|
||||
if target_account
|
||||
redirect_to settings_account_subscribes_path
|
||||
else
|
||||
set_account_subscribings
|
||||
|
||||
render :index
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
target_account = current_account.active_subscribes.find(params[:id]).target_account
|
||||
UnsubscribeAccountService.new.call(current_account, target_account)
|
||||
redirect_to settings_account_subscribes_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account_subscribings
|
||||
@account_subscribings = current_account.active_subscribes.order(:updated_at).reject(&:new_record?).map do |subscribing|
|
||||
{id: subscribing.id, acct: subscribing.target_account.acct}
|
||||
end
|
||||
end
|
||||
|
||||
def account_subscribe_params
|
||||
params.require(:account_input).permit(:acct)
|
||||
end
|
||||
end
|
73
app/controllers/settings/domain_subscribes_controller.rb
Normal file
73
app/controllers/settings/domain_subscribes_controller.rb
Normal file
|
@ -0,0 +1,73 @@
|
|||
# frozen_string_literal: true
|
||||
class Settings::DomainSubscribesController < Settings::BaseController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_lists, only: [:index, :new, :edit, :update]
|
||||
before_action :set_domain_subscribes, only: :index
|
||||
before_action :set_domain_subscribe, only: [:edit, :update, :destroy]
|
||||
|
||||
def index
|
||||
@domain_subscribe = DomainSubscribe.new
|
||||
end
|
||||
|
||||
def new
|
||||
@domain_subscribe = current_account.domain_subscribes.build
|
||||
end
|
||||
|
||||
def create
|
||||
@domain_subscribe = current_account.domain_subscribes.new(domain_subscribe_params)
|
||||
|
||||
if @domain_subscribe.save
|
||||
redirect_to settings_domain_subscribes_path
|
||||
else
|
||||
set_domain_subscribe
|
||||
|
||||
render :index
|
||||
end
|
||||
end
|
||||
|
||||
def edit; end
|
||||
|
||||
def update
|
||||
if @domain_subscribe.update(domain_subscribe_params)
|
||||
redirect_to settings_domain_subscribes_path
|
||||
else
|
||||
render action: :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@domain_subscribe.destroy!
|
||||
redirect_to settings_domain_subscribes_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_domain_subscribe
|
||||
@domain_subscribe = current_account.domain_subscribes.find(params[:id])
|
||||
end
|
||||
|
||||
def set_domain_subscribes
|
||||
@domain_subscribes = current_account.domain_subscribes.includes(:list).order('list_id NULLS FIRST', :domain).reject(&:new_record?)
|
||||
end
|
||||
|
||||
def set_lists
|
||||
@lists = List.where(account: current_account).all
|
||||
end
|
||||
|
||||
def domain_subscribe_params
|
||||
new_params = resource_params.permit!.to_h
|
||||
|
||||
if resource_params[:list_id] == '-1'
|
||||
list = List.find_or_create_by!({ account: current_account, title: new_params[:domain] })
|
||||
new_params.merge!({list_id: list.id})
|
||||
end
|
||||
|
||||
new_params
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:domain_subscribe).permit(:domain, :list_id, :exclude_reblog)
|
||||
end
|
||||
end
|
44
app/controllers/settings/follow_tags_controller.rb
Normal file
44
app/controllers/settings/follow_tags_controller.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::FollowTagsController < Settings::BaseController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_follow_tags, only: :index
|
||||
before_action :set_follow_tag, except: [:index, :create]
|
||||
|
||||
def index
|
||||
@follow_tag = FollowTag.new
|
||||
end
|
||||
|
||||
def create
|
||||
@follow_tag = current_account.follow_tags.new(follow_tag_params)
|
||||
|
||||
if @follow_tag.save
|
||||
redirect_to settings_follow_tags_path
|
||||
else
|
||||
set_follow_tags
|
||||
|
||||
render :index
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@follow_tag.destroy!
|
||||
redirect_to settings_follow_tags_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_follow_tag
|
||||
@follow_tag = current_account.follow_tags.find(params[:id])
|
||||
end
|
||||
|
||||
def set_follow_tags
|
||||
@follow_tags = current_account.follow_tags.order(:updated_at).reject(&:new_record?)
|
||||
end
|
||||
|
||||
def follow_tag_params
|
||||
params.require(:follow_tag).permit(:name)
|
||||
end
|
||||
end
|
78
app/controllers/settings/keyword_subscribes_controller.rb
Normal file
78
app/controllers/settings/keyword_subscribes_controller.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::KeywordSubscribesController < ApplicationController
|
||||
include Authorization
|
||||
|
||||
layout 'admin'
|
||||
|
||||
before_action :set_lists, only: [:index, :new, :edit, :update]
|
||||
before_action :set_keyword_subscribes, only: :index
|
||||
before_action :set_keyword_subscribe, only: [:edit, :update, :destroy]
|
||||
before_action :set_body_classes
|
||||
|
||||
def index
|
||||
@keyword_subscribe = KeywordSubscribe.new
|
||||
end
|
||||
|
||||
def new
|
||||
@keyword_subscribe = current_account.keyword_subscribes.build
|
||||
end
|
||||
|
||||
def create
|
||||
@keyword_subscribe = current_account.keyword_subscribes.build(keyword_subscribe_params)
|
||||
|
||||
if @keyword_subscribe.save
|
||||
redirect_to settings_keyword_subscribes_path
|
||||
else
|
||||
render action: :new
|
||||
end
|
||||
end
|
||||
|
||||
def edit; end
|
||||
|
||||
def update
|
||||
if @keyword_subscribe.update(keyword_subscribe_params)
|
||||
redirect_to settings_keyword_subscribes_path
|
||||
else
|
||||
render action: :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@keyword_subscribe.destroy
|
||||
redirect_to settings_keyword_subscribes_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_keyword_subscribe
|
||||
@keyword_subscribe = current_account.keyword_subscribes.find(params[:id])
|
||||
end
|
||||
|
||||
def set_keyword_subscribes
|
||||
@keyword_subscribes = current_account.keyword_subscribes.includes(:list).order('list_id NULLS FIRST', :name).reject(&:new_record?)
|
||||
end
|
||||
|
||||
def set_lists
|
||||
@lists = List.where(account: current_account).all
|
||||
end
|
||||
|
||||
def keyword_subscribe_params
|
||||
new_params = resource_params.permit!.to_h
|
||||
|
||||
if resource_params[:list_id] == '-1'
|
||||
list = List.find_or_create_by!({ account: current_account, title: new_params[:name].presence || "keyword_#{Time.now.strftime('%Y%m%d%H%M%S')}" })
|
||||
new_params.merge!({list_id: list.id})
|
||||
end
|
||||
|
||||
new_params
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:keyword_subscribe).permit(:name, :keyword, :exclude_keyword, :ignorecase, :regexp, :ignore_block, :disabled, :list_id)
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
@body_classes = 'admin'
|
||||
end
|
||||
end
|
7
app/helpers/lists_helper.rb
Normal file
7
app/helpers/lists_helper.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
module ListsHelper
|
||||
def home_list_new(lists)
|
||||
items = { nil => t('column.home') }
|
||||
items.merge!(lists&.pluck(:id, :title).to_h)
|
||||
items.merge!({ -1 => t('lists.add_new') })
|
||||
end
|
||||
end
|
|
@ -916,3 +916,7 @@ a.name-tag,
|
|||
.dashboard__counters.admin-account-counters {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.exclude-keyword {
|
||||
color: $error-value-color;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
th.nowrap,
|
||||
td.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&.inline-table {
|
||||
& > tbody > tr:nth-child(odd) {
|
||||
& > td,
|
||||
|
|
|
@ -100,10 +100,10 @@ class FeedManager
|
|||
# @param [Account] from_account
|
||||
# @param [Account] into_account
|
||||
# @return [void]
|
||||
def merge_into_home(from_account, into_account)
|
||||
def merge_into_home(from_account, into_account, public_only = false)
|
||||
timeline_key = key(:home, into_account.id)
|
||||
aggregate = into_account.user&.aggregates_reblogs?
|
||||
query = from_account.statuses.where(visibility: [:public, :unlisted, :private]).includes(:preloadable_poll, reblog: :account).limit(FeedManager::MAX_ITEMS / 4)
|
||||
query = from_account.statuses.where(visibility: public_only ? :public : [:public, :unlisted, :private]).includes(:preloadable_poll, reblog: :account).limit(FeedManager::MAX_ITEMS / 4)
|
||||
|
||||
if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4
|
||||
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
|
||||
|
@ -360,21 +360,32 @@ class FeedManager
|
|||
|
||||
return true if check_for_blocks.any? { |target_account_id| crutches[:blocking][target_account_id] || crutches[:muting][target_account_id] }
|
||||
|
||||
if status.reply? && !status.in_reply_to_account_id.nil? # Filter out if it's a reply
|
||||
should_filter = !crutches[:following][status.in_reply_to_account_id] # and I'm not following the person it's a reply to
|
||||
should_filter &&= receiver_id != status.in_reply_to_account_id # and it's not a reply to me
|
||||
should_filter &&= status.account_id != status.in_reply_to_account_id # and it's not a self-reply
|
||||
if status.reblog? # Filter out a reblog
|
||||
should_filter = crutches[:hiding_reblogs][status.account_id] # if the reblogger's reblogs are suppressed
|
||||
should_filter ||= crutches[:domain_blocking][status.account.domain] # or the reblogger's domain is blocked
|
||||
should_filter ||= crutches[:blocked_by][status.reblog.account_id] # or if the author of the reblogged status is blocking me
|
||||
should_filter ||= crutches[:domain_blocking_r][status.reblog.account.domain] # or the author's domain is blocked
|
||||
|
||||
return !!should_filter
|
||||
elsif status.reblog? # Filter out a reblog
|
||||
should_filter = crutches[:hiding_reblogs][status.account_id] # if the reblogger's reblogs are suppressed
|
||||
should_filter ||= crutches[:blocked_by][status.reblog.account_id] # or if the author of the reblogged status is blocking me
|
||||
should_filter ||= crutches[:domain_blocking][status.reblog.account.domain] # or the author's domain is blocked
|
||||
else
|
||||
if status.reply? # Filter out a reply
|
||||
should_filter = !crutches[:following][status.in_reply_to_account_id] # and I'm not following the person it's a reply to
|
||||
should_filter &&= receiver_id != status.in_reply_to_account_id # and it's not a reply to me
|
||||
should_filter &&= status.account_id != status.in_reply_to_account_id # and it's not a self-reply
|
||||
should_filter &&= !status.tags.any? { |tag| crutches[:following_tag_by][tag.id] } # and It's not follow tag
|
||||
should_filter &&= !KeywordSubscribe.match?(status.index_text, account_id: receiver_id) # and It's not subscribe keywords
|
||||
should_filter &&= !crutches[:domain_subscribe][status.account.domain] # and It's not domain subscribes
|
||||
|
||||
return true if should_filter
|
||||
end
|
||||
|
||||
should_filter = crutches[:domain_blocking][status.account.domain]
|
||||
should_filter &&= !crutches[:following][status.account_id]
|
||||
should_filter &&= !crutches[:account_subscribe][status.account_id]
|
||||
should_filter &&= !KeywordSubscribe.match?(status.index_text, account_id: receiver_id, as_ignore_block: true)
|
||||
|
||||
return !!should_filter
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# Check if status should not be added to the mentions feed
|
||||
|
@ -563,13 +574,16 @@ class FeedManager
|
|||
arr
|
||||
end
|
||||
|
||||
crutches[:following] = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:in_reply_to_account_id).compact).pluck(:target_account_id).index_with(true)
|
||||
crutches[:hiding_reblogs] = Follow.where(account_id: receiver_id, target_account_id: statuses.map { |s| s.account_id if s.reblog? }.compact, show_reblogs: false).pluck(:target_account_id).index_with(true)
|
||||
crutches[:blocking] = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
|
||||
crutches[:muting] = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
|
||||
crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.map { |s| s.reblog&.account&.domain }.compact).pluck(:domain).index_with(true)
|
||||
crutches[:blocked_by] = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| s.reblog&.account_id }.compact).pluck(:account_id).index_with(true)
|
||||
|
||||
crutches[:following] = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:in_reply_to_account_id).compact).pluck(:target_account_id).index_with(true)
|
||||
crutches[:hiding_reblogs] = Follow.where(account_id: receiver_id, target_account_id: statuses.map { |s| s.account_id if s.reblog? }.compact, show_reblogs: false).pluck(:target_account_id).index_with(true)
|
||||
crutches[:blocking] = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
|
||||
crutches[:muting] = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
|
||||
crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.map { |s| s.account&.domain }.compact).pluck(:domain).index_with(true)
|
||||
crutches[:domain_blocking_r] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.map { |s| s.reblog&.account&.domain }.compact).pluck(:domain).index_with(true)
|
||||
crutches[:blocked_by] = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| s.reblog&.account_id }.compact).pluck(:account_id).index_with(true)
|
||||
crutches[:following_tag_by] = FollowTag.where(account_id: receiver_id, tag: statuses.map { |s| s.tags }.flatten.uniq.compact).pluck(:tag_id).index_with(true)
|
||||
crutches[:domain_subscribe] = DomainSubscribe.where(account_id: receiver_id, list_id: nil, domain: statuses.map { |s| s&.account&.domain }.compact).pluck(:domain).index_with(true)
|
||||
crutches[:account_subscribe] = AccountSubscribe.where(account_id: receiver_id, target_account_id: statuses.map(&:account_id).compact).pluck(:target_account_id).index_with(true)
|
||||
crutches
|
||||
end
|
||||
end
|
||||
|
|
23
app/models/account_subscribe.rb
Normal file
23
app/models/account_subscribe.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: account_subscribes
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# account_id :bigint(8)
|
||||
# target_account_id :bigint(8)
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# list_id :bigint(8)
|
||||
#
|
||||
|
||||
class AccountSubscribe < ApplicationRecord
|
||||
belongs_to :account
|
||||
belongs_to :target_account, class_name: 'Account'
|
||||
belongs_to :list, optional: true
|
||||
|
||||
validates :account_id, uniqueness: { scope: [:target_account_id, :list_id] }
|
||||
|
||||
scope :recent, -> { reorder(id: :desc) }
|
||||
scope :subscribed_lists, ->(account) { AccountSubscribe.where(target_account_id: account.id).where.not(list_id: nil).select(:list_id).uniq }
|
||||
|
||||
end
|
|
@ -61,6 +61,13 @@ module AccountAssociations
|
|||
has_and_belongs_to_many :tags
|
||||
has_many :favourite_tags, -> { includes(:tag) }, dependent: :destroy, inverse_of: :account
|
||||
has_many :featured_tags, -> { includes(:tag) }, dependent: :destroy, inverse_of: :account
|
||||
has_many :follow_tags, -> { includes(:tag) }, dependent: :destroy, inverse_of: :account
|
||||
|
||||
# KeywordSubscribes
|
||||
has_many :keyword_subscribes, inverse_of: :account, dependent: :destroy
|
||||
|
||||
# DomainSubscribes
|
||||
has_many :domain_subscribes, inverse_of: :account, dependent: :destroy
|
||||
|
||||
# Account deletion requests
|
||||
has_one :deletion_request, class_name: 'AccountDeletionRequest', inverse_of: :account, dependent: :destroy
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module AccountCounters
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
ALLOWED_COUNTER_KEYS = %i(statuses_count following_count followers_count).freeze
|
||||
ALLOWED_COUNTER_KEYS = %i(statuses_count following_count followers_count subscribing_count).freeze
|
||||
|
||||
included do
|
||||
has_one :account_stat, inverse_of: :account
|
||||
|
|
|
@ -98,6 +98,13 @@ module AccountInteractions
|
|||
has_many :conversation_mutes, dependent: :destroy
|
||||
has_many :domain_blocks, class_name: 'AccountDomainBlock', dependent: :destroy
|
||||
has_many :announcement_mutes, dependent: :destroy
|
||||
|
||||
# Subscribers
|
||||
has_many :active_subscribes, class_name: 'AccountSubscribe', foreign_key: 'account_id', dependent: :destroy
|
||||
has_many :passive_subscribes, class_name: 'AccountSubscribe', foreign_key: 'target_account_id', dependent: :destroy
|
||||
|
||||
has_many :subscribing, through: :active_subscribes, source: :target_account
|
||||
has_many :subscribers, through: :passive_subscribes, source: :account
|
||||
end
|
||||
|
||||
def follow!(other_account, reblogs: nil, notify: nil, uri: nil, rate_limit: false, bypass_limit: false)
|
||||
|
@ -183,6 +190,10 @@ module AccountInteractions
|
|||
block&.destroy
|
||||
end
|
||||
|
||||
def subscribe!(other_account)
|
||||
active_subscribes.find_or_create_by!(target_account: other_account)
|
||||
end
|
||||
|
||||
def following?(other_account)
|
||||
active_relationships.where(target_account: other_account).exists?
|
||||
end
|
||||
|
@ -243,12 +254,22 @@ module AccountInteractions
|
|||
account_pins.where(target_account: account).exists?
|
||||
end
|
||||
|
||||
def subscribing?(other_account)
|
||||
active_subscribes.where(target_account: other_account).exists?
|
||||
end
|
||||
|
||||
def followers_for_local_distribution
|
||||
followers.local
|
||||
.joins(:user)
|
||||
.where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago)
|
||||
end
|
||||
|
||||
def subscribers_for_local_distribution
|
||||
subscribers.local
|
||||
.joins(:user)
|
||||
.where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago)
|
||||
end
|
||||
|
||||
def lists_for_local_distribution
|
||||
lists.joins(account: :user)
|
||||
.where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago)
|
||||
|
|
24
app/models/domain_subscribe.rb
Normal file
24
app/models/domain_subscribe.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: domain_subscribes
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# account_id :bigint(8)
|
||||
# list_id :bigint(8)
|
||||
# domain :string default(""), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# exclude_reblog :boolean default(TRUE)
|
||||
#
|
||||
|
||||
class DomainSubscribe < ApplicationRecord
|
||||
belongs_to :account
|
||||
belongs_to :list, optional: true
|
||||
|
||||
validates :domain, presence: true
|
||||
validates :account_id, uniqueness: { scope: [:domain, :list_id] }
|
||||
|
||||
scope :domain_to_home, ->(domain) { where(domain: domain).where(list_id: nil) }
|
||||
scope :domain_to_list, ->(domain) { where(domain: domain).where.not(list_id: nil) }
|
||||
scope :with_reblog, ->(reblog) { where(exclude_reblog: false) if reblog }
|
||||
end
|
|
@ -11,6 +11,7 @@
|
|||
# show_reblogs :boolean default(TRUE), not null
|
||||
# uri :string
|
||||
# notify :boolean default(FALSE), not null
|
||||
# private :boolean default(TRUE), not null
|
||||
#
|
||||
|
||||
class Follow < ApplicationRecord
|
||||
|
|
|
@ -30,6 +30,7 @@ class FollowRequest < ApplicationRecord
|
|||
|
||||
def authorize!
|
||||
account.follow!(target_account, reblogs: show_reblogs, notify: notify, uri: uri, bypass_limit: true)
|
||||
UnsubscribeAccountService.new.call(account, target_account) if account.subscribing?(target_account)
|
||||
MergeWorker.perform_async(target_account.id, account.id) if account.local?
|
||||
destroy!
|
||||
end
|
||||
|
|
27
app/models/follow_tag.rb
Normal file
27
app/models/follow_tag.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: follow_tags
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# account_id :bigint(8)
|
||||
# tag_id :bigint(8)
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# list_id :bigint(8)
|
||||
#
|
||||
|
||||
class FollowTag < ApplicationRecord
|
||||
belongs_to :account, inverse_of: :follow_tags, required: true
|
||||
belongs_to :tag, inverse_of: :follow_tags, required: true
|
||||
belongs_to :list, optional: true
|
||||
|
||||
delegate :name, to: :tag, allow_nil: true
|
||||
|
||||
validates_associated :tag, on: :create
|
||||
validates :name, presence: true, on: :create
|
||||
validates :account_id, uniqueness: { scope: [:tag_id, :list_id] }
|
||||
|
||||
def name=(str)
|
||||
self.tag = Tag.find_or_create_by_names(str.strip)&.first
|
||||
end
|
||||
end
|
107
app/models/keyword_subscribe.rb
Normal file
107
app/models/keyword_subscribe.rb
Normal file
|
@ -0,0 +1,107 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: keyword_subscribes
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# account_id :bigint(8)
|
||||
# keyword :string not null
|
||||
# ignorecase :boolean default(TRUE)
|
||||
# regexp :boolean default(FALSE)
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# name :string default(""), not null
|
||||
# ignore_block :boolean default(FALSE)
|
||||
# disabled :boolean default(FALSE)
|
||||
# exclude_keyword :string default(""), not null
|
||||
# list_id :bigint(8)
|
||||
#
|
||||
|
||||
class KeywordSubscribe < ApplicationRecord
|
||||
belongs_to :account, inverse_of: :keyword_subscribes, required: true
|
||||
belongs_to :list, optional: true
|
||||
|
||||
validates :keyword, presence: true
|
||||
validate :validate_subscribes_limit, on: :create
|
||||
validate :validate_keyword_regexp_syntax
|
||||
validate :validate_exclude_keyword_regexp_syntax
|
||||
validate :validate_uniqueness_in_account, on: :create
|
||||
|
||||
scope :active, -> { where(disabled: false) }
|
||||
scope :ignore_block, -> { where(ignore_block: true) }
|
||||
scope :home, -> { where(list_id: nil) }
|
||||
scope :list, -> { where.not(list_id: nil) }
|
||||
scope :without_local_followed_home, ->(account) { home.where.not(account: account.followers.local).where.not(account: account.subscribers.local) }
|
||||
scope :without_local_followed_list, ->(account) { list.where.not(list_id: ListAccount.followed_lists(account)).where.not(list_id: AccountSubscribe.subscribed_lists(account)) }
|
||||
|
||||
def keyword=(val)
|
||||
super(regexp ? val : keyword_normalization(val))
|
||||
end
|
||||
|
||||
def exclude_keyword=(val)
|
||||
super(regexp ? val : keyword_normalization(val))
|
||||
end
|
||||
|
||||
def match?(text)
|
||||
keyword_regexp.match?(text) && (exclude_keyword.empty? || !exclude_keyword_regexp.match?(text))
|
||||
end
|
||||
|
||||
def keyword_regexp
|
||||
to_regexp keyword
|
||||
end
|
||||
|
||||
def exclude_keyword_regexp
|
||||
to_regexp exclude_keyword
|
||||
end
|
||||
|
||||
class << self
|
||||
def match?(text, account_id: account_id = nil, as_ignore_block: as_ignore_block = false)
|
||||
target = KeywordSubscribe.active
|
||||
target = target.where(account_id: account_id) if account_id.present?
|
||||
target = target.ignore_block if as_ignore_block
|
||||
!target.find{ |t| t.match?(text) }.nil?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def keyword_normalization(val)
|
||||
val.to_s.strip.gsub(/\s{2,}/, ' ').split(/\s*,\s*/).reject(&:blank?).uniq.join(',')
|
||||
end
|
||||
|
||||
def to_regexp(words)
|
||||
Regexp.new(regexp ? words : "(?<![#])(#{words.split(',').map do |k|
|
||||
sb = k =~ /\A[A-Za-z0-9]/ ? '\b' : k !~ /\A[\/\.]/ ? '(?<![\/\.])' : ''
|
||||
eb = k =~ /[A-Za-z0-9]\z/ ? '\b' : k !~ /[\/\.]\z/ ? '(?![\/\.])' : ''
|
||||
|
||||
/(?m#{ignorecase ? 'i': ''}x:#{sb}#{Regexp.quote(k).gsub("\\ ", "[[:space:]]+")}#{eb})/
|
||||
end.join('|')})", ignorecase)
|
||||
end
|
||||
|
||||
def validate_keyword_regexp_syntax
|
||||
return unless regexp
|
||||
|
||||
begin
|
||||
Regexp.compile(keyword, ignorecase)
|
||||
rescue RegexpError => exception
|
||||
errors.add(:base, I18n.t('keyword_subscribes.errors.regexp', message: exception.message))
|
||||
end
|
||||
end
|
||||
|
||||
def validate_exclude_keyword_regexp_syntax
|
||||
return unless regexp
|
||||
|
||||
begin
|
||||
Regexp.compile(exclude_keyword, ignorecase)
|
||||
rescue RegexpError => exception
|
||||
errors.add(:base, I18n.t('keyword_subscribes.errors.regexp', message: exception.message))
|
||||
end
|
||||
end
|
||||
|
||||
def validate_subscribes_limit
|
||||
errors.add(:base, I18n.t('keyword_subscribes.errors.limit')) if account.keyword_subscribes.count >= 100
|
||||
end
|
||||
|
||||
def validate_uniqueness_in_account
|
||||
errors.add(:base, I18n.t('keyword_subscribes.errors.duplicate')) if account.keyword_subscribes.find_by(keyword: keyword, exclude_keyword: exclude_keyword, list_id: list_id)
|
||||
end
|
||||
end
|
|
@ -18,6 +18,8 @@ class ListAccount < ApplicationRecord
|
|||
|
||||
before_validation :set_follow
|
||||
|
||||
scope :followed_lists, ->(account) { ListAccount.includes(:follow).where(follows: { account_id: account.id }).pluck(:list_id).uniq }
|
||||
|
||||
private
|
||||
|
||||
def set_follow
|
||||
|
|
|
@ -244,6 +244,10 @@ class Status < ApplicationRecord
|
|||
@emojis = CustomEmoji.from_text(fields.join(' '), account.domain) + (quote? ? CustomEmoji.from_text([quote.spoiler_text, quote.text].join(' '), quote.account.domain) : [])
|
||||
end
|
||||
|
||||
def index_text
|
||||
@index_text ||= [spoiler_text, Formatter.instance.plaintext(self)].concat(media_attachments.map(&:description)).concat(preloadable_poll ? preloadable_poll.options : []).join("\n\n")
|
||||
end
|
||||
|
||||
def replies_count
|
||||
status_stat&.replies_count || 0
|
||||
end
|
||||
|
|
|
@ -23,6 +23,7 @@ class Tag < ApplicationRecord
|
|||
|
||||
has_many :favourite_tags, dependent: :destroy, inverse_of: :tag
|
||||
has_many :featured_tags, dependent: :destroy, inverse_of: :tag
|
||||
has_many :follow_tags, dependent: :destroy, inverse_of: :tag
|
||||
|
||||
HASHTAG_SEPARATORS = "_\u00B7\u200c"
|
||||
HASHTAG_NAME_RE = "([[:word:]_][[:word:]#{HASHTAG_SEPARATORS}]*[[:alpha:]#{HASHTAG_SEPARATORS}][[:word:]#{HASHTAG_SEPARATORS}]*[[:word:]_])|([[:word:]_]*[[:alpha:]][[:word:]_]*)"
|
||||
|
|
9
app/serializers/rest/account_subscribe_serializer.rb
Normal file
9
app/serializers/rest/account_subscribe_serializer.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class REST::AccountSubscribeSerializer < ActiveModel::Serializer
|
||||
attributes :id, :target_account, :updated_at
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
end
|
||||
end
|
9
app/serializers/rest/domain_subscribe_serializer.rb
Normal file
9
app/serializers/rest/domain_subscribe_serializer.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class REST::DomainSubscribeSerializer < ActiveModel::Serializer
|
||||
attributes :id, :list_id, :domain, :updated_at
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
end
|
||||
end
|
9
app/serializers/rest/follow_tag_serializer.rb
Normal file
9
app/serializers/rest/follow_tag_serializer.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class REST::FollowTagSerializer < ActiveModel::Serializer
|
||||
attributes :id, :name, :updated_at
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
end
|
||||
end
|
9
app/serializers/rest/keyword_subscribes_serializer.rb
Normal file
9
app/serializers/rest/keyword_subscribes_serializer.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class REST::KeywordSubscribesSerializer < ActiveModel::Serializer
|
||||
attributes :id, :name, :keyword, :exclude_keyword, :ignorecase, :regexp, :ignore_block, :disabled, :exclude_home
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
end
|
||||
end
|
29
app/services/account_subscribe_service.rb
Normal file
29
app/services/account_subscribe_service.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AccountSubscribeService < BaseService
|
||||
# Subscribe a remote user
|
||||
# @param [Account] source_account From which to subscribe
|
||||
# @param [String, Account] uri User URI to subscribe in the form of username@domain (or account record)
|
||||
def call(source_account, target_account)
|
||||
begin
|
||||
target_account = ResolveAccountService.new.call(target_account, skip_webfinger: false)
|
||||
rescue
|
||||
target_account = nil
|
||||
end
|
||||
|
||||
raise ActiveRecord::RecordNotFound if target_account.nil? || target_account.id == source_account.id || target_account.suspended?
|
||||
raise Mastodon::NotPermittedError if target_account.blocking?(source_account) || source_account.blocking?(target_account) || (!target_account.local? && target_account.ostatus?) || source_account.domain_blocking?(target_account.domain)
|
||||
|
||||
if source_account.following?(target_account)
|
||||
return
|
||||
elsif source_account.subscribing?(target_account)
|
||||
return
|
||||
end
|
||||
|
||||
ActivityTracker.increment('activity:interactions')
|
||||
|
||||
subscribe = source_account.subscribe!(target_account)
|
||||
MergeWorker.perform_async(target_account.id, source_account.id, true)
|
||||
subscribe
|
||||
end
|
||||
end
|
|
@ -18,11 +18,18 @@ class FanOutOnWriteService < BaseService
|
|||
deliver_to_lists(status)
|
||||
end
|
||||
|
||||
return if status.account.silenced? || !status.public_visibility? || status.reblog?
|
||||
return if status.account.silenced? || !status.public_visibility?
|
||||
|
||||
deliver_to_domain_subscribers(status)
|
||||
|
||||
return if status.reblog?
|
||||
|
||||
render_anonymous_payload(status)
|
||||
|
||||
deliver_to_hashtags(status)
|
||||
deliver_to_hashtag_followers(status)
|
||||
deliver_to_subscribers(status)
|
||||
deliver_to_keyword_subscribers(status)
|
||||
|
||||
return if status.reply? && status.in_reply_to_account_id != status.account_id
|
||||
|
||||
|
@ -47,6 +54,72 @@ class FanOutOnWriteService < BaseService
|
|||
end
|
||||
end
|
||||
|
||||
def deliver_to_subscribers(status)
|
||||
Rails.logger.debug "Delivering status #{status.id} to subscribers"
|
||||
|
||||
status.account.subscribers_for_local_distribution.select(:id).reorder(nil).find_in_batches do |subscribings|
|
||||
FeedInsertWorker.push_bulk(subscribings) do |subscribing|
|
||||
[status.id, subscribing.id, :home]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def deliver_to_domain_subscribers(status)
|
||||
Rails.logger.debug "Delivering status #{status.id} to domain subscribers"
|
||||
|
||||
deliver_to_domain_subscribers_home(status)
|
||||
deliver_to_domain_subscribers_list(status)
|
||||
end
|
||||
|
||||
def deliver_to_domain_subscribers_home(status)
|
||||
DomainSubscribe.domain_to_home(status.account.domain).with_reblog(status.reblog?).select(:id, :account_id).find_in_batches do |subscribes|
|
||||
FeedInsertWorker.push_bulk(subscribes) do |subscribe|
|
||||
[status.id, subscribe.account_id, :home]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def deliver_to_domain_subscribers_list(status)
|
||||
DomainSubscribe.domain_to_list(status.account.domain).with_reblog(status.reblog?).select(:id, :list_id).find_in_batches do |subscribes|
|
||||
FeedInsertWorker.push_bulk(subscribes) do |subscribe|
|
||||
[status.id, subscribe.list_id, :list]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def deliver_to_keyword_subscribers(status)
|
||||
Rails.logger.debug "Delivering status #{status.id} to keyword subscribers"
|
||||
|
||||
deliver_to_keyword_subscribers_home(status)
|
||||
deliver_to_keyword_subscribers_list(status)
|
||||
end
|
||||
|
||||
def deliver_to_keyword_subscribers_home(status)
|
||||
match_accounts = []
|
||||
|
||||
KeywordSubscribe.active.without_local_followed_home(status.account).order(:account_id).each do |keyword_subscribe|
|
||||
next if match_accounts[-1] == keyword_subscribe.account_id
|
||||
match_accounts << keyword_subscribe.account_id if keyword_subscribe.match?(status.index_text)
|
||||
end
|
||||
|
||||
FeedInsertWorker.push_bulk(match_accounts) do |match_account|
|
||||
[status.id, match_account, :home]
|
||||
end
|
||||
end
|
||||
|
||||
def deliver_to_keyword_subscribers_list(status)
|
||||
match_lists = []
|
||||
|
||||
KeywordSubscribe.active.without_local_followed_list(status.account).order(:list_id).each do |keyword_subscribe|
|
||||
next if match_lists[-1] == keyword_subscribe.list_id
|
||||
match_lists << keyword_subscribe.list_id if keyword_subscribe.match?(status.index_text)
|
||||
end
|
||||
|
||||
FeedInsertWorker.push_bulk(match_lists) do |match_list|
|
||||
[status.id, match_list, :list]
|
||||
end
|
||||
end
|
||||
|
||||
def deliver_to_lists(status)
|
||||
Rails.logger.debug "Delivering status #{status.id} to lists"
|
||||
|
||||
|
@ -81,6 +154,14 @@ class FanOutOnWriteService < BaseService
|
|||
end
|
||||
end
|
||||
|
||||
def deliver_to_hashtag_followers(status)
|
||||
Rails.logger.debug "Delivering status #{status.id} to hashtag followers"
|
||||
|
||||
FeedInsertWorker.push_bulk(FollowTag.where(tag: status.tags).pluck(:account_id).uniq) do |follower|
|
||||
[status.id, follower, :home]
|
||||
end
|
||||
end
|
||||
|
||||
def deliver_to_public(status)
|
||||
Rails.logger.debug "Delivering status #{status.id} to public timeline"
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@ class FollowService < BaseService
|
|||
def direct_follow!
|
||||
follow = @source_account.follow!(@target_account, reblogs: @options[:reblogs], notify: @options[:notify], rate_limit: @options[:with_rate_limit], bypass_limit: @options[:bypass_limit])
|
||||
|
||||
UnsubscribeAccountService.new.call(@source_account, @target_account) if @source_account.subscribing?(@target_account)
|
||||
LocalNotificationWorker.perform_async(@target_account.id, follow.id, follow.class.name, :follow)
|
||||
MergeWorker.perform_async(@target_account.id, @source_account.id)
|
||||
|
||||
|
|
16
app/services/unsubscribe_account_service.rb
Normal file
16
app/services/unsubscribe_account_service.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UnsubscribeAccountService < BaseService
|
||||
# UnsubscribeAccount
|
||||
# @param [Account] source_account Where to unsubscribe from
|
||||
# @param [Account] target_account Which to unsubscribe
|
||||
def call(source_account, target_account)
|
||||
subscribe = AccountSubscribe.find_by(account: source_account, target_account: target_account)
|
||||
|
||||
return unless subscribe
|
||||
|
||||
subscribe.destroy!
|
||||
UnmergeWorker.perform_async(target_account.id, source_account.id)
|
||||
subscribe
|
||||
end
|
||||
end
|
25
app/views/settings/account_subscribes/index.html.haml
Normal file
25
app/views/settings/account_subscribes/index.html.haml
Normal file
|
@ -0,0 +1,25 @@
|
|||
- content_for :page_title do
|
||||
= t('settings.account_subscribes')
|
||||
|
||||
%p= t('account_subscribes.hint_html')
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
= simple_form_for :account_input, url: settings_account_subscribes_path do |f|
|
||||
|
||||
.fields-group
|
||||
= f.input :acct, wrapper: :with_block_label, hint: false
|
||||
|
||||
.actions
|
||||
= f.button :button, t('account_subscribes.add_new'), type: :submit
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
- @account_subscribings.each do |account_subscribing|
|
||||
.directory__tag
|
||||
%div
|
||||
%h4
|
||||
= fa_icon 'user'
|
||||
= account_subscribing[:acct]
|
||||
%small
|
||||
= table_link_to 'trash', t('filters.index.delete'), settings_account_subscribe_path(account_subscribing), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }
|
11
app/views/settings/domain_subscribes/_fields.html.haml
Normal file
11
app/views/settings/domain_subscribes/_fields.html.haml
Normal file
|
@ -0,0 +1,11 @@
|
|||
.fields-group
|
||||
= f.input :domain, wrapper: :with_label
|
||||
|
||||
.fields-group
|
||||
= f.input :exclude_reblog, wrapper: :with_label
|
||||
|
||||
.fields-group
|
||||
= f.label :list_id
|
||||
= f.collection_select :list_id, home_list_new(@lists), :first, :last
|
||||
|
||||
%hr.spacer/
|
9
app/views/settings/domain_subscribes/edit.html.haml
Normal file
9
app/views/settings/domain_subscribes/edit.html.haml
Normal file
|
@ -0,0 +1,9 @@
|
|||
- content_for :page_title do
|
||||
= t('domain_subscribes.edit.title')
|
||||
|
||||
= simple_form_for @domain_subscribe, url: settings_domain_subscribe_path(@domain_subscribe), method: :put do |f|
|
||||
= render 'shared/error_messages', object: @domain_subscribe
|
||||
= render 'fields', f: f
|
||||
|
||||
.actions
|
||||
= f.button :button, t('generic.save_changes'), type: :submit
|
35
app/views/settings/domain_subscribes/index.html.haml
Normal file
35
app/views/settings/domain_subscribes/index.html.haml
Normal file
|
@ -0,0 +1,35 @@
|
|||
- content_for :page_title do
|
||||
= t('settings.domain_subscribes')
|
||||
|
||||
%p= t('domain_subscribes.hint_html')
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
.table-wrapper
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%th= t('simple_form.labels.domain_subscribe.domain')
|
||||
%th.nowrap= t('simple_form.labels.domain_subscribe.reblog')
|
||||
%th.nowrap= t('simple_form.labels.domain_subscribe.timeline')
|
||||
%th.nowrap
|
||||
%tbody
|
||||
- @domain_subscribes.each do |domain_subscribe|
|
||||
%tr
|
||||
%td
|
||||
= domain_subscribe.domain
|
||||
%td.nowrap
|
||||
- if domain_subscribe.exclude_reblog
|
||||
= fa_icon('times')
|
||||
%td.nowrap
|
||||
- if domain_subscribe.list_id
|
||||
= fa_icon 'list-ul'
|
||||
= domain_subscribe.list&.title
|
||||
- else
|
||||
= fa_icon 'home'
|
||||
= t 'domain_subscribes.home'
|
||||
%td.nowrap
|
||||
= table_link_to 'pencil', t('domain_subscribes.edit.title'), edit_settings_domain_subscribe_path(domain_subscribe)
|
||||
= table_link_to 'trash', t('filters.index.delete'), settings_domain_subscribe_path(domain_subscribe), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }
|
||||
|
||||
= link_to t('domain_subscribes.new.title'), new_settings_domain_subscribe_path, class: 'button'
|
9
app/views/settings/domain_subscribes/new.html.haml
Normal file
9
app/views/settings/domain_subscribes/new.html.haml
Normal file
|
@ -0,0 +1,9 @@
|
|||
- content_for :page_title do
|
||||
= t('domain_subscribes.new.title')
|
||||
|
||||
= simple_form_for @domain_subscribe, url: settings_domain_subscribes_path do |f|
|
||||
= render 'shared/error_messages', object: @domain_subscribe
|
||||
= render 'fields', f: f
|
||||
|
||||
.actions
|
||||
= f.button :button, t('domain_subscribes.new.title'), type: :submit
|
26
app/views/settings/follow_tags/index.html.haml
Normal file
26
app/views/settings/follow_tags/index.html.haml
Normal file
|
@ -0,0 +1,26 @@
|
|||
- content_for :page_title do
|
||||
= t('settings.follow_tags')
|
||||
|
||||
%p= t('follow_tags.hint_html')
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
= simple_form_for @follow_tag, url: settings_follow_tags_path do |f|
|
||||
= render 'shared/error_messages', object: @follow_tag
|
||||
|
||||
.fields-group
|
||||
= f.input :name, wrapper: :with_block_label, hint: false
|
||||
|
||||
.actions
|
||||
= f.button :button, t('follow_tags.add_new'), type: :submit
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
- @follow_tags.each do |follow_tag|
|
||||
.directory__tag{ class: params[:tag] == follow_tag.name ? 'active' : nil }
|
||||
%div
|
||||
%h4
|
||||
= fa_icon 'hashtag'
|
||||
= follow_tag.name
|
||||
%small
|
||||
= table_link_to 'trash', t('filters.index.delete'), settings_follow_tag_path(follow_tag), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }
|
24
app/views/settings/keyword_subscribes/_fields.html.haml
Normal file
24
app/views/settings/keyword_subscribes/_fields.html.haml
Normal file
|
@ -0,0 +1,24 @@
|
|||
.fields-group
|
||||
= f.input :name, as: :string, wrapper: :with_label
|
||||
|
||||
.fields-group
|
||||
= f.input :keyword, as: :string, wrapper: :with_label
|
||||
|
||||
.fields-group
|
||||
= f.input :exclude_keyword, as: :string, wrapper: :with_label
|
||||
|
||||
.fields-group
|
||||
= f.input :ignorecase, wrapper: :with_label
|
||||
|
||||
.fields-group
|
||||
= f.input :regexp, wrapper: :with_label
|
||||
|
||||
.fields-group
|
||||
= f.input :ignore_block, wrapper: :with_label
|
||||
|
||||
.fields-group
|
||||
= f.label :list_id
|
||||
= f.collection_select :list_id, home_list_new(@lists), :first, :last
|
||||
|
||||
.fields-group
|
||||
= f.input :disabled, wrapper: :with_label
|
9
app/views/settings/keyword_subscribes/edit.html.haml
Normal file
9
app/views/settings/keyword_subscribes/edit.html.haml
Normal file
|
@ -0,0 +1,9 @@
|
|||
- content_for :page_title do
|
||||
= t('keyword_subscribes.edit.title')
|
||||
|
||||
= simple_form_for @keyword_subscribe, url: settings_keyword_subscribe_path(@keyword_subscribe), method: :put do |f|
|
||||
= render 'shared/error_messages', object: @keyword_subscribe
|
||||
= render 'fields', f: f
|
||||
|
||||
.actions
|
||||
= f.button :button, t('generic.save_changes'), type: :submit
|
66
app/views/settings/keyword_subscribes/index.html.haml
Normal file
66
app/views/settings/keyword_subscribes/index.html.haml
Normal file
|
@ -0,0 +1,66 @@
|
|||
- content_for :page_title do
|
||||
= t('keyword_subscribes.index.title')
|
||||
|
||||
%p= t('keyword_subscribes.hint_html')
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
= render 'shared/error_messages', object: @keyword_subscribe
|
||||
|
||||
.table-wrapper
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%th.nowrap= t('simple_form.labels.keyword_subscribes.name')
|
||||
%th.nowrap= t('simple_form.labels.keyword_subscribes.regexp')
|
||||
%th= t('simple_form.labels.keyword_subscribes.keyword')
|
||||
%th.nowrap= t('simple_form.labels.keyword_subscribes.ignorecase')
|
||||
%th.nowrap= t('simple_form.labels.keyword_subscribes.ignore_block')
|
||||
%th.nowrap= t('simple_form.labels.keyword_subscribes.timeline')
|
||||
%th.nowrap= t('simple_form.labels.keyword_subscribes.disabled')
|
||||
%th.nowrap
|
||||
%tbody
|
||||
- @keyword_subscribes.each do |keyword_subscribe|
|
||||
%tr
|
||||
%td.nowrap= keyword_subscribe.name
|
||||
%td.nowrap
|
||||
- if keyword_subscribe.regexp
|
||||
= t 'keyword_subscribes.regexp.enabled'
|
||||
- else
|
||||
= t 'keyword_subscribes.regexp.disabled'
|
||||
%td
|
||||
.include-keyword
|
||||
= keyword_subscribe.keyword
|
||||
.exclude-keyword
|
||||
= keyword_subscribe.exclude_keyword
|
||||
%td.nowrap
|
||||
- if keyword_subscribe.ignorecase
|
||||
= t 'keyword_subscribes.ignorecase.enabled'
|
||||
- else
|
||||
= t 'keyword_subscribes.ignorecase.disabled'
|
||||
%td.nowrap
|
||||
- if keyword_subscribe.ignore_block
|
||||
= t 'keyword_subscribes.ignore_block'
|
||||
%td.nowrap
|
||||
- if keyword_subscribe.list_id
|
||||
= fa_icon 'list-ul'
|
||||
= keyword_subscribe.list&.title
|
||||
- else
|
||||
= fa_icon 'home'
|
||||
= t 'keyword_subscribe.home'
|
||||
%td.nowrap
|
||||
- if !keyword_subscribe.disabled
|
||||
%span.positive-hint
|
||||
= fa_icon('check')
|
||||
= ' '
|
||||
= t 'keyword_subscribes.enabled'
|
||||
- else
|
||||
%span.negative-hint
|
||||
= fa_icon('times')
|
||||
= ' '
|
||||
= t 'keyword_subscribes.disabled'
|
||||
%td.nowrap
|
||||
= table_link_to 'pencil', t('keyword_subscribes.edit.title'), edit_settings_keyword_subscribe_path(keyword_subscribe)
|
||||
= table_link_to 'times', t('keyword_subscribes.index.delete'), settings_keyword_subscribe_path(keyword_subscribe), method: :delete
|
||||
|
||||
= link_to t('keyword_subscribes.new.title'), new_settings_keyword_subscribe_path, class: 'button'
|
9
app/views/settings/keyword_subscribes/new.html.haml
Normal file
9
app/views/settings/keyword_subscribes/new.html.haml
Normal file
|
@ -0,0 +1,9 @@
|
|||
- content_for :page_title do
|
||||
= t('keyword_subscribes.new.title')
|
||||
|
||||
= simple_form_for @keyword_subscribe, url: settings_keyword_subscribes_path do |f|
|
||||
= render 'shared/error_messages', object: @keyword_subscribe
|
||||
= render 'fields', f: f
|
||||
|
||||
.actions
|
||||
= f.button :button, t('keyword_subscribes.new.title'), type: :submit
|
|
@ -3,8 +3,8 @@
|
|||
class MergeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(from_account_id, into_account_id)
|
||||
FeedManager.instance.merge_into_home(Account.find(from_account_id), Account.find(into_account_id))
|
||||
def perform(from_account_id, into_account_id, public_only = false)
|
||||
FeedManager.instance.merge_into_home(Account.find(from_account_id), Account.find(into_account_id), public_only)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
ensure
|
||||
|
|
|
@ -87,6 +87,9 @@ en:
|
|||
moderator: Mod
|
||||
unavailable: Profile unavailable
|
||||
unfollow: Unfollow
|
||||
account_subscribes:
|
||||
add_new: Add
|
||||
hint_html: "<strong>What are account subscription?</strong> Insert public posts from the specified account into the home timeline. Posts received by the server (federated timeline) are targets. You cannot subscribe if you are following."
|
||||
admin:
|
||||
account_actions:
|
||||
action: Perform action
|
||||
|
@ -876,6 +879,33 @@ en:
|
|||
directory: Profile directory
|
||||
explanation: Discover users based on their interests
|
||||
explore_mastodon: Explore %{title}
|
||||
domain_blocks:
|
||||
blocked_domains: List of limited and blocked domains
|
||||
description: This is the list of servers that %{instance} limits or reject federation with.
|
||||
domain: Domain
|
||||
media_block: Media block
|
||||
no_domain_blocks: "(No domain blocks)"
|
||||
severity: Severity
|
||||
severity_legend:
|
||||
media_block: Media files coming from the server are neither fetched, stored, or displayed to the user.
|
||||
silence: Accounts from silenced servers can be found, followed and interacted with, but their toots will not appear in the public timelines, and notifications from them will not reach local users who are not following them.
|
||||
suspension: No content from suspended servers is stored or displayed, nor is any content sent to them. Interactions from suspended servers are ignored.
|
||||
suspension_disclaimer: Suspended servers may occasionally retrieve public content from this server.
|
||||
title: Severities
|
||||
show_rationale: Show rationale
|
||||
silence: Silence
|
||||
suspension: Suspension
|
||||
title: "%{instance} List of blocked instances"
|
||||
domain_subscribes:
|
||||
add_new: Add
|
||||
edit:
|
||||
title: Edit
|
||||
exclude_reblog: Exclude
|
||||
hint_html: "<strong>What are domain subscription?</strong> Insert public posts from the specified domain into the home or list timeline. Posts received by the server (federated timeline) are targets."
|
||||
home: Home
|
||||
include_reblog: Include
|
||||
new:
|
||||
title: Add new domain subscription
|
||||
domain_validator:
|
||||
invalid_domain: is not a valid domain name
|
||||
errors:
|
||||
|
@ -939,6 +969,9 @@ en:
|
|||
title: Filters
|
||||
new:
|
||||
title: Add new filter
|
||||
follow_tags:
|
||||
add_new: Add new
|
||||
hint_html: "<strong>What are follow hashtags?</strong> They are a collection of hashtags you follow. From the posts with hashtags received by the server, the one with the hashtag specified here is inserted into the home timeline."
|
||||
footer:
|
||||
developers: Developers
|
||||
more: More…
|
||||
|
@ -1017,7 +1050,32 @@ en:
|
|||
expires_at: Expires
|
||||
uses: Uses
|
||||
title: Invite people
|
||||
keyword_subscribes:
|
||||
add_new: Add
|
||||
disabled: Disabled
|
||||
edit:
|
||||
title: Edit
|
||||
enabled: Enabled
|
||||
errors:
|
||||
duplicate: The same content has already been registered
|
||||
limit: You have reached the maximum number of "Keyword subscribes" that can be registered
|
||||
regexp: "Regular expression error: %{message}"
|
||||
hint_html: "<strong>What is a keyword subscribes?</strong> Inserts a public post that matches one of the specified words or a regular expression into the home timeline. Posts received by the server (federated timeline) are targets."
|
||||
home: Home
|
||||
ignorecase:
|
||||
enabled: Ignore
|
||||
disabled: Sensitive
|
||||
ignore_block: Ignore
|
||||
index:
|
||||
delete: Delete
|
||||
title: Keyword subscribe
|
||||
new:
|
||||
title: Add new keyword subscribe
|
||||
regexp:
|
||||
enabled: Regex
|
||||
disabled: Keyword
|
||||
lists:
|
||||
add_new: Add new list
|
||||
errors:
|
||||
limit: You have reached the maximum amount of lists
|
||||
login_activities:
|
||||
|
@ -1242,19 +1300,24 @@ en:
|
|||
settings:
|
||||
account: Account
|
||||
account_settings: Account settings
|
||||
account_subscribes: Account subscribes
|
||||
aliases: Account aliases
|
||||
appearance: Appearance
|
||||
authorized_apps: Authorized apps
|
||||
back: Back to Mastodon
|
||||
delete: Account deletion
|
||||
development: Development
|
||||
domain_subscribes: Domain subscribes
|
||||
edit_profile: Edit profile
|
||||
export: Data export
|
||||
favourite_tags: Favourite hashtags
|
||||
featured_tags: Featured hashtags
|
||||
follow_and_subscriptions: Follows and subscriptions
|
||||
follow_tags: Following hashtags
|
||||
identity_proofs: Identity proofs
|
||||
import: Import
|
||||
import_and_export: Import and export
|
||||
keyword_subscribes: Keyword subscribes
|
||||
migrate: Account migration
|
||||
notifications: Notifications
|
||||
preferences: Preferences
|
||||
|
|
|
@ -81,6 +81,9 @@ ja:
|
|||
moderator: Mod
|
||||
unavailable: プロフィールは利用できません
|
||||
unfollow: フォロー解除
|
||||
account_subscribes:
|
||||
add_new: 追加
|
||||
hint_html: "<strong>アカウントの購読とは何ですか?</strong> 指定したアカウントの公開投稿をホームタイムラインに挿入します。サーバが受け取っている投稿(連合タイムライン)が対象です。フォローしている場合は購読できません。"
|
||||
admin:
|
||||
account_actions:
|
||||
action: アクションを実行
|
||||
|
@ -854,6 +857,16 @@ ja:
|
|||
directory: ディレクトリ
|
||||
explanation: 関心を軸にユーザーを発見しよう
|
||||
explore_mastodon: "%{title}を探索"
|
||||
domain_subscribes:
|
||||
add_new: 追加
|
||||
edit:
|
||||
title: 編集
|
||||
exclude_reblog: 含めない
|
||||
hint_html: "<strong>ドメインの購読とは何ですか?</strong> 指定したドメインの公開投稿をホームタイムラインまたはリストに挿入します。サーバが受け取っている投稿(連合タイムライン)が対象です。"
|
||||
home: ホーム
|
||||
include_reblog: 含める
|
||||
new:
|
||||
title: 新規ドメイン購読を追加
|
||||
domain_validator:
|
||||
invalid_domain: は無効なドメイン名です
|
||||
errors:
|
||||
|
@ -899,6 +912,9 @@ ja:
|
|||
errors:
|
||||
limit: 注目のハッシュタグの上限に達しました
|
||||
hint_html: "<strong>注目のハッシュタグとは?</strong>プロフィールページに目立つ形で表示され、そのハッシュタグのついたあなたの公開投稿だけを抽出して閲覧できるようにします。クリエイティブな仕事や長期的なプロジェクトを追うのに優れた機能です。"
|
||||
follow_tags:
|
||||
add_new: 追加
|
||||
hint_html: "<strong>ハッシュタグのフォローとは何ですか?</strong> それらはあなたがフォローするハッシュタグのコレクションです。サーバが受け取ったハッシュタグ付きの投稿の中から、ここで指定したハッシュタグのついた投稿をホームタイムラインに挿入します。"
|
||||
filters:
|
||||
contexts:
|
||||
account: プロフィール
|
||||
|
@ -993,7 +1009,32 @@ ja:
|
|||
expires_at: 有効期限
|
||||
uses: 使用
|
||||
title: 新規ユーザーの招待
|
||||
keyword_subscribes:
|
||||
add_new: 追加
|
||||
disabled: 無効
|
||||
edit:
|
||||
title: 編集
|
||||
enabled: 有効
|
||||
errors:
|
||||
duplicate: 既に同じ内容が登録されています
|
||||
limit: キーワード購読の登録可能数の上限に達しました
|
||||
regexp: "正規表現に誤りがあります: %{message}"
|
||||
hint_html: "<strong>キーワードの購読とは何ですか?</strong> 指定した単語のいずれか、または正規表現に一致する公開投稿をホームタイムラインに挿入します。サーバが受け取っている投稿(連合タイムライン)が対象です。"
|
||||
home: ホーム
|
||||
ignorecase:
|
||||
enabled: 無視
|
||||
disabled: 区別
|
||||
ignore_block: 無視
|
||||
index:
|
||||
delete: 削除
|
||||
title: キーワードの購読
|
||||
new:
|
||||
title: 新規キーワード購読を追加
|
||||
regexp:
|
||||
enabled: 正規表現
|
||||
disabled: キーワード
|
||||
lists:
|
||||
add_new: 新しいリストを追加
|
||||
errors:
|
||||
limit: リストの上限に達しました
|
||||
media_attachments:
|
||||
|
@ -1203,19 +1244,24 @@ ja:
|
|||
settings:
|
||||
account: アカウント
|
||||
account_settings: アカウント設定
|
||||
account_subscribes: アカウントの購読
|
||||
aliases: アカウントエイリアス
|
||||
appearance: 外観
|
||||
authorized_apps: 認証済みアプリ
|
||||
back: Mastodon に戻る
|
||||
delete: アカウントの削除
|
||||
development: 開発
|
||||
domain_subscribes: ドメインの購読
|
||||
edit_profile: プロフィールを編集
|
||||
export: データのエクスポート
|
||||
favourite_tags: お気に入りハッシュタグ
|
||||
featured_tags: 注目のハッシュタグ
|
||||
follow_and_subscriptions: フォロー・購読
|
||||
follow_tags: ハッシュタグのフォロー
|
||||
identity_proofs: Identity proofs
|
||||
import: データのインポート
|
||||
import_and_export: インポート・エクスポート
|
||||
keyword_subscribes: キーワードの購読
|
||||
migrate: アカウントの引っ越し
|
||||
notifications: 通知
|
||||
preferences: ユーザー設定
|
||||
|
|
|
@ -60,6 +60,9 @@ en:
|
|||
whole_word: When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word
|
||||
domain_allow:
|
||||
domain: This domain will be able to fetch data from this server and incoming data from it will be processed and stored
|
||||
domain_subscribe:
|
||||
domain: Specify the domain name of the server you want to subscribe to
|
||||
exclude_reblog: Exclude boosted posts from subscription
|
||||
email_domain_block:
|
||||
domain: This can be the domain name that shows up in the e-mail address, the MX record that domain resolves to, or IP of the server that MX record resolves to. Those will be checked upon user sign-up and the sign-up will be rejected.
|
||||
with_dns_records: An attempt to resolve the given domain's DNS records will be made and the results will also be blocked
|
||||
|
@ -79,6 +82,11 @@ en:
|
|||
no_access: Block access to all resources
|
||||
sign_up_requires_approval: New sign-ups will require your approval
|
||||
severity: Choose what will happen with requests from this IP
|
||||
keyword_subscribe:
|
||||
exclude_keyword: List multiple excluded keywords separated by commas (or use regular expressions)
|
||||
ignore_block: You can prioritize keyword subscriptions while keeping the entire domain block
|
||||
keyword: List multiple keywords separated by commas (or use regular expressions)
|
||||
name: Optional
|
||||
rule:
|
||||
text: Describe a rule or requirement for users on this server. Try to keep it short and simple
|
||||
sessions:
|
||||
|
@ -95,6 +103,8 @@ en:
|
|||
value: Content
|
||||
account_alias:
|
||||
acct: Handle of the old account
|
||||
account_input:
|
||||
acct: Account
|
||||
account_migration:
|
||||
acct: Handle of the new account
|
||||
account_warning_preset:
|
||||
|
@ -176,6 +186,12 @@ en:
|
|||
username: Username
|
||||
username_or_email: Username or Email
|
||||
whole_word: Whole word
|
||||
domain_subscribe:
|
||||
domain: Domain
|
||||
exclude_reblog: Exclude boost
|
||||
list_id: Target timeline
|
||||
timeline: Timeline
|
||||
reblog: Boost
|
||||
email_domain_block:
|
||||
with_dns_records: Include MX records and IPs of the domain
|
||||
featured_tag:
|
||||
|
@ -195,6 +211,23 @@ en:
|
|||
no_access: Block access
|
||||
sign_up_requires_approval: Limit sign-ups
|
||||
severity: Rule
|
||||
keyword_subscribe:
|
||||
disabled: Temporarily disable subscription
|
||||
exclude_keyword: Excluded keyword list or regular expression
|
||||
ignorecase: Ignore case
|
||||
ignore_block: Ignore User's domain blocking
|
||||
keyword: Keyword list or regular expression
|
||||
list_id: Target timeline
|
||||
name: Name
|
||||
regexp: Use regular expressions for keywords
|
||||
keyword_subscribes:
|
||||
disabled: State
|
||||
ignorecase: Case
|
||||
ignore_block: Block
|
||||
keyword: String
|
||||
name: Name
|
||||
regexp: Type
|
||||
timeline: Timeline
|
||||
notification_emails:
|
||||
digest: Send digest e-mails
|
||||
favourite: Someone favourited your post
|
||||
|
|
|
@ -60,6 +60,9 @@ ja:
|
|||
whole_word: キーワードまたはフレーズが英数字のみの場合、単語全体と一致する場合のみ適用されるようになります
|
||||
domain_allow:
|
||||
domain: 登録するとこのサーバーからデータを受信したり、このドメインから受信するデータを処理して保存できるようになります
|
||||
domain_subscribe:
|
||||
domain: 購読したいサーバのドメイン名を指定します
|
||||
exclude_reblog: ブーストされた投稿を購読から除外します
|
||||
email_domain_block:
|
||||
domain: メールアドレスのドメイン名および、名前解決したMXレコード、IPアドレスを指定できます。ユーザー登録時にこれらをチェックし、該当する場合はユーザー登録を拒否します。
|
||||
with_dns_records: 指定したドメインのDNSレコードを取得し、その結果もメールドメインブロックに登録されます
|
||||
|
@ -79,6 +82,11 @@ ja:
|
|||
no_access: すべてのリソースへのアクセスをブロックします
|
||||
sign_up_requires_approval: 承認するまで新規登録が完了しなくなります
|
||||
severity: このIPに対する措置を選択してください
|
||||
keyword_subscribe:
|
||||
exclude_keyword: カンマで区切って複数の除外するキーワードを並べます(または正規表現で指定します)
|
||||
ignore_block: ドメイン全体を非表示にしたまま、キーワードの購読を優先することができます
|
||||
keyword: カンマで区切って複数のキーワードを並べます(または正規表現で指定します)
|
||||
name: オプションです
|
||||
rule:
|
||||
text: ユーザーのためのルールや要件を記述してください。短くシンプルにしてください。
|
||||
sessions:
|
||||
|
@ -95,6 +103,8 @@ ja:
|
|||
value: 内容
|
||||
account_alias:
|
||||
acct: 引っ越し元のユーザー ID
|
||||
account_input:
|
||||
acct: アカウント (account@domain.tld)
|
||||
account_migration:
|
||||
acct: 引っ越し先のユーザー ID
|
||||
account_warning_preset:
|
||||
|
@ -176,12 +186,20 @@ ja:
|
|||
username: ユーザー名
|
||||
username_or_email: ユーザー名またはメールアドレス
|
||||
whole_word: 単語全体にマッチ
|
||||
domain_subscribe:
|
||||
domain: ドメイン
|
||||
exclude_reblog: ブースト除外
|
||||
list_id: 対象タイムライン
|
||||
reblog: ブースト
|
||||
timeline: タイムライン
|
||||
email_domain_block:
|
||||
with_dns_records: ドメインのMXレコードとIPアドレスを含む
|
||||
favourite_tag:
|
||||
name: ハッシュタグ
|
||||
featured_tag:
|
||||
name: ハッシュタグ
|
||||
follow_tag:
|
||||
name: ハッシュタグ
|
||||
interactions:
|
||||
must_be_follower: フォロワー以外からの通知をブロック
|
||||
must_be_following: フォローしていないユーザーからの通知をブロック
|
||||
|
@ -197,6 +215,23 @@ ja:
|
|||
no_access: ブロック
|
||||
sign_up_requires_approval: 登録を制限
|
||||
severity: ルール
|
||||
keyword_subscribe:
|
||||
disabled: 一時的に購読を無効にする
|
||||
exclude_keyword: 除外するキーワードまたは正規表現
|
||||
ignorecase: 大文字と小文字を区別しない
|
||||
ignore_block: ユーザーによるドメインブロックを無視する
|
||||
keyword: キーワードまたは正規表現
|
||||
list_id: 対象タイムライン
|
||||
name: 名称
|
||||
regexp: キーワードに正規表現を使う
|
||||
keyword_subscribes:
|
||||
disabled: 状態
|
||||
ignorecase: 大小
|
||||
ignore_block: ブロック
|
||||
keyword: 設定値
|
||||
name: 名称
|
||||
regexp: 種別
|
||||
timeline: タイムライン
|
||||
notification_emails:
|
||||
digest: タイムラインからピックアップしてメールで通知する
|
||||
favourite: お気に入り登録された時
|
||||
|
|
|
@ -17,7 +17,14 @@ SimpleNavigation::Configuration.run do |navigation|
|
|||
s.item :other, safe_join([fa_icon('cog fw'), t('preferences.other')]), settings_preferences_other_url
|
||||
end
|
||||
|
||||
n.item :relationships, safe_join([fa_icon('users fw'), t('settings.relationships')]), relationships_url, if: -> { current_user.functional? }
|
||||
n.item :follow_and_subscriptions, safe_join([fa_icon('users fw'), t('settings.follow_and_subscriptions')]), relationships_url, if: -> { current_user.functional? } do |s|
|
||||
s.item :relationships, safe_join([fa_icon('users fw'), t('settings.relationships')]), relationships_url, highlights_on: %r{/relationships}
|
||||
s.item :follow_tags, safe_join([fa_icon('hashtag fw'), t('settings.follow_tags')]), settings_follow_tags_url
|
||||
s.item :account_subscribes, safe_join([fa_icon('users fw'), t('settings.account_subscribes')]), settings_account_subscribes_url
|
||||
s.item :domain_subscribes, safe_join([fa_icon('server fw'), t('settings.domain_subscribes')]), settings_domain_subscribes_url
|
||||
s.item :keyword_subscribes, safe_join([fa_icon('search fw'), t('settings.keyword_subscribes')]), settings_keyword_subscribes_url
|
||||
end
|
||||
|
||||
n.item :filters, safe_join([fa_icon('filter fw'), t('filters.index.title')]), filters_path, highlights_on: %r{/filters}, if: -> { current_user.functional? }
|
||||
n.item :statuses_cleanup, safe_join([fa_icon('history fw'), t('settings.statuses_cleanup')]), statuses_cleanup_url, if: -> { current_user.functional? }
|
||||
|
||||
|
|
|
@ -165,6 +165,10 @@ Rails.application.routes.draw do
|
|||
resources :sessions, only: [:destroy]
|
||||
resources :featured_tags, only: [:index, :create, :destroy]
|
||||
resources :favourite_tags, only: [:index, :create, :destroy]
|
||||
resources :follow_tags, only: [:index, :create, :destroy]
|
||||
resources :account_subscribes, only: [:index, :create, :destroy]
|
||||
resources :domain_subscribes, except: [:show]
|
||||
resources :keyword_subscribes, except: [:show]
|
||||
resources :login_activities, only: [:index]
|
||||
end
|
||||
|
||||
|
@ -480,6 +484,11 @@ Rails.application.routes.draw do
|
|||
end
|
||||
|
||||
resources :featured_tags, only: [:index, :create, :destroy]
|
||||
resources :favourite_tags, only: [:index, :create, :show, :update, :destroy]
|
||||
resources :follow_tags, only: [:index, :create, :show, :update, :destroy]
|
||||
resources :account_subscribes, only: [:index, :create, :show, :update, :destroy]
|
||||
resources :domain_subscribes, only: [:index, :create, :show, :update, :destroy]
|
||||
resources :keyword_subscribes, only: [:index, :create, :show, :update, :destroy]
|
||||
|
||||
resources :polls, only: [:create, :show] do
|
||||
resources :votes, only: :create, controller: 'polls/votes'
|
||||
|
|
10
db/migrate/20190829202944_create_follow_tags.rb
Normal file
10
db/migrate/20190829202944_create_follow_tags.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
class CreateFollowTags < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :follow_tags do |t|
|
||||
t.references :account, foreign_key: { on_delete: :cascade }
|
||||
t.references :tag, foreign_key: { on_delete: :cascade }
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
10
db/migrate/20190901090544_create_account_subscribes.rb
Normal file
10
db/migrate/20190901090544_create_account_subscribes.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
class CreateAccountSubscribes < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :account_subscribes do |t|
|
||||
t.references :account, foreign_key: { on_delete: :cascade }
|
||||
t.references :target_account, foreign_key: { to_table: 'accounts', on_delete: :cascade }
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
12
db/migrate/20190903113117_create_keyword_subscribes.rb
Normal file
12
db/migrate/20190903113117_create_keyword_subscribes.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
class CreateKeywordSubscribes < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :keyword_subscribes do |t|
|
||||
t.references :account, foreign_key: { on_delete: :cascade }
|
||||
t.string :keyword, null: false
|
||||
t.boolean :ignorecase, default: true
|
||||
t.boolean :regexp, default: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
class AddNameAndFlagToKeywordSubscribe < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :keyword_subscribes, :name, :string, default: '', null: false
|
||||
add_column :keyword_subscribes, :ignore_block, :boolean, default: false
|
||||
add_column :keyword_subscribes, :disabled, :boolean, default: false
|
||||
add_column :keyword_subscribes, :exclude_home, :boolean, default: false
|
||||
end
|
||||
end
|
11
db/migrate/20190911093445_create_domain_subscribes.rb
Normal file
11
db/migrate/20190911093445_create_domain_subscribes.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
class CreateDomainSubscribes < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :domain_subscribes do |t|
|
||||
t.references :account, foreign_key: { on_delete: :cascade }
|
||||
t.references :list, foreign_key: { on_delete: :cascade }
|
||||
t.string :domain, default: '', null: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddExcludeKeywordToKeywordSubscribe < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :keyword_subscribes, :exclude_keyword, :string, default: '', null: false
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddExcludeReblogToDomainSubscribe < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :domain_subscribes, :exclude_reblog, :boolean, default: true
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class RemoveExcludeHomeFromKeywordSubscribes < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
safety_assured { remove_column :keyword_subscribes, :exclude_home, :boolean }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
class AddListToKeywordSubscribes < ActiveRecord::Migration[5.2]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
add_reference :keyword_subscribes, :list, foreign_key: { on_delete: :cascade }, index: false
|
||||
add_index :keyword_subscribes, :list_id, algorithm: :concurrently
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
class AddListToAccountSubscribes < ActiveRecord::Migration[5.2]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
add_reference :account_subscribes, :list, foreign_key: { on_delete: :cascade }, index: false
|
||||
add_index :account_subscribes, :list_id, algorithm: :concurrently
|
||||
end
|
||||
end
|
8
db/migrate/20191026110502_add_list_to_follow_tags.rb
Normal file
8
db/migrate/20191026110502_add_list_to_follow_tags.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
class AddListToFollowTags < ActiveRecord::Migration[5.2]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
add_reference :follow_tags, :list, foreign_key: { on_delete: :cascade }, index: false
|
||||
add_index :follow_tags, :list_id, algorithm: :concurrently
|
||||
end
|
||||
end
|
60
db/schema.rb
60
db/schema.rb
|
@ -130,6 +130,17 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
|
|||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.index ["account_id"], name: "index_account_statuses_cleanup_policies_on_account_id"
|
||||
end
|
||||
|
||||
create_table "account_subscribes", force: :cascade do |t|
|
||||
t.bigint "account_id"
|
||||
t.bigint "target_account_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.bigint "list_id"
|
||||
t.index ["account_id"], name: "index_account_subscribes_on_account_id"
|
||||
t.index ["list_id"], name: "index_account_subscribes_on_list_id"
|
||||
t.index ["target_account_id"], name: "index_account_subscribes_on_target_account_id"
|
||||
end
|
||||
|
||||
create_table "account_warning_presets", force: :cascade do |t|
|
||||
t.text "text", default: "", null: false
|
||||
|
@ -375,6 +386,17 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
|
|||
t.index ["domain"], name: "index_domain_blocks_on_domain", unique: true
|
||||
end
|
||||
|
||||
create_table "domain_subscribes", force: :cascade do |t|
|
||||
t.bigint "account_id"
|
||||
t.bigint "list_id"
|
||||
t.string "domain", default: "", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.boolean "exclude_reblog", default: true
|
||||
t.index ["account_id"], name: "index_domain_subscribes_on_account_id"
|
||||
t.index ["list_id"], name: "index_domain_subscribes_on_list_id"
|
||||
end
|
||||
|
||||
create_table "email_domain_blocks", force: :cascade do |t|
|
||||
t.string "domain", default: "", null: false
|
||||
t.datetime "created_at", null: false
|
||||
|
@ -445,6 +467,17 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
|
|||
t.index ["account_id", "target_account_id"], name: "index_follow_requests_on_account_id_and_target_account_id", unique: true
|
||||
end
|
||||
|
||||
create_table "follow_tags", force: :cascade do |t|
|
||||
t.bigint "account_id"
|
||||
t.bigint "tag_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.bigint "list_id"
|
||||
t.index ["account_id"], name: "index_follow_tags_on_account_id"
|
||||
t.index ["list_id"], name: "index_follow_tags_on_list_id"
|
||||
t.index ["tag_id"], name: "index_follow_tags_on_tag_id"
|
||||
end
|
||||
|
||||
create_table "follows", force: :cascade do |t|
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
|
@ -453,6 +486,7 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
|
|||
t.boolean "show_reblogs", default: true, null: false
|
||||
t.string "uri"
|
||||
t.boolean "notify", default: false, null: false
|
||||
t.boolean "private", default: true, null: false
|
||||
t.index ["account_id", "target_account_id"], name: "index_follows_on_account_id_and_target_account_id", unique: true
|
||||
t.index ["target_account_id"], name: "index_follows_on_target_account_id"
|
||||
end
|
||||
|
@ -502,6 +536,22 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
|
|||
t.text "comment", default: "", null: false
|
||||
end
|
||||
|
||||
create_table "keyword_subscribes", force: :cascade do |t|
|
||||
t.bigint "account_id"
|
||||
t.string "keyword", null: false
|
||||
t.boolean "ignorecase", default: true
|
||||
t.boolean "regexp", default: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "name", default: "", null: false
|
||||
t.boolean "ignore_block", default: false
|
||||
t.boolean "disabled", default: false
|
||||
t.string "exclude_keyword", default: "", null: false
|
||||
t.bigint "list_id"
|
||||
t.index ["account_id"], name: "index_keyword_subscribes_on_account_id"
|
||||
t.index ["list_id"], name: "index_keyword_subscribes_on_list_id"
|
||||
end
|
||||
|
||||
create_table "list_accounts", force: :cascade do |t|
|
||||
t.bigint "list_id", null: false
|
||||
t.bigint "account_id", null: false
|
||||
|
@ -1013,6 +1063,9 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
|
|||
add_foreign_key "account_pins", "accounts", on_delete: :cascade
|
||||
add_foreign_key "account_stats", "accounts", on_delete: :cascade
|
||||
add_foreign_key "account_statuses_cleanup_policies", "accounts", on_delete: :cascade
|
||||
add_foreign_key "account_subscribes", "accounts", column: "target_account_id", on_delete: :cascade
|
||||
add_foreign_key "account_subscribes", "accounts", on_delete: :cascade
|
||||
add_foreign_key "account_subscribes", "lists", on_delete: :cascade
|
||||
add_foreign_key "account_warnings", "accounts", column: "target_account_id", on_delete: :cascade
|
||||
add_foreign_key "account_warnings", "accounts", on_delete: :nullify
|
||||
add_foreign_key "accounts", "accounts", column: "moved_to_account_id", on_delete: :nullify
|
||||
|
@ -1033,6 +1086,8 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
|
|||
add_foreign_key "custom_filters", "accounts", on_delete: :cascade
|
||||
add_foreign_key "devices", "accounts", on_delete: :cascade
|
||||
add_foreign_key "devices", "oauth_access_tokens", column: "access_token_id", on_delete: :cascade
|
||||
add_foreign_key "domain_subscribes", "accounts", on_delete: :cascade
|
||||
add_foreign_key "domain_subscribes", "lists", on_delete: :cascade
|
||||
add_foreign_key "email_domain_blocks", "email_domain_blocks", column: "parent_id", on_delete: :cascade
|
||||
add_foreign_key "encrypted_messages", "accounts", column: "from_account_id", on_delete: :cascade
|
||||
add_foreign_key "encrypted_messages", "devices", on_delete: :cascade
|
||||
|
@ -1045,11 +1100,16 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
|
|||
add_foreign_key "follow_recommendation_suppressions", "accounts", on_delete: :cascade
|
||||
add_foreign_key "follow_requests", "accounts", column: "target_account_id", name: "fk_9291ec025d", on_delete: :cascade
|
||||
add_foreign_key "follow_requests", "accounts", name: "fk_76d644b0e7", on_delete: :cascade
|
||||
add_foreign_key "follow_tags", "accounts", on_delete: :cascade
|
||||
add_foreign_key "follow_tags", "lists", on_delete: :cascade
|
||||
add_foreign_key "follow_tags", "tags", on_delete: :cascade
|
||||
add_foreign_key "follows", "accounts", column: "target_account_id", name: "fk_745ca29eac", on_delete: :cascade
|
||||
add_foreign_key "follows", "accounts", name: "fk_32ed1b5560", on_delete: :cascade
|
||||
add_foreign_key "identities", "users", name: "fk_bea040f377", on_delete: :cascade
|
||||
add_foreign_key "imports", "accounts", name: "fk_6db1b6e408", on_delete: :cascade
|
||||
add_foreign_key "invites", "users", on_delete: :cascade
|
||||
add_foreign_key "keyword_subscribes", "accounts", on_delete: :cascade
|
||||
add_foreign_key "keyword_subscribes", "lists", on_delete: :cascade
|
||||
add_foreign_key "list_accounts", "accounts", on_delete: :cascade
|
||||
add_foreign_key "list_accounts", "follows", on_delete: :cascade
|
||||
add_foreign_key "list_accounts", "lists", on_delete: :cascade
|
||||
|
|
4
spec/fabricators/account_subscribe_fabricator.rb
Normal file
4
spec/fabricators/account_subscribe_fabricator.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
Fabricator(:account_subscribe) do
|
||||
account
|
||||
target_account
|
||||
end
|
5
spec/fabricators/domain_subscribe_fabricator.rb
Normal file
5
spec/fabricators/domain_subscribe_fabricator.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
Fabricator(:domain_subscribe) do
|
||||
account
|
||||
list
|
||||
domain
|
||||
end
|
4
spec/fabricators/follow_tag_fabricator.rb
Normal file
4
spec/fabricators/follow_tag_fabricator.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
Fabricator(:follow_tag) do
|
||||
account
|
||||
tag
|
||||
end
|
6
spec/fabricators/keyword_subscribe_fabricator.rb
Normal file
6
spec/fabricators/keyword_subscribe_fabricator.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
Fabricator(:keyword_subscribe) do
|
||||
account
|
||||
keyword
|
||||
ignorecase
|
||||
regexp
|
||||
end
|
4
spec/models/account_subscribe_spec.rb
Normal file
4
spec/models/account_subscribe_spec.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountSubscribe, type: :model do
|
||||
end
|
4
spec/models/domain_subscribe_spec.rb
Normal file
4
spec/models/domain_subscribe_spec.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe DomainSubscribe, type: :model do
|
||||
end
|
4
spec/models/follow_tag_spec.rb
Normal file
4
spec/models/follow_tag_spec.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe FollowTag, type: :model do
|
||||
end
|
4
spec/models/keyword_subscribe_spec.rb
Normal file
4
spec/models/keyword_subscribe_spec.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe KeywordSubscribe, type: :model do
|
||||
end
|
Loading…
Reference in a new issue