Compare commits

...

31 Commits
v1.3 ... v1.3.2

Author SHA1 Message Date
Eugen Rochko
01e011bc90 Bump version to 1.3.2 (#2623) 2017-04-29 19:26:32 +02:00
Stephen Burgess
e3b60b07d9 fix(video): Position of play icon (#2608)
* fix(video): Position of play icon

#2601

* fix(overlay-button): Positioning

* fix(expand): Bottom align expand icon

* feat(video): Fit landscape videos into preview area
2017-04-29 15:23:27 +02:00
YOU
d0665726ca Update index.js (#2612) 2017-04-29 14:35:16 +02:00
alpaca-tc
96c84da1d4 Expand uploaded thumbnail (#2611) 2017-04-29 14:29:13 +02:00
alpaca-tc
7d36a76180 Remove uneeded ORDER BY query (#2615) 2017-04-29 10:50:10 +02:00
Patrick Figel
197af5de70 Set unknown attachment type when adding domain blocks (#2605)
Follow-up to #2599. When a domain block with `reject_media` is
added or `rake mastodon:media:remove_remote` is invoked, mastodon
deletes the locally cached attachments and avatars but does not
reflect that change in the database, causing the `file` fields to
still have values. This change persists the deletion in the
database and sets the attachment type to unknown.

This also introduces a one-off rake task that sets all attachments
without a local file to the "unknown" type. The upgrade notes for
the next release should contain a post-upgrade step with
`rake mastodon:media:set_unknown`.
2017-04-29 02:44:03 +02:00
Joël Quenneville
27301312a6 Add tests to the Feed model (#2594)
This adds a test for the `Feed#get` method. While the data
transformations in `Feed#get` may seem redundant, they are important to
maintain the order from Redis. The tests I wrote will fail if someone
tries to refactor away this "redundancy" (as I tried to do in the first
iteration of this change).
2017-04-29 00:21:35 +02:00
Patrick Figel
8ac7fca5d0 Set correct attachment type for rejected media (#2599)
In #2110, a new attachment type "unknown" was introduced for
attachments that were rejected due to a domain being blocked using
reject_media. However, the "type" field was never set to "unknown"
because a default value of "0" (image) is set for that column,
causing the `type.blank?` expression to always equal false.

This version uses type_changed? instead, causing the type to be set
to "unknown" unless a type has been explicitly set. This introduces
a small change in behaviour causing the type to be set to unknown
before paperclip calls `before_post_process`. Presumably this
behaviour is more appropriate than the current one because the
attachment type has not been determined by that point.

Included are new tests for `ProcessFeedService` and
`UpdateRemoteProfileService` which now check that remote media is
downloaded for non-blocked domains and is rejected for others.
2017-04-29 00:18:32 +02:00
Matt Jankowski
a823509b99 Fix broken view spec for about/links (#2591) 2017-04-28 17:54:03 +02:00
Yamagishi Kazutoshi
298d28af51 Fix API for Mastodon version (#2590)
Fix #2589
2017-04-28 17:53:37 +02:00
Mingye Wang
439b2dceda Minor updates to zh-CN JS translation (#2557)
* Minor updates to zh-CN JS translation

* removed "!" from "toot" per #2549 (it's a privacy indicator)
* ellipsis work (from ... to some U+2026 horizontal ellipsis unicode stuff)

* Spillcheck for zh-CN js translations (squash this)

部 [份→分]
2017-04-28 16:45:41 +02:00
Matt Jankowski
9262f6968b Fix broken spec for about/links view partial (#2586) 2017-04-28 16:45:24 +02:00
Yamagishi Kazutoshi
71e73e36cd Fix spec for hide get started (#2585) 2017-04-28 16:44:57 +02:00
Yamagishi Kazutoshi
01c206326f Hide get started link when register closed (#2583) 2017-04-28 15:45:37 +02:00
Matt Jankowski
9566893cc9 More controller specs (#2561)
* Add render_views in more places

* Delegate methods from account to user with allow nil true, so that admin accounts show view renders when missing a user

* Use actual account instances in authorize follow controller spec
2017-04-28 15:12:37 +02:00
Wonderfall
0e2589867f Update en locale federation description (#2558) 2017-04-28 15:11:54 +02:00
Ratmir Karabut
4acc386dd5 Update Russian translation (pluralizations) (#2565)
* Add Russian translation (ru)

* Fix a missing comma

* Fix the wording for better consistency

* Update Russian translation

* Arrange Russian setting alphabetically

* Fix syntax error

* Update Russian translation

* Fix formatting error

* Update Russian translation

* Update Russian translation

* Update ru.jsx

* Fix syntax error

* Remove two_factor_auth.warning (appears obsolete)

* Add missing strings in ru.yml

A lot of new strings translated, especially for the newly added admin section

* Fix translation consistency

* Update Russian translation

* Update Russian translation (pluralizations)
2017-04-28 15:11:36 +02:00
Matt Jankowski
429480bb77 Return missing page when tag does not exist (#2563) 2017-04-28 15:11:21 +02:00
Matt Jankowski
61067dc2e6 Gem version updates (#2566)
* Update autoprefixer-rails to version 6.7.7.2

* Update aws-sdk to version 2.9.12

* Update http to version 2.2.2

* Update mail to version 2.6.5

* Update pghero to version 1.6.5

* Update ox to version 2.4.13

* Update json to version 2.1.0

* Update capistrano-rbenv to version 2.1.1

* Update httplog to version 0.99.3

* Update rainbow to version 2.2.2

* Update pkg-config to version 1.2.0

* Update globalid to version 0.4.0

* Update redis-rack to version 2.0.2

* Update webmock to version 3.0.1

* Update oj to version 3.0.2
2017-04-28 15:10:58 +02:00
Matt Jankowski
effb08edbb More status specs (#2564)
* Add rough outline of coverage needed for public timeline

* Specs for visibility, replies, boosts

* Specs for silenced account

* Specs for local_only option

* Specs for blocks and mutes

* Add tentative spec around including other silenced account statuses

* Add with_public_visibility scope

* Add simple coverage for tag_timeline

* Tag timeline includes replies

* Replace tag.statuses with a tagged_with scope in tag timeline method

* Use with_public_visibility in tag timeline

* Extract common scope between public and tag timelines to method

* Extract local domain check to local_only scope

* Extract local_only check to starting scope method

* Move list of excluded from timeline account ids to account model

* Simplify excluded accounts list on account model

* Only join accounts when needed

* Rename method for account specific filtering

* Extract method for account exclusions

* Fix bug where silenced accounts were not including statuses from other silenced accounts

* DRY up filter application from account or no account

* timeline_scope can be private

* Add spec showing that account can find its excluded accounts ids

* Add spec which fails if local_only does not have a left outer join

* rubocop
2017-04-28 15:10:41 +02:00
Ratmir Karabut
d1b4ebe07d Update Russian translation (#2570)
New column subheadings
2017-04-28 09:08:23 -04:00
Ash Furrow
5eef9dab80 Update version to 1.3.1 (#2571) 2017-04-28 09:07:48 -04:00
y-temp4
2ca246d7d1 Use the new Ruby 1.9 hash syntax (#2577) 2017-04-28 09:07:16 -04:00
ButterflyOfFire
9a085e138e Update settings_helper.rb (#2556)
Correcting language name arabic = العربية
2017-04-28 01:59:19 +02:00
Wonderfall
546b5a9dcf Update onboard federation description (#2555) 2017-04-28 01:58:45 +02:00
Matt Jankowski
a39e719b39 Add render_views to admin/reports controller spec (#2548) 2017-04-28 01:21:38 +02:00
Shel Raphen
f51b2cb2e7 Copy edits to reflect changes in post privacy (#2552)
There was still a reference to "private posts" and the phrasing of unlisted in contrast to the others now made it seem strangely more private.
2017-04-28 01:21:26 +02:00
Matt Jankowski
9736753985 Fix remote follow (#2547)
* Add coverage for remote_follow#new

* Fix reference to authorize_follows/card partial
2017-04-28 00:16:44 +02:00
Ratmir Karabut
ea783d3632 Fix typo and wording in follower auth settings (#2544) 2017-04-28 00:16:31 +02:00
Patrick Figel
074e9612a2 fix partial path in admin/reports (#2546)
In #2505, the authorize_follow views were renamed to
authorize_follows. This change was not applied in the show view
of admin/reports, which causes a 500 when reports are viewed.
2017-04-28 00:09:25 +02:00
Eugen Rochko
7406404fa3 Hotfix remote status formatting (#2543) 2017-04-27 23:37:22 +02:00
60 changed files with 489 additions and 131 deletions

View File

@@ -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'

View File

@@ -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

View File

@@ -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>
}

View File

@@ -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>

View File

@@ -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",

View File

@@ -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": "Нравится",

View File

@@ -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": "被屏蔽的用户",

View File

@@ -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%;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -3,7 +3,7 @@
module SettingsHelper
HUMAN_LOCALES = {
en: 'English',
ar: 'عربى',
ar: 'العربية',
bg: 'Български',
de: 'Deutsch',
eo: 'Esperanto',

View File

@@ -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)

View File

@@ -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)

View File

@@ -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?

View File

@@ -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

View File

@@ -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

View File

@@ -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'

View File

@@ -29,5 +29,5 @@
.sidebar
= render 'contact', contact: @instance_presenter
= render 'links'
= render 'links', instance: @instance_presenter
= render 'version', version: @instance_presenter

View File

@@ -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?

View File

@@ -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

View File

@@ -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 }

View File

@@ -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

View File

@@ -58,4 +58,6 @@ ru:
not_locked: не был заблокирован
not_saved:
one: '1 ошибка помешала сохранению этого %{resource}:'
other: "%{count} ошибки помешали сохранению этого %{resource}:"
few: "%{count} ошибки помешали сохранению этого %{resource}:"
many: "%{count} ошибок помешали сохранению этого %{resource}:"
other: "%{count} ошибок помешали сохранению этого %{resource}:"

View File

@@ -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}:'

View File

@@ -13,7 +13,7 @@ module Mastodon
end
def patch
0
2
end
def pre

View File

@@ -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

View File

@@ -2,6 +2,7 @@ require 'rails_helper'
describe AccountFollowController do
render_views
let(:user) { Fabricate(:user) }
let(:alice) { Fabricate(:account, username: 'alice') }

View File

@@ -2,6 +2,7 @@ require 'rails_helper'
describe AccountUnfollowController do
render_views
let(:user) { Fabricate(:user) }
let(:alice) { Fabricate(:account, username: 'alice') }

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -2,6 +2,7 @@ require 'rails_helper'
describe FollowerAccountsController do
render_views
let(:alice) { Fabricate(:account, username: 'alice') }
describe 'GET #index' do

View File

@@ -2,6 +2,7 @@ require 'rails_helper'
describe FollowingAccountsController do
render_views
let(:alice) { Fabricate(:account, username: 'alice') }
describe 'GET #index' do

View File

@@ -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)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Oauth::AuthorizationsController, type: :controller do

View 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

View File

@@ -1,6 +1,8 @@
require 'rails_helper'
describe Settings::Exports::BlockedAccountsController do
render_views
before do
sign_in Fabricate(:user), scope: :user
end

View File

@@ -1,6 +1,8 @@
require 'rails_helper'
describe Settings::Exports::FollowingAccountsController do
render_views
before do
sign_in Fabricate(:user), scope: :user
end

View File

@@ -1,6 +1,8 @@
require 'rails_helper'
describe Settings::Exports::MutedAccountsController do
render_views
before do
sign_in Fabricate(:user), scope: :user
end

View File

@@ -1,6 +1,8 @@
require 'rails_helper'
describe Settings::FollowerDomainsController do
render_views
let(:user) { Fabricate(:user) }
before do

View File

@@ -1,6 +1,7 @@
require 'rails_helper'
RSpec.describe Settings::ImportsController, type: :controller do
render_views
before do
sign_in Fabricate(:user), scope: :user

View File

@@ -1,6 +1,8 @@
require 'rails_helper'
describe Settings::PreferencesController do
render_views
let(:user) { Fabricate(:user) }
before do

View File

@@ -1,6 +1,7 @@
require 'rails_helper'
RSpec.describe Settings::ProfilesController, type: :controller do
render_views
before do
sign_in Fabricate(:user), scope: :user

View File

@@ -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

View File

@@ -1,3 +1,4 @@
Fabricator(:mute) do
account
target_account { Fabricate(:account) }
end

View File

@@ -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(

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -56,4 +56,29 @@ RSpec.describe UpdateRemoteProfileService do
expect(remote_account.reload.note).to eq 'Software engineer, free time musician and 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 ' '
end
it 'sets note' do
expect(remote_account.reload.note).to eq 'Software engineer, free time musician and 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

View File

@@ -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

View File

@@ -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)