mirror of
https://framagit.org/tykayn/mastodon.git
synced 2023-08-25 08:33:12 +02:00
Merge branch 'master' into master
This commit is contained in:
commit
bf7cefa516
2
.buildpacks
Normal file
2
.buildpacks
Normal file
@ -0,0 +1,2 @@
|
||||
https://github.com/Scalingo/nodejs-buildpack
|
||||
https://github.com/Scalingo/ruby-buildpack
|
@ -22,6 +22,8 @@ OTP_SECRET=
|
||||
# SINGLE_USER_MODE=true
|
||||
# Prevent registrations with following e-mail domains
|
||||
# EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc
|
||||
# Only allow registrations with the following e-mail domains
|
||||
# EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc
|
||||
|
||||
# E-mail configuration
|
||||
SMTP_SERVER=smtp.mailgun.org
|
||||
|
2
.slugignore
Normal file
2
.slugignore
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
.cache/
|
8
Gemfile
8
Gemfile
@ -8,8 +8,6 @@ gem 'sass-rails', '~> 5.0'
|
||||
gem 'uglifier', '>= 1.3.0'
|
||||
gem 'coffee-rails', '~> 4.1.0'
|
||||
gem 'jquery-rails'
|
||||
gem 'jbuilder', '~> 2.0'
|
||||
gem 'sdoc', '~> 0.4.0', group: :doc
|
||||
gem 'puma'
|
||||
|
||||
gem 'hamlit-rails'
|
||||
@ -38,7 +36,7 @@ gem 'rqrcode'
|
||||
gem 'twitter-text'
|
||||
gem 'oj'
|
||||
gem 'hiredis'
|
||||
gem 'redis', '~>3.2'
|
||||
gem 'redis', '~>3.2', require: ['redis', 'redis/connection/hiredis']
|
||||
gem 'fast_blank'
|
||||
gem 'htmlentities'
|
||||
gem 'simple_form'
|
||||
@ -46,6 +44,7 @@ gem 'will_paginate'
|
||||
gem 'rack-attack'
|
||||
gem 'rack-cors', require: 'rack/cors'
|
||||
gem 'sidekiq'
|
||||
gem 'sidekiq-unique-jobs'
|
||||
gem 'rails-settings-cached'
|
||||
gem 'simple-navigation'
|
||||
gem 'statsd-instrument'
|
||||
@ -66,9 +65,10 @@ group :development, :test do
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'faker'
|
||||
gem 'rspec-sidekiq'
|
||||
gem 'simplecov', require: false
|
||||
gem 'webmock'
|
||||
gem 'rspec-sidekiq'
|
||||
end
|
||||
|
||||
group :development do
|
||||
|
18
Gemfile.lock
18
Gemfile.lock
@ -149,6 +149,8 @@ GEM
|
||||
erubis (2.7.0)
|
||||
execjs (2.7.0)
|
||||
fabrication (2.15.2)
|
||||
faker (1.6.6)
|
||||
i18n (~> 0.5)
|
||||
fast_blank (1.0.0)
|
||||
font-awesome-rails (4.6.3.1)
|
||||
railties (>= 3.2, < 5.1)
|
||||
@ -196,9 +198,6 @@ GEM
|
||||
parser (>= 2.2.3.0)
|
||||
term-ansicolor (>= 1.3.2)
|
||||
terminal-table (>= 1.5.1)
|
||||
jbuilder (2.6.0)
|
||||
activesupport (>= 3.0.0, < 5.1)
|
||||
multi_json (~> 1.2)
|
||||
jmespath (1.3.1)
|
||||
jquery-rails (4.1.1)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
@ -229,7 +228,6 @@ GEM
|
||||
mimemagic (0.3.2)
|
||||
mini_portile2 (2.1.0)
|
||||
minitest (5.10.1)
|
||||
multi_json (1.12.1)
|
||||
net-scp (1.2.1)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (4.0.1)
|
||||
@ -308,8 +306,6 @@ GEM
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rainbow (2.1.0)
|
||||
rake (12.0.0)
|
||||
rdoc (4.2.2)
|
||||
json (~> 1.4)
|
||||
react-rails (1.10.0)
|
||||
babel-transpiler (>= 0.7.0)
|
||||
coffee-script-source (~> 1.8)
|
||||
@ -379,14 +375,14 @@ GEM
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
tilt (>= 1.1, < 3)
|
||||
sdoc (0.4.1)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
rdoc (~> 4.0)
|
||||
sidekiq (4.2.7)
|
||||
concurrent-ruby (~> 1.0)
|
||||
connection_pool (~> 2.2, >= 2.2.0)
|
||||
rack-protection (>= 1.5.0)
|
||||
redis (~> 3.2, >= 3.2.1)
|
||||
sidekiq-unique-jobs (4.0.18)
|
||||
sidekiq (>= 2.6)
|
||||
thor
|
||||
simple-navigation (4.0.3)
|
||||
activesupport (>= 2.3.2)
|
||||
simple_form (3.2.1)
|
||||
@ -467,6 +463,7 @@ DEPENDENCIES
|
||||
doorkeeper
|
||||
dotenv-rails
|
||||
fabrication
|
||||
faker
|
||||
fast_blank
|
||||
font-awesome-rails
|
||||
fuubar
|
||||
@ -477,7 +474,6 @@ DEPENDENCIES
|
||||
http
|
||||
httplog
|
||||
i18n-tasks (~> 0.9.6)
|
||||
jbuilder (~> 2.0)
|
||||
jquery-rails
|
||||
letter_opener
|
||||
letter_opener_web
|
||||
@ -508,8 +504,8 @@ DEPENDENCIES
|
||||
rubocop
|
||||
ruby-oembed
|
||||
sass-rails (~> 5.0)
|
||||
sdoc (~> 0.4.0)
|
||||
sidekiq
|
||||
sidekiq-unique-jobs
|
||||
simple-navigation
|
||||
simple_form
|
||||
simplecov
|
||||
|
5
ISSUE_TEMPLATE.md
Normal file
5
ISSUE_TEMPLATE.md
Normal file
@ -0,0 +1,5 @@
|
||||
[Issue text goes here].
|
||||
|
||||
* * * *
|
||||
|
||||
- [ ] I searched or browsed the repo’s other issues to ensure this is not a duplicate.
|
@ -117,6 +117,12 @@ Which will re-create the updated containers, leaving databases and data as is. D
|
||||
|
||||
Docker is great for quickly trying out software, but it has its drawbacks too. If you prefer to run Mastodon without using Docker, refer to the [production guide](docs/Running-Mastodon/Production-guide.md) for examples, configuration and instructions.
|
||||
|
||||
## Deployment on Scalingo
|
||||
|
||||
[![Deploy on Scalingo](https://cdn.scalingo.com/deploy/button.svg)](https://my.scalingo.com/deploy?source=https://github.com/tootsuite/mastodon#master)
|
||||
|
||||
[You can view a guide for deployment on Scalingo here.](docs/Running-Mastodon/Scalingo-guide.md)
|
||||
|
||||
## Deployment on Heroku (experimental)
|
||||
|
||||
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 59 KiB |
@ -46,6 +46,7 @@ import fr from 'react-intl/locale-data/fr';
|
||||
import pt from 'react-intl/locale-data/pt';
|
||||
import hu from 'react-intl/locale-data/hu';
|
||||
import uk from 'react-intl/locale-data/uk';
|
||||
import fi from 'react-intl/locale-data/fi';
|
||||
import eo from 'react-intl/locale-data/eo';
|
||||
import getMessagesForLocale from '../locales';
|
||||
import { hydrateStore } from '../actions/store';
|
||||
@ -59,7 +60,7 @@ const browserHistory = useRouterHistory(createBrowserHistory)({
|
||||
basename: '/web'
|
||||
});
|
||||
|
||||
addLocaleData([...en, ...de, ...es, ...fr, ...pt, ...hu, ...uk, ...eo]);
|
||||
addLocaleData([...en, ...de, ...es, ...fr, ...pt, ...hu, ...uk, ...fi, ...eo]);
|
||||
|
||||
const Mastodon = React.createClass({
|
||||
|
||||
|
@ -43,7 +43,7 @@ const GettingStarted = ({ intl, me }) => {
|
||||
|
||||
<div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<div className='static-content getting-started'>
|
||||
<p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on github at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/mastodon/blob/master/docs/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
|
||||
<p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/mastodon/blob/master/docs/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
|
||||
</div>
|
||||
</div>
|
||||
</Column>
|
||||
|
@ -9,7 +9,7 @@ const iconStyle = {
|
||||
};
|
||||
|
||||
const ClearColumnButton = ({ onClick }) => (
|
||||
<div className='column-icon' style={iconStyle} onClick={onClick}>
|
||||
<div className='column-icon' tabindex='0' style={iconStyle} onClick={onClick}>
|
||||
<i className='fa fa-trash' />
|
||||
</div>
|
||||
);
|
||||
|
@ -25,7 +25,7 @@ const en = {
|
||||
"getting_started.heading": "Getting started",
|
||||
"getting_started.about_addressing": "You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the search form.",
|
||||
"getting_started.about_shortcuts": "If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.",
|
||||
"getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on github at {github}. {apps}.",
|
||||
"getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.",
|
||||
"column.home": "Home",
|
||||
"column.community": "Local timeline",
|
||||
"column.public": "Federated timeline",
|
||||
|
68
app/assets/javascripts/components/locales/fi.jsx
Normal file
68
app/assets/javascripts/components/locales/fi.jsx
Normal file
@ -0,0 +1,68 @@
|
||||
const fi = {
|
||||
"column_back_button.label": "Takaisin",
|
||||
"lightbox.close": "Sulje",
|
||||
"loading_indicator.label": "Ladataan...",
|
||||
"status.mention": "Mainitse @{name}",
|
||||
"status.delete": "Poista",
|
||||
"status.reply": "Vastaa",
|
||||
"status.reblog": "Buustaa",
|
||||
"status.favourite": "Tykkää",
|
||||
"status.reblogged_by": "{name} buustasi",
|
||||
"status.sensitive_warning": "Arkaluontoista sisältöä",
|
||||
"status.sensitive_toggle": "Klikkaa nähdäksesi",
|
||||
"video_player.toggle_sound": "Äänet päälle/pois",
|
||||
"account.mention": "Mainitse @{name}",
|
||||
"account.edit_profile": "Muokkaa",
|
||||
"account.unblock": "Salli @{name}",
|
||||
"account.unfollow": "Lopeta seuraaminen",
|
||||
"account.block": "Estä @{name}",
|
||||
"account.follow": "Seuraa",
|
||||
"account.posts": "Postit",
|
||||
"account.follows": "Seuraa",
|
||||
"account.followers": "Seuraajia",
|
||||
"account.follows_you": "Seuraa sinua",
|
||||
"account.requested": "Odottaa hyväksyntää",
|
||||
"getting_started.heading": "Aloitus",
|
||||
"getting_started.about_addressing": "Voit seurata ihmisiä jos tiedät heidän käyttäjänimensä ja domainin missä he ovat syöttämällä e-mail-esque osoitteen Etsi kenttään.",
|
||||
"getting_started.about_shortcuts": "Jos etsimäsi henkilö on samassa domainissa kuin sinä, pelkkä käyttäjänimi kelpaa. Sama pätee kun mainitset ihmisiä statuksessasi",
|
||||
"getting_started.open_source_notice": "Mastodon Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHub palvelussa {github}. {apps}.",
|
||||
"column.home": "Koti",
|
||||
"column.community": "Paikallinen aikajana",
|
||||
"column.public": "Yleinen aikajana",
|
||||
"column.notifications": "Ilmoitukset",
|
||||
"tabs_bar.compose": "Luo",
|
||||
"tabs_bar.home": "Koti",
|
||||
"tabs_bar.mentions": "Maininnat",
|
||||
"tabs_bar.public": "Yleinen aikajana",
|
||||
"tabs_bar.notifications": "Ilmoitukset",
|
||||
"compose_form.placeholder": "Mitä sinulla on mielessä?",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.sensitive": "Merkitse media herkäksi",
|
||||
"compose_form.spoiler": "Piiloita teksti varoituksen taakse",
|
||||
"compose_form.private": "Merkitse yksityiseksi",
|
||||
"compose_form.privacy_disclaimer": "Sinun yksityinen status toimitetaan mainitsemallesi käyttäjille domaineissa {domains}. Luotatko {domainsCount, plural, one {tähän palvelimeen} other {näihin palvelimiin}}? Postauksen yksityisyys toimii van Mastodon palvelimilla. Jos {domains} {domainsCount, plural, one {ei ole Mastodon palvelin} other {eivät ole Mastodon palvelin}}, viestiin ei tule Yksityinen-merkintää, ja sitä voidaan boostata tai muuten tehdä näkyväksi muille vastaanottajille.",
|
||||
"compose_form.unlisted": "Älä näytä yleisillä aikajanoilla",
|
||||
"navigation_bar.edit_profile": "Muokkaa profiilia",
|
||||
"navigation_bar.preferences": "Ominaisuudet",
|
||||
"navigation_bar.community_timeline": "Paikallinen aikajana",
|
||||
"navigation_bar.public_timeline": "Yleinen aikajana",
|
||||
"navigation_bar.logout": "Kirjaudu ulos",
|
||||
"reply_indicator.cancel": "Peruuta",
|
||||
"search.placeholder": "Hae",
|
||||
"search.account": "Tili",
|
||||
"search.hashtag": "Hashtag",
|
||||
"upload_button.label": "Lisää mediaa",
|
||||
"upload_form.undo": "Peru",
|
||||
"notification.follow": "{name} seurasi sinua",
|
||||
"notification.favourite": "{name} tykkäsi statuksestasi",
|
||||
"notification.reblog": "{name} buustasi statustasi",
|
||||
"notification.mention": "{name} mainitsi sinut",
|
||||
"notifications.column_settings.alert": "Työpöytä ilmoitukset",
|
||||
"notifications.column_settings.show": "Näytä sarakkeessa",
|
||||
"notifications.column_settings.follow": "Uusia seuraajia:",
|
||||
"notifications.column_settings.favourite": "Tykkäyksiä:",
|
||||
"notifications.column_settings.mention": "Mainintoja:",
|
||||
"notifications.column_settings.reblog": "Buusteja:",
|
||||
};
|
||||
|
||||
export default fi;
|
@ -5,6 +5,7 @@ import hu from './hu';
|
||||
import fr from './fr';
|
||||
import pt from './pt';
|
||||
import uk from './uk';
|
||||
import fi from './fi';
|
||||
import eo from './eo';
|
||||
|
||||
const locales = {
|
||||
@ -15,6 +16,7 @@ const locales = {
|
||||
fr,
|
||||
pt,
|
||||
uk,
|
||||
fi,
|
||||
eo
|
||||
};
|
||||
|
||||
|
@ -319,7 +319,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.simple_form {
|
||||
.simple_form, .closed-registrations-message {
|
||||
width: 300px;
|
||||
flex: 0 0 auto;
|
||||
background: rgba(darken($color1, 7%), 0.5);
|
||||
@ -340,3 +340,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.closed-registrations-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -34,6 +34,7 @@
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
text-shadow: 0 0 2px $color8;
|
||||
|
||||
small {
|
||||
display: block;
|
||||
@ -128,6 +129,7 @@
|
||||
text-transform: uppercase;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
text-shadow: 0 0 2px $color8;
|
||||
}
|
||||
|
||||
.counter-number {
|
||||
@ -385,5 +387,6 @@
|
||||
.account__header__content {
|
||||
font-size: 14px;
|
||||
color: $color1;
|
||||
text-shadow: 0 0 2px $color8;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ class AboutController < ApplicationController
|
||||
before_action :set_body_classes
|
||||
|
||||
def index
|
||||
@description = Setting.site_description
|
||||
@description = Setting.site_description
|
||||
@open_registrations = Setting.open_registrations
|
||||
@closed_registrations_message = Setting.closed_registrations_message
|
||||
|
||||
@user = User.new
|
||||
@user.build_account
|
||||
|
@ -9,6 +9,24 @@ class Admin::DomainBlocksController < ApplicationController
|
||||
@blocks = DomainBlock.paginate(page: params[:page], per_page: 40)
|
||||
end
|
||||
|
||||
def new
|
||||
@domain_block = DomainBlock.new
|
||||
end
|
||||
|
||||
def create
|
||||
@domain_block = DomainBlock.new(resource_params)
|
||||
|
||||
if @domain_block.save
|
||||
DomainBlockWorker.perform_async(@domain_block.id)
|
||||
redirect_to admin_domain_blocks_path, notice: 'Domain block is now being processed'
|
||||
else
|
||||
render action: :new
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:domain_block).permit(:domain, :severity)
|
||||
end
|
||||
end
|
||||
|
@ -16,19 +16,19 @@ class Admin::ReportsController < ApplicationController
|
||||
end
|
||||
|
||||
def resolve
|
||||
@report.update(action_taken: true)
|
||||
@report.update(action_taken: true, action_taken_by_account_id: current_account.id)
|
||||
redirect_to admin_report_path(@report)
|
||||
end
|
||||
|
||||
def suspend
|
||||
Admin::SuspensionWorker.perform_async(@report.target_account.id)
|
||||
@report.update(action_taken: true)
|
||||
Report.unresolved.where(target_account: @report.target_account).update_all(action_taken: true, action_taken_by_account_id: current_account.id)
|
||||
redirect_to admin_report_path(@report)
|
||||
end
|
||||
|
||||
def silence
|
||||
@report.target_account.update(silenced: true)
|
||||
@report.update(action_taken: true)
|
||||
Report.unresolved.where(target_account: @report.target_account).update_all(action_taken: true, action_taken_by_account_id: current_account.id)
|
||||
redirect_to admin_report_path(@report)
|
||||
end
|
||||
|
||||
|
@ -11,9 +11,13 @@ class Admin::SettingsController < ApplicationController
|
||||
|
||||
def update
|
||||
@setting = Setting.where(var: params[:id]).first_or_initialize(var: params[:id])
|
||||
value = settings_params[:value]
|
||||
|
||||
if @setting.value != params[:setting][:value]
|
||||
@setting.value = params[:setting][:value]
|
||||
# Special cases
|
||||
value = value == 'true' if @setting.var == 'open_registrations'
|
||||
|
||||
if @setting.value != value
|
||||
@setting.value = value
|
||||
@setting.save
|
||||
end
|
||||
|
||||
@ -22,4 +26,10 @@ class Admin::SettingsController < ApplicationController
|
||||
format.json { respond_with_bip(@setting) }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def settings_params
|
||||
params.require(:setting).permit(:value)
|
||||
end
|
||||
end
|
||||
|
@ -4,6 +4,12 @@ class Api::V1::AppsController < ApiController
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
@app = Doorkeeper::Application.create!(name: params[:client_name], redirect_uri: params[:redirect_uris], scopes: (params[:scopes] || Doorkeeper.configuration.default_scopes), website: params[:website])
|
||||
@app = Doorkeeper::Application.create!(name: app_params[:client_name], redirect_uri: app_params[:redirect_uris], scopes: (app_params[:scopes] || Doorkeeper.configuration.default_scopes), website: app_params[:website])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def app_params
|
||||
params.permit(:client_name, :redirect_uris, :scopes, :website)
|
||||
end
|
||||
end
|
||||
|
@ -7,7 +7,7 @@ class Api::V1::FollowsController < ApiController
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
raise ActiveRecord::RecordNotFound if params[:uri].blank?
|
||||
raise ActiveRecord::RecordNotFound if follow_params[:uri].blank?
|
||||
|
||||
@account = FollowService.new.call(current_user.account, target_uri).try(:target_account)
|
||||
render action: :show
|
||||
@ -16,6 +16,10 @@ class Api::V1::FollowsController < ApiController
|
||||
private
|
||||
|
||||
def target_uri
|
||||
params[:uri].strip.gsub(/\A@/, '')
|
||||
follow_params[:uri].strip.gsub(/\A@/, '')
|
||||
end
|
||||
|
||||
def follow_params
|
||||
params.permit(:uri)
|
||||
end
|
||||
end
|
||||
|
@ -10,10 +10,16 @@ class Api::V1::MediaController < ApiController
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
@media = MediaAttachment.create!(account: current_user.account, file: params[:file])
|
||||
@media = MediaAttachment.create!(account: current_user.account, file: media_params[:file])
|
||||
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
|
||||
render json: { error: 'File type of uploaded media could not be verified' }, status: 422
|
||||
rescue Paperclip::Error
|
||||
render json: { error: 'Error processing thumbnail for uploaded media' }, status: 500
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def media_params
|
||||
params.permit(:file)
|
||||
end
|
||||
end
|
||||
|
@ -12,13 +12,19 @@ class Api::V1::ReportsController < ApiController
|
||||
end
|
||||
|
||||
def create
|
||||
status_ids = params[:status_ids].is_a?(Enumerable) ? params[:status_ids] : [params[:status_ids]]
|
||||
status_ids = report_params[:status_ids].is_a?(Enumerable) ? report_params[:status_ids] : [report_params[:status_ids]]
|
||||
|
||||
@report = Report.create!(account: current_account,
|
||||
target_account: Account.find(params[:account_id]),
|
||||
target_account: Account.find(report_params[:account_id]),
|
||||
status_ids: Status.find(status_ids).pluck(:id),
|
||||
comment: params[:comment])
|
||||
comment: report_params[:comment])
|
||||
|
||||
render :show
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def report_params
|
||||
params.permit(:account_id, :comment, status_ids: [])
|
||||
end
|
||||
end
|
||||
|
@ -62,11 +62,11 @@ class Api::V1::StatusesController < ApiController
|
||||
end
|
||||
|
||||
def create
|
||||
@status = PostStatusService.new.call(current_user.account, params[:status], params[:in_reply_to_id].blank? ? nil : Status.find(params[:in_reply_to_id]), media_ids: params[:media_ids],
|
||||
sensitive: params[:sensitive],
|
||||
spoiler_text: params[:spoiler_text],
|
||||
visibility: params[:visibility],
|
||||
application: doorkeeper_token.application)
|
||||
@status = PostStatusService.new.call(current_user.account, status_params[:status], status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]), media_ids: status_params[:media_ids],
|
||||
sensitive: status_params[:sensitive],
|
||||
spoiler_text: status_params[:spoiler_text],
|
||||
visibility: status_params[:visibility],
|
||||
application: doorkeeper_token.application)
|
||||
render action: :show
|
||||
end
|
||||
|
||||
@ -111,4 +111,8 @@ class Api::V1::StatusesController < ApiController
|
||||
@status = Status.find(params[:id])
|
||||
raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account)
|
||||
end
|
||||
|
||||
def status_params
|
||||
params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, media_ids: [])
|
||||
end
|
||||
end
|
||||
|
@ -39,7 +39,14 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
def set_user_activity
|
||||
current_user.touch(:current_sign_in_at) if !current_user.nil? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < 24.hours.ago)
|
||||
return unless !current_user.nil? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < 24.hours.ago)
|
||||
|
||||
# Mark user as signed-in today
|
||||
current_user.update_tracked_fields(request)
|
||||
|
||||
# If the sign in is after a two week break, we need to regenerate their feed
|
||||
RegenerationWorker.perform_async(current_user.account_id) if current_user.last_sign_in_at < 14.days.ago
|
||||
return
|
||||
end
|
||||
|
||||
def check_suspension
|
||||
|
@ -3,7 +3,7 @@
|
||||
class Auth::RegistrationsController < Devise::RegistrationsController
|
||||
layout :determine_layout
|
||||
|
||||
before_action :check_single_user_mode
|
||||
before_action :check_enabled_registrations, only: [:new, :create]
|
||||
before_action :configure_sign_up_params, only: [:create]
|
||||
|
||||
protected
|
||||
@ -27,12 +27,12 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
||||
new_user_session_path
|
||||
end
|
||||
|
||||
def check_single_user_mode
|
||||
redirect_to root_path if Rails.configuration.x.single_user_mode
|
||||
def check_enabled_registrations
|
||||
redirect_to root_path if Rails.configuration.x.single_user_mode || !Setting.open_registrations
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
|
||||
def determine_layout
|
||||
%w(edit update).include?(action_name) ? 'admin' : 'auth'
|
||||
end
|
||||
|
@ -3,6 +3,7 @@
|
||||
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
||||
skip_before_action :authenticate_resource_owner!
|
||||
|
||||
before_action :set_locale
|
||||
before_action :store_current_location
|
||||
before_action :authenticate_resource_owner!
|
||||
|
||||
@ -11,4 +12,10 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
||||
def store_current_location
|
||||
store_location_for(:user, request.url)
|
||||
end
|
||||
|
||||
def set_locale
|
||||
I18n.locale = current_user.try(:locale) || I18n.default_locale
|
||||
rescue I18n::InvalidLocale
|
||||
I18n.locale = I18n.default_locale
|
||||
end
|
||||
end
|
||||
|
@ -8,6 +8,7 @@ class RemoteFollowController < ApplicationController
|
||||
|
||||
def new
|
||||
@remote_follow = RemoteFollow.new
|
||||
@remote_follow.acct = session[:remote_follow] if session.key?(:remote_follow)
|
||||
end
|
||||
|
||||
def create
|
||||
@ -22,6 +23,8 @@ class RemoteFollowController < ApplicationController
|
||||
render(:new) && return
|
||||
end
|
||||
|
||||
session[:remote_follow] = @remote_follow.acct
|
||||
|
||||
redirect_to Addressable::Template.new(redirect_url_link.template).expand(uri: "#{@account.username}@#{Rails.configuration.x.local_domain}").to_s
|
||||
else
|
||||
render :new
|
||||
|
@ -10,6 +10,7 @@ module SettingsHelper
|
||||
hu: 'Magyar',
|
||||
uk: 'Українська',
|
||||
'zh-CN': '简体中文',
|
||||
fi: 'Suomi',
|
||||
eo: 'Esperanto',
|
||||
}.freeze
|
||||
|
||||
|
@ -2,17 +2,30 @@
|
||||
|
||||
class EmailValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
return if Rails.configuration.x.email_domains_blacklist.empty?
|
||||
|
||||
record.errors.add(attribute, I18n.t('users.invalid_email')) if blocked_email?(value)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def blocked_email?(value)
|
||||
on_blacklist?(value) || not_on_whitelist?(value)
|
||||
end
|
||||
|
||||
def on_blacklist?(value)
|
||||
return false if Rails.configuration.x.email_domains_blacklist.blank?
|
||||
|
||||
domains = Rails.configuration.x.email_domains_blacklist.gsub('.', '\.')
|
||||
regexp = Regexp.new("@(.+\\.)?(#{domains})", true)
|
||||
|
||||
value =~ regexp
|
||||
end
|
||||
|
||||
def not_on_whitelist?(value)
|
||||
return false if Rails.configuration.x.email_domains_whitelist.blank?
|
||||
|
||||
domains = Rails.configuration.x.email_domains_whitelist.gsub('.', '\.')
|
||||
regexp = Regexp.new("@(.+\\.)?(#{domains})", true)
|
||||
|
||||
value !~ regexp
|
||||
end
|
||||
end
|
||||
|
@ -4,4 +4,5 @@ module Mastodon
|
||||
class Error < StandardError; end
|
||||
class NotPermittedError < Error; end
|
||||
class ValidationError < Error; end
|
||||
class RaceConditionError < Error; end
|
||||
end
|
||||
|
@ -5,17 +5,17 @@ require 'singleton'
|
||||
class FeedManager
|
||||
include Singleton
|
||||
|
||||
MAX_ITEMS = 800
|
||||
MAX_ITEMS = 400
|
||||
|
||||
def key(type, id)
|
||||
"feed:#{type}:#{id}"
|
||||
end
|
||||
|
||||
def filter?(timeline_type, status, receiver)
|
||||
def filter?(timeline_type, status, receiver_id)
|
||||
if timeline_type == :home
|
||||
filter_from_home?(status, receiver)
|
||||
filter_from_home?(status, receiver_id)
|
||||
elsif timeline_type == :mentions
|
||||
filter_from_mentions?(status, receiver)
|
||||
filter_from_mentions?(status, receiver_id)
|
||||
else
|
||||
false
|
||||
end
|
||||
@ -34,12 +34,7 @@ class FeedManager
|
||||
trim(timeline_type, account.id)
|
||||
end
|
||||
|
||||
broadcast(account.id, event: 'update', payload: inline_render(account, 'api/v1/statuses/show', status))
|
||||
end
|
||||
|
||||
def broadcast(timeline_id, options = {})
|
||||
options[:queued_at] = (Time.now.to_f * 1000.0).to_i
|
||||
ActionCable.server.broadcast("timeline:#{timeline_id}", options)
|
||||
PushUpdateWorker.perform_async(account.id, status.id)
|
||||
end
|
||||
|
||||
def trim(type, account_id)
|
||||
@ -50,10 +45,18 @@ class FeedManager
|
||||
|
||||
def merge_into_timeline(from_account, into_account)
|
||||
timeline_key = key(:home, into_account.id)
|
||||
query = from_account.statuses.limit(FeedManager::MAX_ITEMS / 4)
|
||||
|
||||
from_account.statuses.limit(MAX_ITEMS).each do |status|
|
||||
next if status.direct_visibility? || filter?(:home, status, into_account)
|
||||
redis.zadd(timeline_key, status.id, status.id)
|
||||
if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4
|
||||
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true)&.first&.last&.to_i || 0
|
||||
query = query.where('id > ?', oldest_home_score)
|
||||
end
|
||||
|
||||
redis.pipelined do
|
||||
query.each do |status|
|
||||
next if status.direct_visibility? || filter?(:home, status, into_account)
|
||||
redis.zadd(timeline_key, status.id, status.id)
|
||||
end
|
||||
end
|
||||
|
||||
trim(:home, into_account.id)
|
||||
@ -61,31 +64,16 @@ class FeedManager
|
||||
|
||||
def unmerge_from_timeline(from_account, into_account)
|
||||
timeline_key = key(:home, into_account.id)
|
||||
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true)&.first&.last&.to_i || 0
|
||||
|
||||
from_account.statuses.select('id').find_each do |status|
|
||||
redis.zrem(timeline_key, status.id)
|
||||
redis.zremrangebyscore(timeline_key, status.id, status.id)
|
||||
end
|
||||
end
|
||||
|
||||
def inline_render(target_account, template, object)
|
||||
rabl_scope = Class.new do
|
||||
include RoutingHelper
|
||||
|
||||
def initialize(account)
|
||||
@account = account
|
||||
end
|
||||
|
||||
def current_user
|
||||
@account.try(:user)
|
||||
end
|
||||
|
||||
def current_account
|
||||
@account
|
||||
from_account.statuses.select('id').where('id > ?', oldest_home_score).find_in_batches do |statuses|
|
||||
redis.pipelined do
|
||||
statuses.each do |status|
|
||||
redis.zrem(timeline_key, status.id)
|
||||
redis.zremrangebyscore(timeline_key, status.id, status.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Rabl::Renderer.new(template, object, view_path: 'app/views', format: :json, scope: rabl_scope.new(target_account)).render
|
||||
end
|
||||
|
||||
private
|
||||
@ -94,37 +82,39 @@ class FeedManager
|
||||
Redis.current
|
||||
end
|
||||
|
||||
def filter_from_home?(status, receiver)
|
||||
return true if receiver.muting?(status.account)
|
||||
def filter_from_home?(status, receiver_id)
|
||||
return true if status.reply? && status.in_reply_to_id.nil?
|
||||
|
||||
should_filter = false
|
||||
check_for_mutes = [status.account_id]
|
||||
check_for_mutes.concat([status.reblog.account_id]) if status.reblog?
|
||||
|
||||
if status.reply? && status.in_reply_to_id.nil?
|
||||
should_filter = true
|
||||
elsif status.reply? && !status.in_reply_to_account_id.nil? # Filter out if it's a reply
|
||||
should_filter = !receiver.following?(status.in_reply_to_account) # and I'm not following the person it's a reply to
|
||||
should_filter &&= !(receiver.id == status.in_reply_to_account_id) # and it's not a reply to me
|
||||
should_filter &&= !(status.account_id == status.in_reply_to_account_id) # and it's not a self-reply
|
||||
elsif status.reblog? # Filter out a reblog
|
||||
should_filter = receiver.blocking?(status.reblog.account) # if I'm blocking the reblogged person
|
||||
should_filter ||= receiver.muting?(status.reblog.account) # or muting that person
|
||||
should_filter ||= status.reblog.account.blocking?(receiver) # or if the author of the reblogged status is blocking me
|
||||
return true if Mute.where(account_id: receiver_id, target_account_id: check_for_mutes).any?
|
||||
|
||||
check_for_blocks = status.mentions.map(&:account_id)
|
||||
check_for_blocks.concat([status.reblog.account_id]) if status.reblog?
|
||||
|
||||
return true if Block.where(account_id: receiver_id, target_account_id: check_for_blocks).any?
|
||||
|
||||
if status.reply? && !status.in_reply_to_account_id.nil? # Filter out if it's a reply
|
||||
should_filter = !Follow.where(account_id: receiver_id, target_account_id: status.in_reply_to_account_id).exists? # and I'm not following the person it's a reply to
|
||||
should_filter &&= !(receiver_id == status.in_reply_to_account_id) # and it's not a reply to me
|
||||
should_filter &&= !(status.account_id == status.in_reply_to_account_id) # and it's not a self-reply
|
||||
return should_filter
|
||||
elsif status.reblog? # Filter out a reblog
|
||||
return Block.where(account_id: status.reblog.account_id, target_account_id: receiver_id).exists? # or if the author of the reblogged status is blocking me
|
||||
end
|
||||
|
||||
should_filter ||= receiver.blocking?(status.mentions.map(&:account_id)) # or if it mentions someone I blocked
|
||||
|
||||
should_filter
|
||||
false
|
||||
end
|
||||
|
||||
def filter_from_mentions?(status, receiver)
|
||||
should_filter = receiver.id == status.account_id # Filter if I'm mentioning myself
|
||||
should_filter ||= receiver.blocking?(status.account) # or it's from someone I blocked
|
||||
should_filter ||= receiver.blocking?(status.mentions.includes(:account).map(&:account)) # or if it mentions someone I blocked
|
||||
should_filter ||= (status.account.silenced? && !receiver.following?(status.account)) # of if the account is silenced and I'm not following them
|
||||
def filter_from_mentions?(status, receiver_id)
|
||||
check_for_blocks = [status.account_id]
|
||||
check_for_blocks.concat(status.mentions.pluck(:account_id))
|
||||
check_for_blocks.concat([status.in_reply_to_account]) if status.reply? && !status.in_reply_to_account_id.nil?
|
||||
|
||||
if status.reply? && !status.in_reply_to_account_id.nil? # or it's a reply
|
||||
should_filter ||= receiver.blocking?(status.in_reply_to_account) # to a user I blocked
|
||||
end
|
||||
should_filter = receiver_id == status.account_id # Filter if I'm mentioning myself
|
||||
should_filter ||= Block.where(account_id: receiver_id, target_account_id: check_for_blocks).any? # or it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked
|
||||
should_filter ||= (status.account.silenced? && !Follow.where(account_id: receiver_id, target_account_id: status.account_id).exists?) # of if the account is silenced and I'm not following them
|
||||
|
||||
should_filter
|
||||
end
|
||||
|
17
app/lib/inline_rabl_scope.rb
Normal file
17
app/lib/inline_rabl_scope.rb
Normal file
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class InlineRablScope
|
||||
include RoutingHelper
|
||||
|
||||
def initialize(account)
|
||||
@account = account
|
||||
end
|
||||
|
||||
def current_user
|
||||
@account.try(:user)
|
||||
end
|
||||
|
||||
def current_account
|
||||
@account
|
||||
end
|
||||
end
|
13
app/lib/inline_renderer.rb
Normal file
13
app/lib/inline_renderer.rb
Normal file
@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class InlineRenderer
|
||||
def self.render(status, current_account, template)
|
||||
Rabl::Renderer.new(
|
||||
template,
|
||||
status,
|
||||
view_path: 'app/views',
|
||||
format: :json,
|
||||
scope: InlineRablScope.new(current_account)
|
||||
).render
|
||||
end
|
||||
end
|
@ -3,9 +3,8 @@
|
||||
class Block < ApplicationRecord
|
||||
include Paginable
|
||||
|
||||
belongs_to :account
|
||||
belongs_to :target_account, class_name: 'Account'
|
||||
belongs_to :account, required: true
|
||||
belongs_to :target_account, class_name: 'Account', required: true
|
||||
|
||||
validates :account, :target_account, presence: true
|
||||
validates :account_id, uniqueness: { scope: :target_account_id }
|
||||
end
|
||||
|
@ -10,17 +10,9 @@ class Feed
|
||||
max_id = '+inf' if max_id.blank?
|
||||
since_id = '-inf' if since_id.blank?
|
||||
unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", "(#{since_id}", limit: [0, limit], with_scores: true).map(&:last).map(&:to_i)
|
||||
status_map = Status.where(id: unhydrated).cache_ids.map { |s| [s.id, s] }.to_h
|
||||
|
||||
# If we're after most recent items and none are there, we need to precompute the feed
|
||||
if unhydrated.empty? && max_id == '+inf' && since_id == '-inf'
|
||||
RegenerationWorker.perform_async(@account.id, @type)
|
||||
@statuses = Status.send("as_#{@type}_timeline", @account).cache_ids.paginate_by_max_id(limit, nil, nil)
|
||||
else
|
||||
status_map = Status.where(id: unhydrated).cache_ids.map { |s| [s.id, s] }.to_h
|
||||
@statuses = unhydrated.map { |id| status_map[id] }.compact
|
||||
end
|
||||
|
||||
@statuses
|
||||
unhydrated.map { |id| status_map[id] }.compact
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -3,11 +3,14 @@
|
||||
class Follow < ApplicationRecord
|
||||
include Paginable
|
||||
|
||||
belongs_to :account, counter_cache: :following_count
|
||||
belongs_to :target_account, class_name: 'Account', counter_cache: :followers_count
|
||||
belongs_to :account, counter_cache: :following_count, required: true
|
||||
|
||||
belongs_to :target_account,
|
||||
class_name: 'Account',
|
||||
counter_cache: :followers_count,
|
||||
required: true
|
||||
|
||||
has_one :notification, as: :activity, dependent: :destroy
|
||||
|
||||
validates :account, :target_account, presence: true
|
||||
validates :account_id, uniqueness: { scope: :target_account_id }
|
||||
end
|
||||
|
@ -3,12 +3,11 @@
|
||||
class FollowRequest < ApplicationRecord
|
||||
include Paginable
|
||||
|
||||
belongs_to :account
|
||||
belongs_to :target_account, class_name: 'Account'
|
||||
belongs_to :account, required: true
|
||||
belongs_to :target_account, class_name: 'Account', required: true
|
||||
|
||||
has_one :notification, as: :activity, dependent: :destroy
|
||||
|
||||
validates :account, :target_account, presence: true
|
||||
validates :account_id, uniqueness: { scope: :target_account_id }
|
||||
|
||||
def authorize!
|
||||
|
@ -1,11 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Mention < ApplicationRecord
|
||||
belongs_to :account, inverse_of: :mentions
|
||||
belongs_to :status
|
||||
belongs_to :account, inverse_of: :mentions, required: true
|
||||
belongs_to :status, required: true
|
||||
|
||||
has_one :notification, as: :activity, dependent: :destroy
|
||||
|
||||
validates :account, :status, presence: true
|
||||
validates :account, uniqueness: { scope: :status }
|
||||
end
|
||||
|
@ -3,6 +3,7 @@
|
||||
class Report < ApplicationRecord
|
||||
belongs_to :account
|
||||
belongs_to :target_account, class_name: 'Account'
|
||||
belongs_to :action_taken_by_account, class_name: 'Account'
|
||||
|
||||
scope :unresolved, -> { where(action_taken: false) }
|
||||
scope :resolved, -> { where(action_taken: true) }
|
||||
|
@ -161,9 +161,9 @@ class Status < ApplicationRecord
|
||||
return where.not(visibility: [:private, :direct]) if account.nil?
|
||||
|
||||
if target_account.blocking?(account) # get rid of blocked peeps
|
||||
where('1 = 0')
|
||||
none
|
||||
elsif account.id == target_account.id # author can see own stuff
|
||||
where('1 = 1')
|
||||
all
|
||||
elsif account.following?(target_account) # followers can see followers-only stuff, but also things they are mentioned in
|
||||
joins('LEFT OUTER JOIN mentions ON statuses.id = mentions.status_id AND mentions.account_id = ' + account.id.to_s)
|
||||
.where('statuses.visibility != ? OR mentions.id IS NOT NULL', Status.visibilities[:direct])
|
||||
@ -188,7 +188,7 @@ class Status < ApplicationRecord
|
||||
end
|
||||
|
||||
before_validation do
|
||||
text.strip!
|
||||
text&.strip!
|
||||
spoiler_text&.strip!
|
||||
|
||||
self.reply = !(in_reply_to_id.nil? && thread.nil?) unless reply
|
||||
|
@ -1,13 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class BlockDomainService < BaseService
|
||||
def call(domain, severity)
|
||||
DomainBlock.where(domain: domain).first_or_create!(domain: domain, severity: severity)
|
||||
|
||||
if severity == :silence
|
||||
Account.where(domain: domain).update_all(silenced: true)
|
||||
def call(domain_block)
|
||||
if domain_block.silence?
|
||||
Account.where(domain: domain_block.domain).update_all(silenced: true)
|
||||
else
|
||||
Account.where(domain: domain).find_each do |account|
|
||||
Account.where(domain: domain_block.domain).find_each do |account|
|
||||
account.subscription(api_subscription_url(account.id)).unsubscribe if account.subscribed?
|
||||
SuspendAccountService.new.call(account)
|
||||
end
|
||||
|
@ -4,6 +4,8 @@ class FanOutOnWriteService < BaseService
|
||||
# Push a status into home and mentions feeds
|
||||
# @param [Status] status
|
||||
def call(status)
|
||||
raise Mastodon::RaceConditionError if status.visibility.nil?
|
||||
|
||||
deliver_to_self(status) if status.account.local?
|
||||
|
||||
if status.direct_visibility?
|
||||
@ -14,6 +16,7 @@ class FanOutOnWriteService < BaseService
|
||||
|
||||
return if status.account.silenced? || !status.public_visibility? || status.reblog?
|
||||
|
||||
render_anonymous_payload(status)
|
||||
deliver_to_hashtags(status)
|
||||
|
||||
return if status.reply? && status.in_reply_to_account_id != status.account_id
|
||||
@ -31,9 +34,8 @@ class FanOutOnWriteService < BaseService
|
||||
def deliver_to_followers(status)
|
||||
Rails.logger.debug "Delivering status #{status.id} to followers"
|
||||
|
||||
status.account.followers.where(domain: nil).joins(:user).where('users.current_sign_in_at > ?', 14.days.ago).find_each do |follower|
|
||||
next if FeedManager.instance.filter?(:home, status, follower)
|
||||
FeedManager.instance.push(:home, follower, status)
|
||||
status.account.followers.where(domain: nil).joins(:user).where('users.current_sign_in_at > ?', 14.days.ago).select(:id).find_each do |follower|
|
||||
FeedInsertWorker.perform_async(status.id, follower.id)
|
||||
end
|
||||
end
|
||||
|
||||
@ -42,28 +44,28 @@ class FanOutOnWriteService < BaseService
|
||||
|
||||
status.mentions.includes(:account).each do |mention|
|
||||
mentioned_account = mention.account
|
||||
next if !mentioned_account.local? || !mentioned_account.following?(status.account) || FeedManager.instance.filter?(:home, status, mentioned_account)
|
||||
next if !mentioned_account.local? || !mentioned_account.following?(status.account) || FeedManager.instance.filter?(:home, status, mention.account_id)
|
||||
FeedManager.instance.push(:home, mentioned_account, status)
|
||||
end
|
||||
end
|
||||
|
||||
def render_anonymous_payload(status)
|
||||
@payload = InlineRenderer.render(status, nil, 'api/v1/statuses/show')
|
||||
end
|
||||
|
||||
def deliver_to_hashtags(status)
|
||||
Rails.logger.debug "Delivering status #{status.id} to hashtags"
|
||||
|
||||
payload = FeedManager.instance.inline_render(nil, 'api/v1/statuses/show', status)
|
||||
|
||||
status.tags.find_each do |tag|
|
||||
FeedManager.instance.broadcast("hashtag:#{tag.name}", event: 'update', payload: payload)
|
||||
FeedManager.instance.broadcast("hashtag:#{tag.name}:local", event: 'update', payload: payload) if status.account.local?
|
||||
status.tags.pluck(:name).each do |hashtag|
|
||||
Redis.current.publish("hashtag:#{hashtag}", Oj.dump(event: :update, payload: @payload))
|
||||
Redis.current.publish("hashtag:#{hashtag}:local", Oj.dump(event: :update, payload: @payload)) if status.account.local?
|
||||
end
|
||||
end
|
||||
|
||||
def deliver_to_public(status)
|
||||
Rails.logger.debug "Delivering status #{status.id} to public timeline"
|
||||
|
||||
payload = FeedManager.instance.inline_render(nil, 'api/v1/statuses/show', status)
|
||||
|
||||
FeedManager.instance.broadcast(:public, event: 'update', payload: payload)
|
||||
FeedManager.instance.broadcast('public:local', event: 'update', payload: payload) if status.account.local?
|
||||
Redis.current.publish('public', Oj.dump(event: 'update', payload: @payload))
|
||||
Redis.current.publish('public:local', Oj.dump(event: 'update', payload: @payload)) if status.account.local?
|
||||
end
|
||||
end
|
||||
|
@ -17,7 +17,7 @@ class NotifyService < BaseService
|
||||
private
|
||||
|
||||
def blocked_mention?
|
||||
FeedManager.instance.filter?(:mentions, @notification.mention.status, @recipient)
|
||||
FeedManager.instance.filter?(:mentions, @notification.mention.status, @recipient.id)
|
||||
end
|
||||
|
||||
def blocked_favourite?
|
||||
@ -50,7 +50,7 @@ class NotifyService < BaseService
|
||||
def create_notification
|
||||
@notification.save!
|
||||
return unless @notification.browserable?
|
||||
FeedManager.instance.broadcast(@recipient.id, event: 'notification', payload: FeedManager.instance.inline_render(@recipient, 'api/v1/notifications/show', @notification))
|
||||
Redis.current.publish(@recipient.id, Oj.dump(event: :notification, payload: InlineRenderer.render(@notification, @recipient, 'api/v1/notifications/show')))
|
||||
end
|
||||
|
||||
def send_email
|
||||
|
@ -5,9 +5,11 @@ class PrecomputeFeedService < BaseService
|
||||
# @param [Symbol] type :home or :mentions
|
||||
# @param [Account] account
|
||||
def call(_, account)
|
||||
Status.as_home_timeline(account).limit(FeedManager::MAX_ITEMS).each do |status|
|
||||
next if status.direct_visibility? || FeedManager.instance.filter?(:home, status, account)
|
||||
redis.zadd(FeedManager.instance.key(:home, account.id), status.id, status.reblog? ? status.reblog_of_id : status.id)
|
||||
redis.pipelined do
|
||||
Status.as_home_timeline(account).limit(FeedManager::MAX_ITEMS / 4).each do |status|
|
||||
next if status.direct_visibility? || FeedManager.instance.filter?(:home, status, account.id)
|
||||
redis.zadd(FeedManager.instance.key(:home, account.id), status.id, status.reblog? ? status.reblog_of_id : status.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,17 +65,17 @@ class RemoveStatusService < BaseService
|
||||
redis.zremrangebyscore |