Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
01e011bc90 | ||
|
e3b60b07d9 | ||
|
d0665726ca | ||
|
96c84da1d4 | ||
|
7d36a76180 | ||
|
197af5de70 | ||
|
27301312a6 | ||
|
8ac7fca5d0 | ||
|
a823509b99 | ||
|
298d28af51 | ||
|
439b2dceda | ||
|
9262f6968b | ||
|
71e73e36cd | ||
|
01c206326f | ||
|
9566893cc9 | ||
|
0e2589867f | ||
|
4acc386dd5 | ||
|
429480bb77 | ||
|
61067dc2e6 | ||
|
effb08edbb | ||
|
d1b4ebe07d | ||
|
5eef9dab80 | ||
|
2ca246d7d1 | ||
|
9a085e138e | ||
|
546b5a9dcf | ||
|
a39e719b39 | ||
|
f51b2cb2e7 | ||
|
9736753985 | ||
|
ea783d3632 | ||
|
074e9612a2 | ||
|
7406404fa3 |
2
Gemfile
2
Gemfile
@@ -54,7 +54,7 @@ gem 'sidekiq'
|
||||
gem 'sidekiq-unique-jobs'
|
||||
gem 'simple-navigation'
|
||||
gem 'simple_form'
|
||||
gem 'sprockets-rails', :require => 'sprockets/railtie'
|
||||
gem 'sprockets-rails', require: 'sprockets/railtie'
|
||||
gem 'statsd-instrument'
|
||||
gem 'twitter-text'
|
||||
gem 'tzinfo-data'
|
||||
|
44
Gemfile.lock
44
Gemfile.lock
@@ -47,17 +47,17 @@ GEM
|
||||
ast (2.3.0)
|
||||
attr_encrypted (3.0.3)
|
||||
encryptor (~> 3.0.0)
|
||||
autoprefixer-rails (6.7.7.1)
|
||||
autoprefixer-rails (6.7.7.2)
|
||||
execjs
|
||||
av (0.9.0)
|
||||
cocaine (~> 0.5.3)
|
||||
aws-sdk (2.9.6)
|
||||
aws-sdk-resources (= 2.9.6)
|
||||
aws-sdk-core (2.9.6)
|
||||
aws-sdk (2.9.12)
|
||||
aws-sdk-resources (= 2.9.12)
|
||||
aws-sdk-core (2.9.12)
|
||||
aws-sigv4 (~> 1.0)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-resources (2.9.6)
|
||||
aws-sdk-core (= 2.9.6)
|
||||
aws-sdk-resources (2.9.12)
|
||||
aws-sdk-core (= 2.9.12)
|
||||
aws-sigv4 (1.0.0)
|
||||
babel-source (5.8.35)
|
||||
babel-transpiler (0.7.0)
|
||||
@@ -94,7 +94,7 @@ GEM
|
||||
capistrano-rails (1.2.3)
|
||||
capistrano (~> 3.1)
|
||||
capistrano-bundler (~> 1.1)
|
||||
capistrano-rbenv (2.1.0)
|
||||
capistrano-rbenv (2.1.1)
|
||||
capistrano (~> 3.1)
|
||||
sshkit (~> 1.3)
|
||||
capistrano-yarn (2.0.2)
|
||||
@@ -163,8 +163,8 @@ GEM
|
||||
fuubar (2.2.0)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
globalid (0.3.7)
|
||||
activesupport (>= 4.1.0)
|
||||
globalid (0.4.0)
|
||||
activesupport (>= 4.2.0)
|
||||
goldfinger (1.2.0)
|
||||
addressable (~> 2.4)
|
||||
http (~> 2.0)
|
||||
@@ -182,7 +182,7 @@ GEM
|
||||
highline (1.7.8)
|
||||
hiredis (0.6.1)
|
||||
htmlentities (4.3.4)
|
||||
http (2.2.1)
|
||||
http (2.2.2)
|
||||
addressable (~> 2.3)
|
||||
http-cookie (~> 1.0)
|
||||
http-form_data (~> 1.0.1)
|
||||
@@ -192,8 +192,9 @@ GEM
|
||||
http-form_data (1.0.1)
|
||||
http_accept_language (2.1.0)
|
||||
http_parser.rb (0.6.0)
|
||||
httplog (0.99.2)
|
||||
httplog (0.99.3)
|
||||
colorize
|
||||
rack
|
||||
i18n (0.8.1)
|
||||
i18n-tasks (0.9.13)
|
||||
activesupport (>= 4.0.2)
|
||||
@@ -210,7 +211,7 @@ GEM
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (2.0.3)
|
||||
json (2.1.0)
|
||||
kaminari (1.0.1)
|
||||
activesupport (>= 4.1.0)
|
||||
kaminari-actionview (= 1.0.1)
|
||||
@@ -240,7 +241,7 @@ GEM
|
||||
railties (>= 4, < 5.1)
|
||||
loofah (2.0.3)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.6.4)
|
||||
mail (2.6.5)
|
||||
mime-types (>= 1.16, < 4)
|
||||
method_source (0.8.2)
|
||||
microformats2 (2.1.0)
|
||||
@@ -261,7 +262,7 @@ GEM
|
||||
mini_portile2 (~> 2.1.0)
|
||||
nokogumbo (1.4.10)
|
||||
nokogiri
|
||||
oj (2.18.5)
|
||||
oj (3.0.2)
|
||||
openssl (2.0.3)
|
||||
orm_adapter (0.5.0)
|
||||
ostatus2 (1.1.0)
|
||||
@@ -269,7 +270,7 @@ GEM
|
||||
http (~> 2.0)
|
||||
nokogiri (~> 1.6)
|
||||
openssl (~> 2.0)
|
||||
ox (2.4.11)
|
||||
ox (2.4.13)
|
||||
paperclip (5.1.0)
|
||||
activemodel (>= 4.2.0)
|
||||
activesupport (>= 4.2.0)
|
||||
@@ -282,9 +283,9 @@ GEM
|
||||
parser (2.4.0.0)
|
||||
ast (~> 2.2)
|
||||
pg (0.20.0)
|
||||
pghero (1.6.4)
|
||||
pghero (1.6.5)
|
||||
activerecord
|
||||
pkg-config (1.1.7)
|
||||
pkg-config (1.2.0)
|
||||
powerpack (0.1.1)
|
||||
pry (0.10.4)
|
||||
coderay (~> 1.1.0)
|
||||
@@ -342,7 +343,8 @@ GEM
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rainbow (2.2.1)
|
||||
rainbow (2.2.2)
|
||||
rake
|
||||
rake (12.0.0)
|
||||
react-rails (1.11.0)
|
||||
babel-transpiler (>= 0.7.0)
|
||||
@@ -358,8 +360,8 @@ GEM
|
||||
redis-activesupport (5.0.2)
|
||||
activesupport (>= 3, < 6)
|
||||
redis-store (~> 1.3.0)
|
||||
redis-rack (2.0.1)
|
||||
rack (>= 2.0, < 3)
|
||||
redis-rack (2.0.2)
|
||||
rack (>= 1.5, < 3)
|
||||
redis-store (>= 1.2, < 1.4)
|
||||
redis-rails (5.0.2)
|
||||
redis-actionpack (>= 5.0, < 6)
|
||||
@@ -464,7 +466,7 @@ GEM
|
||||
uniform_notifier (1.10.0)
|
||||
warden (1.2.7)
|
||||
rack (>= 1.0)
|
||||
webmock (2.3.2)
|
||||
webmock (3.0.1)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff
|
||||
|
@@ -18,7 +18,7 @@ class UploadForm extends React.PureComponent {
|
||||
<div className='compose-form__upload' key={attachment.get('id')}>
|
||||
<Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
|
||||
{({ scale }) =>
|
||||
<div className='compose-form__upload-thumbnail' style={{ transform: `translateZ(0) scale(${scale})`, background: `url(${attachment.get('preview_url')}) no-repeat center` }}>
|
||||
<div className='compose-form__upload-thumbnail' style={{ transform: `translateZ(0) scale(${scale})`, backgroundImage: `url(${attachment.get('preview_url')})` }}>
|
||||
<IconButton icon='times' title={intl.formatMessage(messages.undo)} size={36} onClick={this.props.onRemoveFile.bind(this, attachment.get('id'))} />
|
||||
</div>
|
||||
}
|
||||
|
@@ -25,7 +25,7 @@ const PageOne = ({ acct, domain }) => (
|
||||
|
||||
<div>
|
||||
<h1><FormattedMessage id='onboarding.page_one.welcome' defaultMessage='Welcome to Mastodon!' /></h1>
|
||||
<p><FormattedMessage id='onboarding.page_one.federation' defaultMessage='Mastodon is a network of independent servers joining up to make one larger social network. We call these servers: <em>Instances</em>' /></p>
|
||||
<p><FormattedMessage id='onboarding.page_one.federation' defaultMessage='Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.' /></p>
|
||||
<p><FormattedMessage id='onboarding.page_one.handle' defaultMessage='You are on {domain}, so your full handle is {handle}' values={{ domain, handle: <strong>{acct}@{domain}</strong> }}/></p>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -39,7 +39,7 @@ const en = {
|
||||
"compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
|
||||
"compose_form.lock_disclaimer.lock": "locked",
|
||||
"compose_form.placeholder": "What is on your mind?",
|
||||
"compose_form.privacy_disclaimer": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.",
|
||||
"compose_form.privacy_disclaimer": "Your post will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is not a public post, and it may be boosted or otherwise made visible to unintended recipients.",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.sensitive": "Mark media as sensitive",
|
||||
"compose_form.spoiler": "Hide text behind warning",
|
||||
@@ -111,7 +111,7 @@ const en = {
|
||||
"onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
|
||||
"onboarding.page_four.home": "The home timeline shows posts from people you follow.",
|
||||
"onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
|
||||
"onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers: <em>Instances</em>",
|
||||
"onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
|
||||
"onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
|
||||
"onboarding.page_one.welcome": "Welcome to Mastodon!",
|
||||
"onboarding.page_six.admin": "Your instance's admin is {admin}.",
|
||||
@@ -133,7 +133,7 @@ const en = {
|
||||
"privacy.private.short": "Followers-only",
|
||||
"privacy.public.long": "Post to public timelines",
|
||||
"privacy.public.short": "Public",
|
||||
"privacy.unlisted.long": "Do not show in public timelines",
|
||||
"privacy.unlisted.long": "Do not post to public timelines",
|
||||
"privacy.unlisted.short": "Unlisted",
|
||||
"reply_indicator.cancel": "Cancel",
|
||||
"report.heading": "New report",
|
||||
|
@@ -24,6 +24,8 @@ const ru = {
|
||||
"column.notifications": "Уведомления",
|
||||
"column.public": "Глобальная лента",
|
||||
"column_back_button.label": "Назад",
|
||||
"column_subheading.navigation": "Навигация",
|
||||
"column_subheading.settings": "Настройки",
|
||||
"compose_form.placeholder": "О чем Вы думаете?",
|
||||
"compose_form.privacy_disclaimer": "Ваш приватный статус будет доставлен упомянутым пользователям на доменах {domains}. Доверяете ли вы {domainsCount, plural, one {этому серверу} other {этим серверам}}? Приватность постов работает только на узлах Mastodon. Если {domains} {domainsCount, plural, one {не является узлом Mastodon} other {не являются узлами Mastodon}}, приватность поста не будет указана, и он может оказаться продвинут или иным образом показан не обозначенным Вами пользователям.",
|
||||
"compose_form.publish": "Трубить",
|
||||
@@ -101,7 +103,7 @@ const ru = {
|
||||
"report.target": "Жалуемся на",
|
||||
"search.placeholder": "Поиск",
|
||||
"search.status_by": "Статус от {name}",
|
||||
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
|
||||
"search_results.total": "{count, number} {count, plural, one {результат} few {результата} many {результатов} other {результатов}}",
|
||||
"status.cannot_reblog": "Этот статус не может быть продвинут",
|
||||
"status.delete": "Удалить",
|
||||
"status.favourite": "Нравится",
|
||||
|
@@ -49,11 +49,11 @@ const zh_cn = {
|
||||
"compose_form.placeholder": "在想啥?",
|
||||
"compose_form.privacy_disclaimer": "你的私人嘟文,将被发送至你所提及的 {domains} 用户。你是否信任{domainsCount, plural, one {这个网站} other {这些网站}}?请留意,嘟文隐私设置只适用于各 Mastodon 服务站,如果 {domains} {domainsCount, plural, one {不是 Mastodon 服务站} other {之中有些不是 Mastodon 服务站}},对方将无法收到这篇嘟文的隐私设置,然后可能被转嘟给不能预知的用户阅读。",
|
||||
"compose_form.private": "标示为“只有关注你的人能看”",
|
||||
// Going "toot-toot!" here below.
|
||||
"compose_form.publish": "嘟嘟!",
|
||||
// Going "toot-toot" here below.
|
||||
"compose_form.publish": "嘟嘟",
|
||||
"compose_form.sensitive": "将媒体文件标示为“敏感内容”",
|
||||
"compose_form.spoiler_placeholder": "敏感内容的警告消息",
|
||||
"compose_form.spoiler": "将部份文本藏于警告消息之后",
|
||||
"compose_form.spoiler": "将部分文本藏于警告消息之后",
|
||||
"compose_form.unlisted": "请勿在公共时间轴显示",
|
||||
"emoji_button.label": "加入表情符号",
|
||||
"empty_column.community": "本站时间轴暂时未有内容,快贴文来抢头香啊!",
|
||||
@@ -77,7 +77,7 @@ const zh_cn = {
|
||||
"home.column_settings.show_replies": "显示回应嘟文",
|
||||
"home.settings": "字段设置",
|
||||
"lightbox.close": "关闭",
|
||||
"loading_indicator.label": "加载中...",
|
||||
"loading_indicator.label": "加载中……",
|
||||
"media_gallery.toggle_visible": "打开或关上",
|
||||
"missing_indicator.label": "找不到内容",
|
||||
"navigation_bar.blocks": "被屏蔽的用户",
|
||||
|
@@ -274,6 +274,9 @@
|
||||
|
||||
.compose-form__upload-thumbnail {
|
||||
border-radius: 4px;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
height: 100px;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -3094,7 +3097,7 @@ button.icon-button.active i.fa-retweet {
|
||||
object-fit: cover;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
transform: translateY(-35%);
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
@@ -3106,19 +3109,24 @@ button.icon-button.active i.fa-retweet {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
text-shadow: 0px 1px 1px #000, 1px 0px 1px #000;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.status__video-player-spoiler {
|
||||
color: #fff;
|
||||
left: 4px;
|
||||
position: absolute;
|
||||
text-shadow: 0px 1px 1px #000, 1px 0px 1px #000;
|
||||
top: 4px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.status__video-player-expand {
|
||||
bottom: 4px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.status__video-player-mute {
|
||||
top: 4px;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
@@ -3133,6 +3141,7 @@ button.icon-button.active i.fa-retweet {
|
||||
border-radius: 100px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 36px;
|
||||
left: 50%;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
|
@@ -4,12 +4,6 @@ class FollowerAccountsController < ApplicationController
|
||||
include AccountControllerConcern
|
||||
|
||||
def index
|
||||
@accounts = ordered_accounts.page(params[:page]).per(FOLLOW_PER_PAGE)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ordered_accounts
|
||||
@account.followers.order('follows.created_at desc')
|
||||
@accounts = @account.followers.page(params[:page]).per(FOLLOW_PER_PAGE)
|
||||
end
|
||||
end
|
||||
|
@@ -4,12 +4,6 @@ class FollowingAccountsController < ApplicationController
|
||||
include AccountControllerConcern
|
||||
|
||||
def index
|
||||
@accounts = ordered_accounts.page(params[:page]).per(FOLLOW_PER_PAGE)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ordered_accounts
|
||||
@account.following.order('follows.created_at desc')
|
||||
@accounts = @account.following.page(params[:page]).per(FOLLOW_PER_PAGE)
|
||||
end
|
||||
end
|
||||
|
@@ -4,7 +4,7 @@ class TagsController < ApplicationController
|
||||
layout 'public'
|
||||
|
||||
def show
|
||||
@tag = Tag.find_by(name: params[:id].downcase)
|
||||
@tag = Tag.find_by!(name: params[:id].downcase)
|
||||
@statuses = @tag.nil? ? [] : Status.as_tag_timeline(@tag, current_account, params[:local]).paginate_by_max_id(20, params[:max_id])
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
end
|
||||
|
@@ -3,7 +3,7 @@
|
||||
module SettingsHelper
|
||||
HUMAN_LOCALES = {
|
||||
en: 'English',
|
||||
ar: 'عربى',
|
||||
ar: 'العربية',
|
||||
bg: 'Български',
|
||||
de: 'Deutsch',
|
||||
eo: 'Esperanto',
|
||||
|
@@ -23,7 +23,7 @@ class Formatter
|
||||
end
|
||||
|
||||
def reformat(html)
|
||||
sanitize(html, Sanitize::Config::MASTODON_STRICT)
|
||||
sanitize(html, Sanitize::Config::MASTODON_STRICT).html_safe # rubocop:disable Rails/OutputSafety
|
||||
end
|
||||
|
||||
def plaintext(status)
|
||||
|
@@ -46,6 +46,8 @@ class Account < ApplicationRecord
|
||||
# Block relationships
|
||||
has_many :block_relationships, class_name: 'Block', foreign_key: 'account_id', dependent: :destroy
|
||||
has_many :blocking, -> { order('blocks.id desc') }, through: :block_relationships, source: :target_account
|
||||
has_many :blocked_by_relationships, class_name: 'Block', foreign_key: :target_account_id, dependent: :destroy
|
||||
has_many :blocked_by, -> { order('blocks.id desc') }, through: :blocked_by_relationships, source: :account
|
||||
|
||||
# Mute relationships
|
||||
has_many :mute_relationships, class_name: 'Mute', foreign_key: 'account_id', dependent: :destroy
|
||||
@@ -72,6 +74,14 @@ class Account < ApplicationRecord
|
||||
scope :alphabetic, -> { order(domain: :asc, username: :asc) }
|
||||
scope :by_domain_accounts, -> { group(:domain).select(:domain, 'COUNT(*) AS accounts_count').order('accounts_count desc') }
|
||||
|
||||
delegate :email,
|
||||
:current_sign_in_ip,
|
||||
:current_sign_in_at,
|
||||
:confirmed?,
|
||||
to: :user,
|
||||
prefix: true,
|
||||
allow_nil: true
|
||||
|
||||
def follow!(other_account)
|
||||
active_relationships.where(target_account: other_account).first_or_create!(target_account: other_account)
|
||||
end
|
||||
@@ -211,6 +221,10 @@ class Account < ApplicationRecord
|
||||
username
|
||||
end
|
||||
|
||||
def excluded_from_timeline_account_ids
|
||||
Rails.cache.fetch("exclude_account_ids_for:#{id}") { blocking.pluck(:target_account_id) + blocked_by.pluck(:account_id) + muting.pluck(:target_account_id) }
|
||||
end
|
||||
|
||||
class << self
|
||||
def find_local!(username)
|
||||
find_remote!(username, nil)
|
||||
|
@@ -96,7 +96,7 @@ class MediaAttachment < ApplicationRecord
|
||||
private
|
||||
|
||||
def set_shortcode
|
||||
self.type = :unknown if file.blank? && type.blank?
|
||||
self.type = :unknown if file.blank? && !type_changed?
|
||||
|
||||
return unless local?
|
||||
|
||||
|
@@ -37,6 +37,12 @@ class Status < ApplicationRecord
|
||||
|
||||
scope :without_replies, -> { where('statuses.reply = FALSE OR statuses.in_reply_to_account_id = statuses.account_id') }
|
||||
scope :without_reblogs, -> { where('statuses.reblog_of_id IS NULL') }
|
||||
scope :with_public_visibility, -> { where(visibility: :public) }
|
||||
scope :tagged_with, ->(tag) { joins(:statuses_tags).where(statuses_tags: { tag_id: tag }) }
|
||||
scope :local_only, -> { left_outer_joins(:account).where(accounts: { domain: nil }) }
|
||||
scope :excluding_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced: false }) }
|
||||
scope :including_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced: true }) }
|
||||
scope :not_excluded_by_account, ->(account) { where.not(account_id: account.excluded_from_timeline_account_ids) }
|
||||
|
||||
cache_associated :account, :application, :media_attachments, :tags, :stream_entry, mentions: :account, reblog: [:account, :application, :stream_entry, :tags, :media_attachments, mentions: :account], thread: :account
|
||||
|
||||
@@ -118,25 +124,15 @@ class Status < ApplicationRecord
|
||||
end
|
||||
|
||||
def as_public_timeline(account = nil, local_only = false)
|
||||
query = joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id')
|
||||
.where(visibility: :public)
|
||||
.without_replies
|
||||
.without_reblogs
|
||||
query = timeline_scope(local_only).without_replies
|
||||
|
||||
query = query.where('accounts.domain IS NULL') if local_only
|
||||
|
||||
account.nil? ? filter_timeline_default(query) : filter_timeline_default(filter_timeline(query, account))
|
||||
apply_timeline_filters(query, account)
|
||||
end
|
||||
|
||||
def as_tag_timeline(tag, account = nil, local_only = false)
|
||||
query = tag.statuses
|
||||
.joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id')
|
||||
.where(visibility: :public)
|
||||
.without_reblogs
|
||||
query = timeline_scope(local_only).tagged_with(tag)
|
||||
|
||||
query = query.where('accounts.domain IS NULL') if local_only
|
||||
|
||||
account.nil? ? filter_timeline_default(query) : filter_timeline_default(filter_timeline(query, account))
|
||||
apply_timeline_filters(query, account)
|
||||
end
|
||||
|
||||
def as_outbox_timeline(account)
|
||||
@@ -185,15 +181,36 @@ class Status < ApplicationRecord
|
||||
|
||||
private
|
||||
|
||||
def filter_timeline(query, account)
|
||||
blocked = Rails.cache.fetch("exclude_account_ids_for:#{account.id}") { Block.where(account: account).pluck(:target_account_id) + Block.where(target_account: account).pluck(:account_id) + Mute.where(account: account).pluck(:target_account_id) }
|
||||
query = query.where('statuses.account_id NOT IN (?)', blocked) unless blocked.empty? # Only give us statuses from people we haven't blocked, or muted, or that have blocked us
|
||||
query = query.where('accounts.silenced = TRUE') if account.silenced? # and if we're hellbanned, only people who are also hellbanned
|
||||
query
|
||||
def timeline_scope(local_only = false)
|
||||
starting_scope = local_only ? Status.local_only : Status
|
||||
starting_scope
|
||||
.with_public_visibility
|
||||
.without_reblogs
|
||||
end
|
||||
|
||||
def apply_timeline_filters(query, account)
|
||||
if account.nil?
|
||||
filter_timeline_default(query)
|
||||
else
|
||||
filter_timeline_for_account(query, account)
|
||||
end
|
||||
end
|
||||
|
||||
def filter_timeline_for_account(query, account)
|
||||
query = query.not_excluded_by_account(account)
|
||||
query.merge(account_silencing_filter(account))
|
||||
end
|
||||
|
||||
def filter_timeline_default(query)
|
||||
query.where('accounts.silenced = FALSE')
|
||||
query.excluding_silenced_accounts
|
||||
end
|
||||
|
||||
def account_silencing_filter(account)
|
||||
if account.silenced?
|
||||
including_silenced_accounts
|
||||
else
|
||||
excluding_silenced_accounts
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@@ -39,12 +39,15 @@ class BlockDomainService < BaseService
|
||||
blocked_domain_accounts.find_each do |account|
|
||||
account.avatar.destroy
|
||||
account.header.destroy
|
||||
account.save
|
||||
end
|
||||
end
|
||||
|
||||
def clear_account_attachments
|
||||
media_from_blocked_domain.find_each do |attachment|
|
||||
attachment.file.destroy
|
||||
attachment.type = :unknown
|
||||
attachment.save
|
||||
end
|
||||
end
|
||||
|
||||
|
@@ -5,7 +5,8 @@
|
||||
- if user_signed_in?
|
||||
%li= link_to t('about.get_started'), root_path
|
||||
- else
|
||||
%li= link_to t('about.get_started'), new_user_registration_path
|
||||
- if instance.open_registrations
|
||||
%li= link_to t('about.get_started'), new_user_registration_path
|
||||
%li= link_to t('auth.login'), new_user_session_path
|
||||
%li= link_to t('about.terms'), terms_path
|
||||
%li= link_to t('about.source_code'), 'https://github.com/tootsuite/mastodon'
|
||||
|
@@ -29,5 +29,5 @@
|
||||
|
||||
.sidebar
|
||||
= render 'contact', contact: @instance_presenter
|
||||
= render 'links'
|
||||
= render 'links', instance: @instance_presenter
|
||||
= render 'version', version: @instance_presenter
|
||||
|
@@ -16,15 +16,15 @@
|
||||
- if @account.local?
|
||||
%tr
|
||||
%th= t('admin.accounts.email')
|
||||
%td= @account.user.email
|
||||
%td= @account.user_email
|
||||
%tr
|
||||
%th= t('admin.accounts.most_recent_ip')
|
||||
%td= @account.user.current_sign_in_ip
|
||||
%td= @account.user_current_sign_in_ip
|
||||
%tr
|
||||
%th= t('admin.accounts.most_recent_activity')
|
||||
%td
|
||||
- if @account.user.current_sign_in_at
|
||||
= l @account.user.current_sign_in_at
|
||||
- if @account.user_current_sign_in_at
|
||||
= l @account.user_current_sign_in_at
|
||||
- else
|
||||
Never
|
||||
- else
|
||||
@@ -78,7 +78,7 @@
|
||||
= link_to t('admin.accounts.silence'), admin_account_silence_path(@account.id), method: :post, class: 'button'
|
||||
|
||||
- if @account.local?
|
||||
- unless @account.user.confirmed?
|
||||
- unless @account.user_confirmed?
|
||||
= link_to t('admin.accounts.confirm'), admin_account_confirmation_path(@account.id), method: :post, class: 'button'
|
||||
|
||||
- if @account.suspended?
|
||||
|
@@ -4,11 +4,11 @@
|
||||
.report-accounts
|
||||
.report-accounts__item
|
||||
%strong= t('admin.reports.reported_account')
|
||||
= render partial: 'authorize_follow/card', locals: { account: @report.target_account }
|
||||
= render partial: 'authorize_follows/card', locals: { account: @report.target_account }
|
||||
= render partial: 'admin/accounts/card', locals: { account: @report.target_account }
|
||||
.report-accounts__item
|
||||
%strong= t('admin.reports.reported_by')
|
||||
= render partial: 'authorize_follow/card', locals: { account: @report.account }
|
||||
= render partial: 'authorize_follows/card', locals: { account: @report.account }
|
||||
= render partial: 'admin/accounts/card', locals: { account: @report.account }
|
||||
|
||||
%p
|
||||
|
@@ -4,4 +4,4 @@ node(:uri) { site_hostname }
|
||||
node(:title) { Setting.site_title }
|
||||
node(:description) { Setting.site_description }
|
||||
node(:email) { Setting.site_contact_email }
|
||||
node(:version) { Mastodon::Version }
|
||||
node(:version) { Mastodon::Version.to_s }
|
||||
|
@@ -2,7 +2,7 @@
|
||||
.follow-prompt
|
||||
%h2= t('remote_follow.prompt')
|
||||
|
||||
= render partial: 'authorize_follow/card', locals: { account: @account }
|
||||
= render partial: 'authorize_follows/card', locals: { account: @account }
|
||||
|
||||
= simple_form_for @remote_follow, as: :remote_follow, url: account_remote_follow_path(@account) do |f|
|
||||
= render 'shared/error_messages', object: @remote_follow
|
||||
|
@@ -58,4 +58,6 @@ ru:
|
||||
not_locked: не был заблокирован
|
||||
not_saved:
|
||||
one: '1 ошибка помешала сохранению этого %{resource}:'
|
||||
other: "%{count} ошибки помешали сохранению этого %{resource}:"
|
||||
few: "%{count} ошибки помешали сохранению этого %{resource}:"
|
||||
many: "%{count} ошибок помешали сохранению этого %{resource}:"
|
||||
other: "%{count} ошибок помешали сохранению этого %{resource}:"
|
||||
|
@@ -103,6 +103,8 @@ ru:
|
||||
show:
|
||||
affected_accounts:
|
||||
one: Влияет на один аккаунт в базе данных
|
||||
few: "Влияет на %{count} аккаунта в базе данных"
|
||||
many: "Влияет на %{count} аккаунтов в базе данных"
|
||||
other: "Влияет на %{count} аккаунтов в базе данных"
|
||||
retroactive:
|
||||
silence: Снять глушение со всех существующих аккаунтов этого домена
|
||||
@@ -212,12 +214,12 @@ ru:
|
||||
domain: Домен
|
||||
explanation_html: Если Вы хотите быть уверены в приватности Ваших статусов, Вы должны иметь четкое представление о том, кто на Вас подписан. <strong>Ваши приватные статусы отправляются всем узлам, на которых у Вас есть подписчики</strong>. Рекомендуем удалить из подписчиков пользователей узлов, администрации или программному обеспечению которых Вы не доверяете.
|
||||
followers_count: Количество подписчиков
|
||||
lock_link: Закрыть аккаунт
|
||||
lock_link: Закройте аккаунт
|
||||
purge: Удалить из подписчиков
|
||||
success:
|
||||
one: В процессе мягкой блокировки подписчиков с одного домена...
|
||||
other: В процессе мягкой блокировки подписчиков с %{count} доменов...
|
||||
true_privacy_html: Пожалуйста, заметьте, что <strong>настоящая приватность может быть достигнута тольк при помощи end-to-end шифрования</strong>.
|
||||
true_privacy_html: Пожалуйста, заметьте, что <strong>настоящая конфиденциальность может быть достигнута только при помощи end-to-end шифрования</strong>.
|
||||
unlocked_warning_html: Кто угодно может подписаться на Вас и получить доступ к просмотру Ваших приватных статусов. %{lock_link}, чтобы получить возможность рассматривать и вручную подтверждать запросы о подписке.
|
||||
unlocked_warning_title: Ваш аккаунт не закрыт для подписки
|
||||
generic:
|
||||
@@ -246,9 +248,13 @@ ru:
|
||||
mention: "%{name} упомянул(а) Вас в:"
|
||||
new_followers_summary:
|
||||
one: У Вас появился новый подписчик! Ура!
|
||||
other: У Вас появилось %{count} новых подписчика(-ов)! Отлично!
|
||||
few: У Вас появилось %{count} новых подписчика! Отлично!
|
||||
many: У Вас появилось %{count} новых подписчиков! Отлично!
|
||||
other: У Вас появилось %{count} новых подписчиков! Отлично!
|
||||
subject:
|
||||
one: "1 новое уведомление с Вашего последнего захода \U0001F418"
|
||||
few: "%{count} новых уведомления с Вашего последнего захода \U0001F418"
|
||||
many: "%{count} новых уведомлений с Вашего последнего захода \U0001F418"
|
||||
other: "%{count} новых уведомлений с Вашего последнего захода \U0001F418"
|
||||
favourite:
|
||||
body: 'Ваш статус понравился %{name}:'
|
||||
|
@@ -13,7 +13,7 @@ module Mastodon
|
||||
end
|
||||
|
||||
def patch
|
||||
0
|
||||
2
|
||||
end
|
||||
|
||||
def pre
|
||||
|
@@ -53,8 +53,18 @@ namespace :mastodon do
|
||||
task remove_remote: :environment do
|
||||
MediaAttachment.where.not(remote_url: '').where('created_at < ?', 1.week.ago).find_each do |media|
|
||||
media.file.destroy
|
||||
media.type = :unknown
|
||||
media.save
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Set unknown attachment type for remote-only attachments'
|
||||
task set_unknown: :environment do
|
||||
Rails.logger.debug 'Setting unknown attachment type for remote-only attachments...'
|
||||
# rubocop:disable Rails/SkipsModelValidations
|
||||
MediaAttachment.where(file_file_name: nil).where.not(type: :unknown).in_batches.update_all(type: :unknown)
|
||||
Rails.logger.debug 'Done!'
|
||||
end
|
||||
end
|
||||
|
||||
namespace :push do
|
||||
|
@@ -2,6 +2,7 @@ require 'rails_helper'
|
||||
|
||||
describe AccountFollowController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
|
||||
|
@@ -2,6 +2,7 @@ require 'rails_helper'
|
||||
|
||||
describe AccountUnfollowController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
|
||||
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::AccountsController, type: :controller do
|
||||
render_views
|
||||
|
||||
before do
|
||||
sign_in Fabricate(:user, admin: true), scope: :user
|
||||
end
|
||||
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::DomainBlocksController, type: :controller do
|
||||
render_views
|
||||
|
||||
before do
|
||||
sign_in Fabricate(:user, admin: true), scope: :user
|
||||
end
|
||||
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::InstancesController, type: :controller do
|
||||
render_views
|
||||
|
||||
before do
|
||||
sign_in Fabricate(:user, admin: true), scope: :user
|
||||
end
|
||||
|
@@ -2,6 +2,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::PubsubhubbubController, type: :controller do
|
||||
render_views
|
||||
|
||||
describe 'GET #index' do
|
||||
before do
|
||||
sign_in Fabricate(:user, admin: true), scope: :user
|
||||
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::ReportedStatusesController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, admin: true) }
|
||||
before do
|
||||
sign_in user, scope: :user
|
||||
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::ReportsController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user, admin: true) }
|
||||
before do
|
||||
sign_in user, scope: :user
|
||||
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::ResetsController do
|
||||
render_views
|
||||
|
||||
let(:account) { Fabricate(:account, user: Fabricate(:user)) }
|
||||
before do
|
||||
sign_in Fabricate(:user, admin: true), scope: :user
|
||||
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::SilencesController do
|
||||
render_views
|
||||
|
||||
let(:account) { Fabricate(:account) }
|
||||
before do
|
||||
sign_in Fabricate(:user, admin: true), scope: :user
|
||||
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::SuspensionsController do
|
||||
render_views
|
||||
|
||||
let(:account) { Fabricate(:account) }
|
||||
before do
|
||||
sign_in Fabricate(:user, admin: true), scope: :user
|
||||
|
@@ -3,6 +3,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe AuthorizeFollowsController do
|
||||
render_views
|
||||
|
||||
describe 'GET #show' do
|
||||
describe 'when signed out' do
|
||||
it 'redirects to sign in page' do
|
||||
@@ -38,7 +40,7 @@ describe AuthorizeFollowsController do
|
||||
end
|
||||
|
||||
it 'sets account from url' do
|
||||
account = double
|
||||
account = Account.new
|
||||
service = double
|
||||
allow(FetchRemoteAccountService).to receive(:new).and_return(service)
|
||||
allow(service).to receive(:call).with('http://example.com').and_return(account)
|
||||
@@ -50,7 +52,7 @@ describe AuthorizeFollowsController do
|
||||
end
|
||||
|
||||
it 'sets account from acct uri' do
|
||||
account = double
|
||||
account = Account.new
|
||||
service = double
|
||||
allow(FollowRemoteAccountService).to receive(:new).and_return(service)
|
||||
allow(service).to receive(:call).with('found@hostname').and_return(account)
|
||||
|
@@ -2,6 +2,7 @@ require 'rails_helper'
|
||||
|
||||
describe FollowerAccountsController do
|
||||
render_views
|
||||
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
|
||||
describe 'GET #index' do
|
||||
|
@@ -2,6 +2,7 @@ require 'rails_helper'
|
||||
|
||||
describe FollowingAccountsController do
|
||||
render_views
|
||||
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
|
||||
describe 'GET #index' do
|
||||
|
@@ -3,6 +3,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe MediaController do
|
||||
render_views
|
||||
|
||||
describe '#show' do
|
||||
it 'redirects to the file url when attached to a status' do
|
||||
status = Fabricate(:status)
|
||||
|
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Oauth::AuthorizationsController, type: :controller do
|
||||
|
16
spec/controllers/remote_follow_controller_spec.rb
Normal file
16
spec/controllers/remote_follow_controller_spec.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe RemoteFollowController do
|
||||
render_views
|
||||
|
||||
describe '#new' do
|
||||
it 'returns a success' do
|
||||
account = Fabricate(:account)
|
||||
get :new, params: { account_username: account.to_param }
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
end
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Settings::Exports::BlockedAccountsController do
|
||||
render_views
|
||||
|
||||
before do
|
||||
sign_in Fabricate(:user), scope: :user
|
||||
end
|
||||
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Settings::Exports::FollowingAccountsController do
|
||||
render_views
|
||||
|
||||
before do
|
||||
sign_in Fabricate(:user), scope: :user
|
||||
end
|
||||
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Settings::Exports::MutedAccountsController do
|
||||
render_views
|
||||
|
||||
before do
|
||||
sign_in Fabricate(:user), scope: :user
|
||||
end
|
||||
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Settings::FollowerDomainsController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
|
@@ -1,6 +1,7 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Settings::ImportsController, type: :controller do
|
||||
render_views
|
||||
|
||||
before do
|
||||
sign_in Fabricate(:user), scope: :user
|
||||
|
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Settings::PreferencesController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
|
@@ -1,6 +1,7 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Settings::ProfilesController, type: :controller do
|
||||
render_views
|
||||
|
||||
before do
|
||||
sign_in Fabricate(:user), scope: :user
|
||||
|
@@ -12,5 +12,11 @@ RSpec.describe TagsController, type: :controller do
|
||||
get :show, params: { id: 'test' }
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
|
||||
it 'returns http missing for non-existent tag' do
|
||||
get :show, params: { id: 'none' }
|
||||
|
||||
expect(response).to have_http_status(:missing)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -1,3 +1,4 @@
|
||||
Fabricator(:mute) do
|
||||
|
||||
account
|
||||
target_account { Fabricate(:account) }
|
||||
end
|
||||
|
@@ -190,6 +190,21 @@ RSpec.describe Account, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#excluded_from_timeline_account_ids' do
|
||||
it 'includes account ids of blockings, blocked_bys and mutes' do
|
||||
account = Fabricate(:account)
|
||||
block = Fabricate(:block, account: account)
|
||||
mute = Fabricate(:mute, account: account)
|
||||
block_by = Fabricate(:block, target_account: account)
|
||||
|
||||
results = account.excluded_from_timeline_account_ids
|
||||
expect(results.size).to eq 3
|
||||
expect(results).to include(block.target_account.id)
|
||||
expect(results).to include(mute.target_account.id)
|
||||
expect(results).to include(block_by.account.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.search_for' do
|
||||
before do
|
||||
@match = Fabricate(
|
||||
|
@@ -2,6 +2,20 @@ require 'rails_helper'
|
||||
|
||||
RSpec.describe Feed, type: :model do
|
||||
describe '#get' do
|
||||
pending
|
||||
it "gets statuses with ids in the range, maintining the order from Redis" do
|
||||
account = Fabricate(:account)
|
||||
Fabricate(:status, account: account, id: 1)
|
||||
Fabricate(:status, account: account, id: 2)
|
||||
Fabricate(:status, account: account, id: 3)
|
||||
Fabricate(:status, account: account, id: 10)
|
||||
redis = double(zrevrangebyscore: [["val2", 2.0], ["val1", 1.0], ["val3", 3.0], ["deleted", 4.0]])
|
||||
allow(Redis).to receive(:current).and_return(redis)
|
||||
|
||||
feed = Feed.new("type", account)
|
||||
results = feed.get(3)
|
||||
|
||||
expect(results.map(&:id)).to eq [2, 1, 3]
|
||||
expect(results.first.attributes.keys).to eq ["id", "updated_at"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -127,6 +127,19 @@ RSpec.describe Status, type: :model do
|
||||
pending
|
||||
end
|
||||
|
||||
describe '.local_only' do
|
||||
it 'returns only statuses from local accounts' do
|
||||
local_account = Fabricate(:account, domain: nil)
|
||||
remote_account = Fabricate(:account, domain: 'test.com')
|
||||
local_status = Fabricate(:status, account: local_account)
|
||||
remote_status = Fabricate(:status, account: remote_account)
|
||||
|
||||
results = described_class.local_only
|
||||
expect(results).to include(local_status)
|
||||
expect(results).not_to include(remote_status)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.as_home_timeline' do
|
||||
before do
|
||||
account = Fabricate(:account)
|
||||
@@ -153,4 +166,122 @@ RSpec.describe Status, type: :model do
|
||||
expect(@results).not_to include(@not_followed_status)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.as_public_timeline' do
|
||||
it 'only includes statuses with public visibility' do
|
||||
public_status = Fabricate(:status, visibility: :public)
|
||||
private_status = Fabricate(:status, visibility: :private)
|
||||
|
||||
results = Status.as_public_timeline
|
||||
expect(results).to include(public_status)
|
||||
expect(results).not_to include(private_status)
|
||||
end
|
||||
|
||||
it 'does not include replies' do
|
||||
status = Fabricate(:status)
|
||||
reply = Fabricate(:status, in_reply_to_id: status.id)
|
||||
|
||||
results = Status.as_public_timeline
|
||||
expect(results).to include(status)
|
||||
expect(results).not_to include(reply)
|
||||
end
|
||||
|
||||
it 'does not include boosts' do
|
||||
status = Fabricate(:status)
|
||||
boost = Fabricate(:status, reblog_of_id: status.id)
|
||||
|
||||
results = Status.as_public_timeline
|
||||
expect(results).to include(status)
|
||||
expect(results).not_to include(boost)
|
||||
end
|
||||
|
||||
it 'filters out silenced accounts' do
|
||||
account = Fabricate(:account)
|
||||
silenced_account = Fabricate(:account, silenced: true)
|
||||
status = Fabricate(:status, account: account)
|
||||
silenced_status = Fabricate(:status, account: silenced_account)
|
||||
|
||||
results = Status.as_public_timeline
|
||||
expect(results).to include(status)
|
||||
expect(results).not_to include(silenced_status)
|
||||
end
|
||||
|
||||
context 'with a local_only option set' do
|
||||
it 'does not include remote instances statuses' do
|
||||
local_account = Fabricate(:account, domain: nil)
|
||||
remote_account = Fabricate(:account, domain: 'test.com')
|
||||
local_status = Fabricate(:status, account: local_account)
|
||||
remote_status = Fabricate(:status, account: remote_account)
|
||||
|
||||
results = Status.as_public_timeline(nil, true)
|
||||
expect(results).to include(local_status)
|
||||
expect(results).not_to include(remote_status)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with an account passed in' do
|
||||
before do
|
||||
@account = Fabricate(:account)
|
||||
end
|
||||
|
||||
it 'excludes statuses from accounts blocked by the account' do
|
||||
blocked = Fabricate(:account)
|
||||
Fabricate(:block, account: @account, target_account: blocked)
|
||||
blocked_status = Fabricate(:status, account: blocked)
|
||||
|
||||
results = Status.as_public_timeline(@account)
|
||||
expect(results).not_to include(blocked_status)
|
||||
end
|
||||
|
||||
it 'excludes statuses from accounts who have blocked the account' do
|
||||
blocked = Fabricate(:account)
|
||||
Fabricate(:block, account: blocked, target_account: @account)
|
||||
blocked_status = Fabricate(:status, account: blocked)
|
||||
|
||||
results = Status.as_public_timeline(@account)
|
||||
expect(results).not_to include(blocked_status)
|
||||
end
|
||||
|
||||
it 'excludes statuses from accounts muted by the account' do
|
||||
muted = Fabricate(:account)
|
||||
Fabricate(:mute, account: @account, target_account: muted)
|
||||
muted_status = Fabricate(:status, account: muted)
|
||||
|
||||
results = Status.as_public_timeline(@account)
|
||||
expect(results).not_to include(muted_status)
|
||||
end
|
||||
|
||||
context 'where that account is silenced' do
|
||||
it 'includes statuses from other accounts that are silenced' do
|
||||
@account.update(silenced: true)
|
||||
other_silenced_account = Fabricate(:account, silenced: true)
|
||||
other_status = Fabricate(:status, account: other_silenced_account)
|
||||
|
||||
results = Status.as_public_timeline(@account)
|
||||
expect(results).to include(other_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.as_tag_timeline' do
|
||||
it 'includes statuses with a tag' do
|
||||
tag = Fabricate(:tag)
|
||||
status = Fabricate(:status, tags: [tag])
|
||||
other = Fabricate(:status)
|
||||
|
||||
results = Status.as_tag_timeline(tag)
|
||||
expect(results).to include(status)
|
||||
expect(results).not_to include(other)
|
||||
end
|
||||
|
||||
it 'allows replies to be included' do
|
||||
original = Fabricate(:status)
|
||||
tag = Fabricate(:tag)
|
||||
status = Fabricate(:status, tags: [tag], in_reply_to_id: original.id)
|
||||
|
||||
results = Status.as_tag_timeline(tag)
|
||||
expect(results).to include(status)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -12,43 +12,82 @@ RSpec.describe ProcessFeedService do
|
||||
stub_request(:get, "http://kickass.zone/system/accounts/avatars/000/000/001/large/eris.png").to_return(request_fixture('avatar.txt'))
|
||||
stub_request(:get, "http://kickass.zone/system/media_attachments/files/000/000/002/original/morpheus_linux.jpg?1476059910").to_return(request_fixture('attachment1.txt'))
|
||||
stub_request(:get, "http://kickass.zone/system/media_attachments/files/000/000/003/original/gizmo.jpg?1476060065").to_return(request_fixture('attachment2.txt'))
|
||||
|
||||
subject.call(body, account)
|
||||
end
|
||||
|
||||
it 'updates remote user\'s account information' do
|
||||
account.reload
|
||||
expect(account.display_name).to eq '::1'
|
||||
expect(account).to have_attached_file(:avatar)
|
||||
context 'when domain does not reject media' do
|
||||
before do
|
||||
subject.call(body, account)
|
||||
end
|
||||
|
||||
it 'updates remote user\'s account information' do
|
||||
account.reload
|
||||
expect(account.display_name).to eq '::1'
|
||||
expect(account).to have_attached_file(:avatar)
|
||||
expect(account.avatar_file_name).not_to be_nil
|
||||
end
|
||||
|
||||
it 'creates posts' do
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=1:objectType=Status')).to_not be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Status')).to_not be_nil
|
||||
end
|
||||
|
||||
it 'ignores delete statuses unless they existed before' do
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=3:objectType=Status')).to be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=12:objectType=Status')).to be_nil
|
||||
end
|
||||
|
||||
it 'does not create statuses for follows' do
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=1:objectType=Follow')).to be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Follow')).to be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=4:objectType=Follow')).to be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=7:objectType=Follow')).to be_nil
|
||||
end
|
||||
|
||||
it 'does not create statuses for favourites' do
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Favourite')).to be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=3:objectType=Favourite')).to be_nil
|
||||
end
|
||||
|
||||
it 'creates posts with media' do
|
||||
status = Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=14:objectType=Status')
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.media_attachments.first).to have_attached_file(:file)
|
||||
expect(status.media_attachments.first.image?).to be true
|
||||
expect(status.media_attachments.first.file_file_name).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it 'creates posts' do
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=1:objectType=Status')).to_not be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Status')).to_not be_nil
|
||||
end
|
||||
context 'when domain is set to reject media' do
|
||||
let!(:domain_block) { Fabricate(:domain_block, domain: 'kickass.zone', reject_media: true) }
|
||||
|
||||
it 'ignores delete statuses unless they existed before' do
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=3:objectType=Status')).to be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=12:objectType=Status')).to be_nil
|
||||
end
|
||||
before do
|
||||
subject.call(body, account)
|
||||
end
|
||||
|
||||
it 'does not create statuses for follows' do
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=1:objectType=Follow')).to be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Follow')).to be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=4:objectType=Follow')).to be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=7:objectType=Follow')).to be_nil
|
||||
end
|
||||
it 'updates remote user\'s account information' do
|
||||
account.reload
|
||||
expect(account.display_name).to eq '::1'
|
||||
end
|
||||
|
||||
it 'does not create statuses for favourites' do
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Favourite')).to be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=3:objectType=Favourite')).to be_nil
|
||||
end
|
||||
it 'rejects remote user\'s avatar' do
|
||||
account.reload
|
||||
expect(account.display_name).to eq '::1'
|
||||
expect(account.avatar_file_name).to be_nil
|
||||
end
|
||||
|
||||
it 'creates posts with media' do
|
||||
status = Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=14:objectType=Status')
|
||||
it 'creates posts' do
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=1:objectType=Status')).to_not be_nil
|
||||
expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Status')).to_not be_nil
|
||||
end
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.media_attachments.first).to have_attached_file(:file)
|
||||
it 'creates posts with remote-only media' do
|
||||
status = Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=14:objectType=Status')
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.media_attachments.first.file_file_name).to be_nil
|
||||
expect(status.media_attachments.first.unknown?).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@@ -56,4 +56,29 @@ RSpec.describe UpdateRemoteProfileService do
|
||||
expect(remote_account.reload.note).to eq 'Software engineer, free time musician and DIGITAL SPORTS enthusiast. Likes cats. Warning: May contain memes'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with updated details from a domain set to reject media' do
|
||||
let(:remote_account) { Fabricate(:account, username: 'bob', domain: 'example.com') }
|
||||
let!(:domain_block) { Fabricate(:domain_block, domain: 'example.com', reject_media: true) }
|
||||
|
||||
before do
|
||||
subject.call(xml, remote_account)
|
||||
end
|
||||
|
||||
it 'does not the avatar remote url' do
|
||||
expect(remote_account.reload.avatar_remote_url).to be_nil
|
||||
end
|
||||
|
||||
it 'sets display name' do
|
||||
expect(remote_account.reload.display_name).to eq 'DIGITAL CAT'
|
||||
end
|
||||
|
||||
it 'sets note' do
|
||||
expect(remote_account.reload.note).to eq 'Software engineer, free time musician and DIGITAL SPORTS enthusiast. Likes cats. Warning: May contain memes'
|
||||
end
|
||||
|
||||
it 'does not set store the avatar' do
|
||||
expect(remote_account.reload.avatar_file_name).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -3,19 +3,36 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'about/_links.html.haml' do
|
||||
it 'does not show sign in link when signed in' do
|
||||
allow(view).to receive(:user_signed_in?).and_return(true)
|
||||
render
|
||||
context 'when signed in' do
|
||||
before do
|
||||
allow(view).to receive(:user_signed_in?).and_return(true)
|
||||
end
|
||||
|
||||
expect(rendered).to have_content(I18n.t('about.get_started'))
|
||||
expect(rendered).not_to have_content(I18n.t('auth.login'))
|
||||
it 'does not show sign in link' do
|
||||
render 'about/links', instance: InstancePresenter.new
|
||||
|
||||
expect(rendered).to have_content(I18n.t('about.get_started'))
|
||||
expect(rendered).not_to have_content(I18n.t('auth.login'))
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows sign in link when signed out' do
|
||||
allow(view).to receive(:user_signed_in?).and_return(false)
|
||||
render
|
||||
context 'when signed out' do
|
||||
before do
|
||||
allow(view).to receive(:user_signed_in?).and_return(false)
|
||||
end
|
||||
|
||||
expect(rendered).to have_content(I18n.t('about.get_started'))
|
||||
expect(rendered).to have_content(I18n.t('auth.login'))
|
||||
it 'shows get started link when registrations are allowed' do
|
||||
render 'about/links', instance: double(open_registrations: true)
|
||||
|
||||
expect(rendered).to have_content(I18n.t('about.get_started'))
|
||||
expect(rendered).to have_content(I18n.t('auth.login'))
|
||||
end
|
||||
|
||||
it 'hides get started link when registrations are closed' do
|
||||
render 'about/links', instance: double(open_registrations: false)
|
||||
|
||||
expect(rendered).not_to have_content(I18n.t('about.get_started'))
|
||||
expect(rendered).to have_content(I18n.t('auth.login'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -328,7 +328,7 @@ if (cluster.isMaster) {
|
||||
|
||||
server.listen(process.env.PORT || 4000, () => {
|
||||
log.level = process.env.LOG_LEVEL || 'verbose'
|
||||
log.info(`Starting streaming API server worker on ${server.address()}`)
|
||||
log.info(`Starting streaming API server worker on ${server.address().address}:${server.address().port}`)
|
||||
})
|
||||
|
||||
process.on('SIGINT', exit)
|
||||
|
Reference in New Issue
Block a user