Merge branch 'master' into patch-4
commit
0c4e9fdda0
|
@ -1,8 +1,13 @@
|
|||
FROM ruby:2.3.1-alpine
|
||||
|
||||
LABEL maintainer="https://github.com/tootsuite/mastodon" \
|
||||
description="A GNU Social-compatible microblogging server"
|
||||
|
||||
ENV RAILS_ENV=production \
|
||||
NODE_ENV=production
|
||||
|
||||
EXPOSE 3000 4000
|
||||
|
||||
WORKDIR /mastodon
|
||||
|
||||
COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
|
||||
|
|
1
Gemfile
1
Gemfile
|
@ -34,6 +34,7 @@ gem 'doorkeeper'
|
|||
gem 'rabl'
|
||||
gem 'rqrcode'
|
||||
gem 'twitter-text'
|
||||
gem 'ox'
|
||||
gem 'oj'
|
||||
gem 'hiredis'
|
||||
gem 'redis', '~>3.2', require: ['redis', 'redis/connection/hiredis']
|
||||
|
|
|
@ -240,6 +240,7 @@ GEM
|
|||
addressable (~> 2.4)
|
||||
http (~> 2.0)
|
||||
nokogiri (~> 1.6)
|
||||
ox (2.4.11)
|
||||
paperclip (5.1.0)
|
||||
activemodel (>= 4.2.0)
|
||||
activesupport (>= 4.2.0)
|
||||
|
@ -482,6 +483,7 @@ DEPENDENCIES
|
|||
nokogiri
|
||||
oj
|
||||
ostatus2
|
||||
ox
|
||||
paperclip (~> 5.1)
|
||||
paperclip-av-transcoder
|
||||
pg
|
||||
|
|
2
Procfile
2
Procfile
|
@ -1,2 +1,2 @@
|
|||
web: bundle exec puma -C config/puma.rb
|
||||
worker: bundle exec sidekiq -q default -q mailers -q push
|
||||
worker: bundle exec sidekiq -q default -q push -q pull -q mailers
|
||||
|
|
|
@ -65,6 +65,8 @@ Consult the example configuration file, `.env.production.sample` for the full li
|
|||
|
||||
## Running with Docker and Docker-Compose
|
||||
|
||||
[![](https://images.microbadger.com/badges/version/gargron/mastodon.svg)](https://microbadger.com/images/gargron/mastodon "Get your own version badge on microbadger.com") [![](https://images.microbadger.com/badges/image/gargron/mastodon.svg)](https://microbadger.com/images/gargron/mastodon "Get your own image badge on microbadger.com")
|
||||
|
||||
The project now includes a `Dockerfile` and a `docker-compose.yml`. You need to turn `.env.production.sample` into `.env.production` with all the variables set before you can:
|
||||
|
||||
docker-compose build
|
||||
|
|
|
@ -84,6 +84,16 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||
config.vm.provider :virtualbox do |vb|
|
||||
vb.name = "mastodon"
|
||||
vb.customize ["modifyvm", :id, "--memory", "1024"]
|
||||
|
||||
# Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions.
|
||||
# https://github.com/mitchellh/vagrant/issues/1172
|
||||
vb.customize ["modifyvm", :id, "--natdnsproxy1", "off"]
|
||||
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "off"]
|
||||
|
||||
# Use "virtio" network interfaces for better performance.
|
||||
vb.customize ["modifyvm", :id, "--nictype1", "virtio"]
|
||||
vb.customize ["modifyvm", :id, "--nictype2", "virtio"]
|
||||
|
||||
end
|
||||
|
||||
config.vm.hostname = "mastodon.dev"
|
||||
|
@ -91,9 +101,9 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||
# This uses the vagrant-hostsupdater plugin, and lets you
|
||||
# access the development site at http://mastodon.dev.
|
||||
# To install:
|
||||
# $ vagrant plugin install hostsupdater
|
||||
# $ vagrant plugin install vagrant-hostsupdater
|
||||
if defined?(VagrantPlugins::HostsUpdater)
|
||||
config.vm.network :private_network, ip: "192.168.42.42"
|
||||
config.vm.network :private_network, ip: "192.168.42.42", nictype: "virtio"
|
||||
config.hostsupdater.remove_on_suspend = false
|
||||
end
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 874 KiB After Width: | Height: | Size: 209 KiB |
|
@ -16,7 +16,8 @@ class AccountsController < ApplicationController
|
|||
end
|
||||
|
||||
format.atom do
|
||||
@entries = @account.stream_entries.order('id desc').where(activity_type: 'Status').where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@entries = @account.stream_entries.order('id desc').where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
render xml: AtomSerializer.render(AtomSerializer.new.feed(@account, @entries.to_a))
|
||||
end
|
||||
|
||||
format.activitystreams2
|
||||
|
|
|
@ -19,7 +19,9 @@ class StreamEntriesController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
format.atom
|
||||
format.atom do
|
||||
render xml: AtomSerializer.render(AtomSerializer.new.entry(@stream_entry, true))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,351 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AtomSerializer
|
||||
include RoutingHelper
|
||||
|
||||
class << self
|
||||
def render(element)
|
||||
document = Ox::Document.new(version: '1.0')
|
||||
document << element
|
||||
('<?xml version="1.0"?>' + Ox.dump(element)).force_encoding('UTF-8')
|
||||
end
|
||||
end
|
||||
|
||||
def author(account)
|
||||
author = Ox::Element.new('author')
|
||||
|
||||
uri = TagManager.instance.uri_for(account)
|
||||
|
||||
append_element(author, 'id', uri)
|
||||
append_element(author, 'activity:object-type', TagManager::TYPES[:person])
|
||||
append_element(author, 'uri', uri)
|
||||
append_element(author, 'name', account.username)
|
||||
append_element(author, 'email', account.local? ? "#{account.acct}@#{Rails.configuration.x.local_domain}" : account.acct)
|
||||
append_element(author, 'summary', account.note)
|
||||
append_element(author, 'link', nil, rel: :alternate, type: 'text/html', href: TagManager.instance.url_for(account))
|
||||
append_element(author, 'link', nil, rel: :avatar, type: account.avatar_content_type, 'media:width': 120, 'media:height': 120, href: full_asset_url(account.avatar.url(:original)))
|
||||
append_element(author, 'link', nil, rel: :header, type: account.header_content_type, 'media:width': 700, 'media:height': 335, href: full_asset_url(account.header.url(:original)))
|
||||
append_element(author, 'poco:preferredUsername', account.username)
|
||||
append_element(author, 'poco:displayName', account.display_name) unless account.display_name.blank?
|
||||
append_element(author, 'poco:note', Formatter.instance.simplified_format(account).to_str) unless account.note.blank?
|
||||
append_element(author, 'mastodon:scope', account.locked? ? :private : :public)
|
||||
|
||||
author
|
||||
end
|
||||
|
||||
def feed(account, stream_entries)
|
||||
feed = Ox::Element.new('feed')
|
||||
|
||||
add_namespaces(feed)
|
||||
|
||||
append_element(feed, 'id', account_url(account, format: 'atom'))
|
||||
append_element(feed, 'title', account.display_name)
|
||||
append_element(feed, 'subtitle', account.note)
|
||||
append_element(feed, 'updated', account.updated_at.iso8601)
|
||||
append_element(feed, 'logo', full_asset_url(account.avatar.url(:original)))
|
||||
|
||||
feed << author(account)
|
||||
|
||||
append_element(feed, 'link', nil, rel: :alternate, type: 'text/html', href: TagManager.instance.url_for(account))
|
||||
append_element(feed, 'link', nil, rel: :self, type: 'application/atom+xml', href: account_url(account, format: 'atom'))
|
||||
append_element(feed, 'link', nil, rel: :next, type: 'application/atom+xml', href: account_url(account, format: 'atom', max_id: stream_entries.last.id)) if stream_entries.size == 20
|
||||
append_element(feed, 'link', nil, rel: :hub, href: api_push_url)
|
||||
append_element(feed, 'link', nil, rel: :salmon, href: api_salmon_url(account.id))
|
||||
|
||||
stream_entries.each do |stream_entry|
|
||||
feed << entry(stream_entry)
|
||||
end
|
||||
|
||||
feed
|
||||
end
|
||||
|
||||
def entry(stream_entry, root = false)
|
||||
entry = Ox::Element.new('entry')
|
||||
|
||||
add_namespaces(entry) if root
|
||||
|
||||
append_element(entry, 'id', TagManager.instance.unique_tag(stream_entry.created_at, stream_entry.activity_id, stream_entry.activity_type))
|
||||
append_element(entry, 'published', stream_entry.created_at.iso8601)
|
||||
append_element(entry, 'updated', stream_entry.updated_at.iso8601)
|
||||
append_element(entry, 'title', stream_entry&.status&.title)
|
||||
|
||||
entry << author(stream_entry.account) if root
|
||||
|
||||
append_element(entry, 'activity:object-type', TagManager::TYPES[stream_entry.object_type])
|
||||
append_element(entry, 'activity:verb', TagManager::VERBS[stream_entry.verb])
|
||||
|
||||
entry << object(stream_entry.target) if stream_entry.targeted?
|
||||
|
||||
serialize_status_attributes(entry, stream_entry.status) unless stream_entry.status.nil?
|
||||
|
||||
append_element(entry, 'link', nil, rel: :alternate, type: 'text/html', href: account_stream_entry_url(stream_entry.account, stream_entry))
|
||||
append_element(entry, 'link', nil, rel: :self, type: 'application/atom+xml', href: account_stream_entry_url(stream_entry.account, stream_entry, format: 'atom'))
|
||||
append_element(entry, 'thr:in-reply-to', nil, ref: TagManager.instance.uri_for(stream_entry.thread), href: TagManager.instance.url_for(stream_entry.thread)) if stream_entry.threaded?
|
||||
|
||||
entry
|
||||
end
|
||||
|
||||
def object(status)
|
||||
object = Ox::Element.new('activity:object')
|
||||
|
||||
append_element(object, 'id', TagManager.instance.uri_for(status))
|
||||
append_element(object, 'published', status.created_at.iso8601)
|
||||
append_element(object, 'updated', status.updated_at.iso8601)
|
||||
append_element(object, 'title', status.title)
|
||||
|
||||
object << author(status.account)
|
||||
|
||||
append_element(object, 'activity:object-type', TagManager::TYPES[status.object_type])
|
||||
append_element(object, 'activity:verb', TagManager::VERBS[status.verb])
|
||||
|
||||
serialize_status_attributes(object, status)
|
||||
|
||||
append_element(object, 'link', nil, rel: :alternate, type: 'text/html', href: TagManager.instance.url_for(status))
|
||||
append_element(object, 'thr:in-reply-to', nil, ref: TagManager.instance.uri_for(status.thread), href: TagManager.instance.url_for(status.thread)) if status.reply?
|
||||
|
||||
object
|
||||
end
|
||||
|
||||
def follow_salmon(follow)
|
||||
entry = Ox::Element.new('entry')
|
||||
add_namespaces(entry)
|
||||
|
||||
description = "#{follow.account.acct} started following #{follow.target_account.acct}"
|
||||
|
||||
append_element(entry, 'id', TagManager.instance.unique_tag(follow.created_at, follow.id, 'Follow'))
|
||||
append_element(entry, 'title', description)
|
||||
append_element(entry, 'content', description, type: :html)
|
||||
|
||||
entry << author(follow.account)
|
||||
|
||||
append_element(entry, 'activity:object-type', TagManager::TYPES[:activity])
|
||||
append_element(entry, 'activity:verb', TagManager::VERBS[:follow])
|
||||
|
||||
object = author(follow.target_account)
|
||||
object.value = 'activity:object'
|
||||
|
||||
entry << object
|
||||
entry
|
||||
end
|
||||
|
||||
def follow_request_salmon(follow_request)
|
||||
entry = Ox::Element.new('entry')
|
||||
add_namespaces(entry)
|
||||
|
||||
append_element(entry, 'id', TagManager.instance.unique_tag(follow_request.created_at, follow_request.id, 'FollowRequest'))
|
||||
append_element(entry, 'title', "#{follow_request.account.acct} requested to follow #{follow_request.target_account.acct}")
|
||||
|
||||
entry << author(follow_request.account)
|
||||
|
||||
append_element(entry, 'activity:object-type', TagManager::TYPES[:activity])
|
||||
append_element(entry, 'activity:verb', TagManager::VERBS[:request_friend])
|
||||
|
||||
object = author(follow_request.target_account)
|
||||
object.value = 'activity:object'
|
||||
|
||||
entry << object
|
||||
entry
|
||||
end
|
||||
|
||||
def authorize_follow_request_salmon(follow_request)
|
||||
entry = Ox::Element.new('entry')
|
||||
add_namespaces(entry)
|
||||
|
||||
append_element(entry, 'id', TagManager.instance.unique_tag(Time.now.utc, follow_request.id, 'FollowRequest'))
|
||||
append_element(entry, 'title', "#{follow_request.target_account.acct} authorizes follow request by #{follow_request.account.acct}")
|
||||
|
||||
entry << author(follow_request.target_account)
|
||||
|
||||
append_element(entry, 'activity:object-type', TagManager::TYPES[:activity])
|
||||
append_element(entry, 'activity:verb', TagManager::VERBS[:authorize])
|
||||
|
||||
object = Ox::Element.new('activity:object')
|
||||
object << author(follow_request.account)
|
||||
|
||||
append_element(object, 'activity:object-type', TagManager::TYPES[:activity])
|
||||
append_element(object, 'activity:verb', TagManager::VERBS[:request_friend])
|
||||
|
||||
inner_object = author(follow_request.target_account)
|
||||
inner_object.value = 'activity:object'
|
||||
|
||||
object << inner_object
|
||||
entry << object
|
||||
entry
|
||||
end
|
||||
|
||||
def reject_follow_request_salmon(follow_request)
|
||||
entry = Ox::Element.new('entry')
|
||||
add_namespaces(entry)
|
||||
|
||||
append_element(entry, 'id', TagManager.instance.unique_tag(Time.now.utc, follow_request.id, 'FollowRequest'))
|
||||
append_element(entry, 'title', "#{follow_request.target_account.acct} rejects follow request by #{follow_request.account.acct}")
|
||||
|
||||
entry << author(follow_request.target_account)
|
||||
|
||||
append_element(entry, 'activity:object-type', TagManager::TYPES[:activity])
|
||||
append_element(entry, 'activity:verb', TagManager::VERBS[:reject])
|
||||
|
||||
object = Ox::Element.new('activity:object')
|
||||
object << author(follow_request.account)
|
||||
|
||||
append_element(object, 'activity:object-type', TagManager::TYPES[:activity])
|
||||
append_element(object, 'activity:verb', TagManager::VERBS[:request_friend])
|
||||
|
||||
inner_object = author(follow_request.target_account)
|
||||
inner_object.value = 'activity:object'
|
||||
|
||||
object << inner_object
|
||||
entry << object
|
||||
entry
|
||||
end
|
||||
|
||||
def unfollow_salmon(follow)
|
||||
entry = Ox::Element.new('entry')
|
||||
add_namespaces(entry)
|
||||
|
||||
description = "#{follow.account.acct} is no longer following #{follow.target_account.acct}"
|
||||
|
||||
append_element(entry, 'id', TagManager.instance.unique_tag(Time.now.utc, follow.id, 'Follow'))
|
||||
append_element(entry, 'title', description)
|
||||
append_element(entry, 'content', description, type: :html)
|
||||
|
||||
entry << author(follow.account)
|
||||
|
||||
append_element(entry, 'activity:object-type', TagManager::TYPES[:activity])
|
||||
append_element(entry, 'activity:verb', TagManager::VERBS[:unfollow])
|
||||
|
||||
object = author(follow.target_account)
|
||||
object.value = 'activity:object'
|
||||
|
||||
entry << object
|
||||
entry
|
||||
end
|
||||
|
||||
def block_salmon(block)
|
||||
entry = Ox::Element.new('entry')
|
||||
add_namespaces(entry)
|
||||
|
||||
description = "#{block.account.acct} no longer wishes to interact with #{block.target_account.acct}"
|
||||
|
||||
append_element(entry, 'id', TagManager.instance.unique_tag(Time.now.utc, block.id, 'Block'))
|
||||
append_element(entry, 'title', description)
|
||||
|
||||
entry << author(block.account)
|
||||
|
||||
append_element(entry, 'activity:object-type', TagManager::TYPES[:activity])
|
||||
append_element(entry, 'activity:verb', TagManager::VERBS[:block])
|
||||
|
||||
object = author(block.target_account)
|
||||
object.value = 'activity:object'
|
||||
|
||||
entry << object
|
||||
entry
|
||||
end
|
||||
|
||||
def unblock_salmon(block)
|
||||
entry = Ox::Element.new('entry')
|
||||
add_namespaces(entry)
|
||||
|
||||
description = "#{block.account.acct} no longer blocks #{block.target_account.acct}"
|
||||
|
||||
append_element(entry, 'id', TagManager.instance.unique_tag(Time.now.utc, block.id, 'Block'))
|
||||
append_element(entry, 'title', description)
|
||||
|
||||
entry << author(block.account)
|
||||
|
||||
append_element(entry, 'activity:object-type', TagManager::TYPES[:activity])
|
||||
append_element(entry, 'activity:verb', TagManager::VERBS[:unblock])
|
||||
|
||||
object = author(block.target_account)
|
||||
object.value = 'activity:object'
|
||||
|
||||
entry << object
|
||||
entry
|
||||
end
|
||||
|
||||
def favourite_salmon(favourite)
|
||||
entry = Ox::Element.new('entry')
|
||||
add_namespaces(entry)
|
||||
|
||||
description = "#{favourite.account.acct} favourited a status by #{favourite.status.account.acct}"
|
||||
|
||||
append_element(entry, 'id', TagManager.instance.unique_tag(favourite.created_at, favourite.id, 'Favourite'))
|
||||
append_element(entry, 'title', description)
|
||||
append_element(entry, 'content', description, type: :html)
|
||||
|
||||
entry << author(favourite.account)
|
||||
|
||||
append_element(entry, 'activity:object-type', TagManager::TYPES[:activity])
|
||||
append_element(entry, 'activity:verb', TagManager::VERBS[:favorite])
|
||||
|
||||
entry << object(favourite.status)
|
||||
|
||||
append_element(entry, 'thr:in-reply-to', nil, ref: TagManager.instance.uri_for(favourite.status), href: TagManager.instance.url_for(favourite.status))
|
||||
|
||||
entry
|
||||
end
|
||||
|
||||
def unfavourite_salmon(favourite)
|
||||
entry = Ox::Element.new('entry')
|
||||
add_namespaces(entry)
|
||||
|
||||
description = "#{favourite.account.acct} no longer favourites a status by #{favourite.status.account.acct}"
|
||||
|
||||
append_element(entry, 'id', TagManager.instance.unique_tag(Time.now.utc, favourite.id, 'Favourite'))
|
||||
append_element(entry, 'title', description)
|
||||
append_element(entry, 'content', description, type: :html)
|
||||
|
||||
entry << author(favourite.account)
|
||||
|
||||
append_element(entry, 'activity:object-type', TagManager::TYPES[:activity])
|
||||
append_element(entry, 'activity:verb', TagManager::VERBS[:unfavorite])
|
||||
|
||||
entry << object(favourite.status)
|
||||
|
||||
append_element(entry, 'thr:in-reply-to', nil, ref: TagManager.instance.uri_for(favourite.status), href: TagManager.instance.url_for(favourite.status))
|
||||
|
||||
entry
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def append_element(parent, name, content = nil, attributes = {})
|
||||
element = Ox::Element.new(name)
|
||||
attributes.each { |k, v| element[k] = v.to_s }
|
||||
element << content.to_s unless content.nil?
|
||||
parent << element
|
||||
end
|
||||
|
||||
def add_namespaces(parent)
|
||||
parent['xmlns'] = TagManager::XMLNS
|
||||
parent['xmlns:thr'] = TagManager::THR_XMLNS
|
||||
parent['xmlns:activity'] = TagManager::AS_XMLNS
|
||||
parent['xmlns:poco'] = TagManager::POCO_XMLNS
|
||||
parent['xmlns:media'] = TagManager::MEDIA_XMLNS
|
||||
parent['xmlns:ostatus'] = TagManager::OS_XMLNS
|
||||
parent['xmlns:mastodon'] = TagManager::MTDN_XMLNS
|
||||
end
|
||||
|
||||
def serialize_status_attributes(entry, status)
|
||||
append_element(entry, 'summary', status.spoiler_text) unless status.spoiler_text.blank?
|
||||
append_element(entry, 'content', Formatter.instance.format(status.reblog? ? status.reblog : status).to_str, type: 'html')
|
||||
|
||||
status.mentions.each do |mentioned|
|
||||
append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': TagManager::TYPES[:person], href: TagManager.instance.uri_for(mentioned.account))
|
||||
end
|
||||
|
||||
append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': TagManager::TYPES[:collection], href: TagManager::COLLECTIONS[:public]) if status.public_visibility?
|
||||
|
||||
status.tags.each do |tag|
|
||||
append_element(entry, 'category', nil, term: tag.name)
|
||||
end
|
||||
|
||||
append_element(entry, 'category', nil, term: 'nsfw') if status.sensitive?
|
||||
|
||||
status.media_attachments.each do |media|
|
||||
append_element(entry, 'link', nil, rel: :enclosure, type: media.file_content_type, length: media.file_file_size, href: full_asset_url(media.file.url(:original, false)))
|
||||
end
|
||||
|
||||
append_element(entry, 'mastodon:scope', status.visibility)
|
||||
end
|
||||
end
|
|
@ -78,6 +78,8 @@ class TagManager
|
|||
case target.object_type
|
||||
when :person
|
||||
account_url(target)
|
||||
when :note, :comment, :activity
|
||||
unique_tag(target.created_at, target.id, 'Status')
|
||||
else
|
||||
unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type)
|
||||
end
|
||||
|
|
|
@ -5,25 +5,21 @@ class StreamEntry < ApplicationRecord
|
|||
|
||||
belongs_to :account, inverse_of: :stream_entries
|
||||
belongs_to :activity, polymorphic: true
|
||||
|
||||
belongs_to :status, foreign_type: 'Status', foreign_key: 'activity_id', inverse_of: :stream_entry
|
||||
|
||||
validates :account, :activity, presence: true
|
||||
|
||||
STATUS_INCLUDES = [:account, :stream_entry, :media_attachments, :tags, mentions: :account, reblog: [:stream_entry, :account, mentions: :account], thread: [:stream_entry, :account]].freeze
|
||||
STATUS_INCLUDES = [:account, :stream_entry, :media_attachments, :tags, mentions: :account, reblog: [:stream_entry, :account, :media_attachments, :tags, mentions: :account], thread: [:stream_entry, :account]].freeze
|
||||
|
||||
default_scope { where(activity_type: 'Status') }
|
||||
scope :with_includes, -> { includes(:account, status: STATUS_INCLUDES) }
|
||||
|
||||
def object_type
|
||||
if orphaned?
|
||||
:activity
|
||||
else
|
||||
targeted? ? :activity : activity.object_type
|
||||
end
|
||||
orphaned? || targeted? ? :activity : status.object_type
|
||||
end
|
||||
|
||||
def verb
|
||||
orphaned? ? :delete : activity.verb
|
||||
orphaned? ? :delete : status.verb
|
||||
end
|
||||
|
||||
def targeted?
|
||||
|
@ -31,15 +27,15 @@ class StreamEntry < ApplicationRecord
|
|||
end
|
||||
|
||||
def target
|
||||
orphaned? ? nil : activity.target
|
||||
orphaned? ? nil : status.target
|
||||
end
|
||||
|
||||
def title
|
||||
orphaned? ? nil : activity.title
|
||||
orphaned? ? nil : status.title
|
||||
end
|
||||
|
||||
def content
|
||||
orphaned? ? nil : activity.content
|
||||
orphaned? ? nil : status.content
|
||||
end
|
||||
|
||||
def threaded?
|
||||
|
@ -47,20 +43,16 @@ class StreamEntry < ApplicationRecord
|
|||
end
|
||||
|
||||
def thread
|
||||
orphaned? ? nil : activity.thread
|
||||
orphaned? ? nil : status.thread
|
||||
end
|
||||
|
||||
def mentions
|
||||
activity.respond_to?(:mentions) ? activity.mentions.map(&:account) : []
|
||||
end
|
||||
|
||||
def activity
|
||||
!new_record? ? send(activity_type.underscore) || super : super
|
||||
orphaned? ? [] : status.mentions.map(&:account)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def orphaned?
|
||||
activity.nil?
|
||||
status.nil?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,20 +9,20 @@ class AfterBlockService < BaseService
|
|||
private
|
||||
|
||||
def clear_timelines(account, target_account)
|
||||
mentions_key = FeedManager.instance.key(:mentions, account.id)
|
||||
home_key = FeedManager.instance.key(:home, account.id)
|
||||
|
||||
redis.pipelined do
|
||||
target_account.statuses.select('id').find_each do |status|
|
||||
redis.zrem(mentions_key, status.id)
|
||||
redis.zrem(home_key, status.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def clear_notifications(account, target_account)
|
||||
Notification.where(account: account).joins(:follow).where(activity_type: 'Follow', follows: { account_id: target_account.id }).destroy_all
|
||||
Notification.where(account: account).joins(mention: :status).where(activity_type: 'Mention', statuses: { account_id: target_account.id }).destroy_all
|
||||
Notification.where(account: account).joins(:favourite).where(activity_type: 'Favourite', favourites: { account_id: target_account.id }).destroy_all
|
||||
Notification.where(account: account).joins(:status).where(activity_type: 'Status', statuses: { account_id: target_account.id }).destroy_all
|
||||
Notification.where(account: account).joins(:follow).where(activity_type: 'Follow', follows: { account_id: target_account.id }).delete_all
|
||||
Notification.where(account: account).joins(mention: :status).where(activity_type: 'Mention', statuses: { account_id: target_account.id }).delete_all
|
||||
Notification.where(account: account).joins(:favourite).where(activity_type: 'Favourite', favourites: { account_id: target_account.id }).delete_all
|
||||
Notification.where(account: account).joins(:status).where(activity_type: 'Status', statuses: { account_id: target_account.id }).delete_all
|
||||
end
|
||||
|
||||
def redis
|
||||
|
|
|
@ -10,31 +10,6 @@ class AuthorizeFollowService < BaseService
|
|||
private
|
||||
|
||||
def build_xml(follow_request)
|
||||
Nokogiri::XML::Builder.new do |xml|
|
||||
entry(xml, true) do
|
||||
unique_id xml, Time.now.utc, follow_request.id, 'FollowRequest'
|
||||
title xml, "#{follow_request.target_account.acct} authorizes follow request by #{follow_request.account.acct}"
|
||||
|
||||
author(xml) do
|
||||
include_author xml, follow_request.target_account
|
||||
end
|
||||
|
||||
object_type xml, :activity
|
||||
verb xml, :authorize
|
||||
|
||||
target(xml) do
|
||||
author(xml) do
|
||||
include_author xml, follow_request.account
|
||||
end
|
||||
|
||||
object_type xml, :activity
|
||||
verb xml, :request_friend
|
||||
|
||||
target(xml) do
|
||||
include_author xml, follow_request.target_account
|
||||
end
|
||||
end
|
||||
end
|
||||
end.to_xml
|
||||
AtomSerializer.render(AtomSerializer.new.authorize_follow_request_salmon(follow_request))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,22 +18,6 @@ class BlockService < BaseService
|
|||
private
|
||||
|
||||
def build_xml(block)
|
||||
Nokogiri::XML::Builder.new do |xml|
|
||||
entry(xml, true) do
|
||||
unique_id xml, block.created_at, block.id, 'Block'
|
||||
title xml, "#{block.account.acct} no longer wishes to interact with #{block.target_account.acct}"
|
||||
|
||||
author(xml) do
|
||||
include_author xml, block.account
|
||||
end
|
||||
|
||||
object_type xml, :activity
|
||||
verb xml, :block
|
||||
|
||||
target(xml) do
|
||||
include_author xml, block.target_account
|
||||
end
|
||||
end
|
||||
end.to_xml
|
||||
AtomSerializer.render(AtomSerializer.new.block_salmon(block))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
module StreamEntryRenderer
|
||||
def stream_entry_to_xml(stream_entry)
|
||||
renderer = StreamEntriesController.renderer.new(method: 'get', http_host: Rails.configuration.x.local_domain, https: Rails.configuration.x.use_https)
|
||||
renderer.render(:show, assigns: { stream_entry: stream_entry }, formats: [:atom])
|
||||
AtomSerializer.render(AtomSerializer.new.entry(stream_entry, true))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,26 +22,6 @@ class FavouriteService < BaseService
|
|||
private
|
||||
|
||||
def build_xml(favourite)
|
||||
description = "#{favourite.account.acct} favourited a status by #{favourite.status.account.acct}"
|
||||
|
||||
Nokogiri::XML::Builder.new do |xml|
|
||||
entry(xml, true) do
|
||||
unique_id xml, favourite.created_at, favourite.id, 'Favourite'
|
||||
title xml, description
|
||||
content xml, description
|
||||
|
||||
author(xml) do
|
||||
include_author xml, favourite.account
|
||||
end
|
||||
|
||||
object_type xml, :activity
|
||||
verb xml, :favorite
|
||||
in_reply_to xml, TagManager.instance.uri_for(favourite.status), TagManager.instance.url_for(favourite.status)
|
||||
|
||||
target(xml) do
|
||||
include_target xml, favourite.status
|
||||
end
|
||||
end
|
||||
end.to_xml
|
||||
AtomSerializer.render(AtomSerializer.new.favourite_salmon(favourite))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,48 +55,10 @@ class FollowService < BaseService
|
|||
end
|
||||
|
||||
def build_follow_request_xml(follow_request)
|
||||
description = "#{follow_request.account.acct} requested to follow #{follow_request.target_account.acct}"
|
||||
|
||||
Nokogiri::XML::Builder.new do |xml|
|
||||
entry(xml, true) do
|
||||
unique_id xml, follow_request.created_at, follow_request.id, 'FollowRequest'
|
||||
title xml, description
|
||||
content xml, description
|
||||
|
||||
author(xml) do
|
||||
include_author xml, follow_request.account
|
||||
end
|
||||
|
||||
object_type xml, :activity
|
||||
verb xml, :request_friend
|
||||
|
||||
target(xml) do
|
||||
include_author xml, follow_request.target_account
|
||||
end
|
||||
end
|
||||
end.to_xml
|
||||
AtomSerializer.render(AtomSerializer.new.follow_request_salmon(follow_request))
|
||||
end
|
||||
|
||||
def build_follow_xml(follow)
|
||||
description = "#{follow.account.acct} started following #{follow.target_account.acct}"
|
||||
|
||||
Nokogiri::XML::Builder.new do |xml|
|
||||
entry(xml, true) do
|
||||
unique_id xml, follow.created_at, follow.id, 'Follow'
|
||||
title xml, description
|
||||
content xml, description
|
||||
|
||||
author(xml) do
|
||||
include_author xml, follow.account
|
||||
end
|
||||
|
||||
object_type xml, :activity
|
||||
verb xml, :follow
|
||||
|
||||
target(xml) do
|
||||
include_author xml, follow.target_account
|
||||
end
|
||||
end
|
||||
end.to_xml
|
||||
AtomSerializer.render(AtomSerializer.new.follow_salmon(follow))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,31 +10,6 @@ class RejectFollowService < BaseService
|
|||
private
|
||||
|
||||
def build_xml(follow_request)
|
||||
Nokogiri::XML::Builder.new do |xml|
|
||||
entry(xml, true) do
|
||||
unique_id xml, Time.now.utc, follow_request.id, 'FollowRequest'
|
||||
title xml, "#{follow_request.target_account.acct} rejects follow request by #{follow_request.account.acct}"
|
||||
|
||||
author(xml) do
|
||||
include_author xml, follow_request.target_account
|
||||
end
|
||||
|
||||
object_type xml, :activity
|
||||
verb xml, :reject
|
||||
|
||||
target(xml) do
|
||||
author(xml) do
|
||||
include_author xml, follow_request.account
|
||||
end
|
||||
|
||||
object_type xml, :activity
|
||||
verb xml, :request_friend
|
||||
|
||||
target(xml) do
|
||||
include_author xml, follow_request.target_account
|
||||
end
|
||||
end
|
||||
end
|
||||
end.to_xml
|
||||
AtomSerializer.render(AtomSerializer.new.reject_follow_request_salmon(follow_request))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,22 +11,6 @@ class UnblockService < BaseService
|
|||
private
|
||||
|
||||
def build_xml(block)
|
||||
Nokogiri::XML::Builder.new do |xml|
|
||||
entry(xml, true) do
|
||||
unique_id xml, Time.now.utc, block.id, 'Block'
|
||||
title xml, "#{block.account.acct} no longer blocks #{block.target_account.acct}"
|
||||
|
||||
author(xml) do
|
||||
include_author xml, block.account
|
||||
end
|
||||
|
||||
object_type xml, :activity
|
||||
verb xml, :unblock
|
||||
|
||||
target(xml) do
|
||||
include_author xml, block.target_account
|
||||
end
|
||||
end
|
||||
end.to_xml
|
||||
AtomSerializer.render(AtomSerializer.new.unblock_salmon(block))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,26 +13,6 @@ class UnfavouriteService < BaseService
|
|||
private
|
||||
|
||||
def build_xml(favourite)
|
||||
description = "#{favourite.account.acct} no longer favourites a status by #{favourite.status.account.acct}"
|
||||
|
||||
Nokogiri::XML::Builder.new do |xml|
|
||||
entry(xml, true) do
|
||||
unique_id xml, Time.now.utc, favourite.id, 'Favourite'
|
||||
title xml, description
|
||||
content xml, description
|
||||
|
||||
author(xml) do
|
||||
include_author xml, favourite.account
|
||||
end
|
||||
|
||||
object_type xml, :activity
|
||||
verb xml, :unfavorite
|
||||
in_reply_to xml, TagManager.instance.uri_for(favourite.status), TagManager.instance.url_for(favourite.status)
|
||||
|
||||
target(xml) do
|
||||
include_target xml, favourite.status
|
||||
end
|
||||
end
|
||||
end.to_xml
|
||||
AtomSerializer.render(AtomSerializer.new.unfavourite_salmon(favourite))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,25 +13,6 @@ class UnfollowService < BaseService
|
|||
private
|
||||
|
||||
def build_xml(follow)
|
||||
description = "#{follow.account.acct} is no longer following #{follow.target_account.acct}"
|
||||
|
||||
Nokogiri::XML::Builder.new do |xml|
|
||||
entry(xml, true) do
|
||||
unique_id xml, Time.now.utc, follow.id, 'Follow'
|
||||
title xml, description
|
||||
content xml, description
|
||||
|
||||
author(xml) do
|
||||
include_author xml, follow.account
|
||||
end
|
||||
|
||||
object_type xml, :activity
|
||||
verb xml, :unfollow
|
||||
|
||||
target(xml) do
|
||||
include_author xml, follow.target_account
|
||||
end
|
||||
end
|
||||
end.to_xml
|
||||
AtomSerializer.render(AtomSerializer.new.unfollow_salmon(follow))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Nokogiri::XML::Builder.new do |xml|
|
||||
feed(xml) do
|
||||
simple_id xml, account_url(@account, format: 'atom')
|
||||
title xml, @account.display_name
|
||||
subtitle xml, @account.note
|
||||
updated_at xml, stream_updated_at
|
||||
logo xml, full_asset_url(@account.avatar.url(:original))
|
||||
|
||||
author(xml) do
|
||||
include_author xml, @account
|
||||
end
|
||||
|
||||
link_alternate xml, TagManager.instance.url_for(@account)
|
||||
link_self xml, account_url(@account, format: 'atom')
|
||||
link_next xml, account_url(@account, format: 'atom', max_id: @entries.last.id) if @entries.size == 20
|
||||
link_hub xml, api_push_url
|
||||
link_salmon xml, api_salmon_url(@account.id)
|
||||
|
||||
@entries.each do |stream_entry|
|
||||
entry(xml, false) do
|
||||
include_entry xml, stream_entry
|
||||
end
|
||||
end
|
||||
end
|
||||
end.to_xml
|
|
@ -11,8 +11,10 @@
|
|||
%meta{:name => "theme-color", :content => "#282c37"}/
|
||||
%meta{:name => "apple-mobile-web-app-capable", :content => "yes"}/
|
||||
|
||||
%title
|
||||
= "#{yield(:page_title)} - " if content_for?(:page_title)
|
||||
%title<
|
||||
- if content_for?(:page_title)
|
||||
= yield(:page_title)
|
||||
= ' - '
|
||||
= Setting.site_title
|
||||
|
||||
= stylesheet_link_tag 'application', media: 'all'
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
Nokogiri::XML::Builder.new do |xml|
|
||||
entry(xml, true) do
|
||||
author(xml) do
|
||||
include_author xml, @stream_entry.account
|
||||
end
|
||||
|
||||
include_entry xml, @stream_entry
|
||||
end
|
||||
end.to_xml
|
|
@ -13,6 +13,9 @@ class Pubsubhubbub::DeliveryWorker
|
|||
def perform(subscription_id, payload)
|
||||
subscription = Subscription.find(subscription_id)
|
||||
headers = {}
|
||||
host = Addressable::URI.parse(subscription.callback_url).host
|
||||
|
||||
return if DomainBlock.blocked?(host)
|
||||
|
||||
headers['User-Agent'] = 'Mastodon/PubSubHubbub'
|
||||
headers['Link'] = LinkHeader.new([[api_push_url, [%w(rel hub)]], [account_url(subscription.account, format: :atom), [%w(rel self)]]]).to_s
|
||||
|
|
|
@ -11,13 +11,9 @@ class Pubsubhubbub::DistributionWorker
|
|||
return if stream_entry.hidden?
|
||||
|
||||
account = stream_entry.account
|
||||
renderer = AccountsController.renderer.new(method: 'get', http_host: Rails.configuration.x.local_domain, https: Rails.configuration.x.use_https)
|
||||
payload = renderer.render(:show, assigns: { account: account, entries: [stream_entry] }, formats: [:atom])
|
||||
# domains = account.followers_domains
|
||||
payload = AtomSerializer.render(AtomSerializer.new.feed(account, [stream_entry]))
|
||||
|
||||
Subscription.where(account: account).active.select('id, callback_url').find_each do |subscription|
|
||||
host = Addressable::URI.parse(subscription.callback_url).host
|
||||
next if DomainBlock.blocked?(host) # || !domains.include?(host)
|
||||
Pubsubhubbub::DeliveryWorker.perform_async(subscription.id, payload)
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
|
|
|
@ -9,7 +9,7 @@ preload_app!
|
|||
|
||||
on_worker_boot do
|
||||
if ENV['HEROKU'] # Spawn the workers from Puma, to only use one dyno
|
||||
@sidekiq_pid ||= spawn('bundle exec sidekiq -q default -q mailers -q push')
|
||||
@sidekiq_pid ||= spawn('bundle exec sidekiq -q default -q push -q pull -q mailers ')
|
||||
end
|
||||
|
||||
ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
class AddNotificationsAndFavouritesIndices < ActiveRecord::Migration[5.0]
|
||||
def change
|
||||
add_index :notifications, [:activity_id, :activity_type]
|
||||
add_index :accounts, :url
|
||||
add_index :favourites, :status_id
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20170405112956) do
|
||||
ActiveRecord::Schema.define(version: 20170406215816) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -49,6 +49,7 @@ ActiveRecord::Schema.define(version: 20170405112956) do
|
|||
t.integer "following_count", default: 0, null: false
|
||||
t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin
|
||||
t.index "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower", using: :btree
|
||||
t.index ["url"], name: "index_accounts_on_url", using: :btree
|
||||
t.index ["username", "domain"], name: "index_accounts_on_username_and_domain", unique: true, using: :btree
|
||||
end
|
||||
|
||||
|
@ -75,6 +76,7 @@ ActiveRecord::Schema.define(version: 20170405112956) do
|
|||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["account_id", "status_id"], name: "index_favourites_on_account_id_and_status_id", unique: true, using: :btree
|
||||
t.index ["status_id"], name: "index_favourites_on_status_id", using: :btree
|
||||
end
|
||||
|
||||
create_table "follow_requests", force: :cascade do |t|
|
||||
|
@ -128,6 +130,7 @@ ActiveRecord::Schema.define(version: 20170405112956) do
|
|||
t.datetime "updated_at", null: false
|
||||
t.index ["account_id", "status_id"], name: "index_mentions_on_account_id_and_status_id", unique: true, using: :btree
|
||||
t.index ["status_id"], name: "index_mentions_on_status_id", using: :btree
|
||||
t.index ["status_id"], name: "mentions_status_id_index", using: :btree
|
||||
end
|
||||
|
||||
create_table "mutes", force: :cascade do |t|
|
||||
|
@ -146,6 +149,7 @@ ActiveRecord::Schema.define(version: 20170405112956) do
|
|||
t.datetime "updated_at", null: false
|
||||
t.integer "from_account_id"
|
||||
t.index ["account_id", "activity_id", "activity_type"], name: "account_activity", unique: true, using: :btree
|
||||
t.index ["activity_id", "activity_type"], name: "index_notifications_on_activity_id_and_activity_type", using: :btree
|
||||
end
|
||||
|
||||
create_table "oauth_access_grants", force: :cascade do |t|
|
||||
|
|
Reference in New Issue