as well as formatting of contentclosed-social-glitch-2
@ -1,54 +1,4 @@ | |||
module ApplicationHelper | |||
def unique_tag(date, id, type) | |||
"tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}" | |||
end | |||
def unique_tag_to_local_id(tag, expected_type) | |||
matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag) | |||
return matches[1] unless matches.nil? | |||
end | |||
def local_id?(id) | |||
id.start_with?("tag:#{Rails.configuration.x.local_domain}") | |||
end | |||
def content_for_status(actual_status) | |||
if actual_status.local? | |||
linkify(actual_status) | |||
else | |||
sanitize(actual_status.content, tags: %w(a br p), attributes: %w(href rel)) | |||
end | |||
end | |||
def account_from_mentions(search_string, mentions) | |||
mentions.each { |x| return x.account if x.account.acct.eql?(search_string) } | |||
nil | |||
# If that was unsuccessful, try fetching user from db separately | |||
# But this shouldn't ever happen if the mentions were created correctly! | |||
# username, domain = search_string.split('@') | |||
# if domain == Rails.configuration.x.local_domain | |||
# account = Account.find_local(username) | |||
# else | |||
# account = Account.find_remote(username, domain) | |||
# end | |||
# account | |||
end | |||
def linkify(status) | |||
auto_link(HTMLEntities.new.encode(status.text), link: :urls, html: { rel: 'nofollow noopener' }).gsub(Account::MENTION_RE) do |m| | |||
account = account_from_mentions(Account::MENTION_RE.match(m)[1], status.mentions) | |||
unless account.nil? | |||
"#{m.split('@').first}<a href=\"#{url_for_target(account)}\" class=\"mention\">@<span>#{account.acct}</span></a>" | |||
else | |||
m | |||
end | |||
end.html_safe | |||
end | |||
def active_nav_class(path) | |||
current_page?(path) ? 'active' : '' | |||
end | |||
@ -1,11 +1,15 @@ | |||
require 'singleton' | |||
class FeedManager | |||
include Singleton | |||
MAX_ITEMS = 800 | |||
def self.key(type, id) | |||
def key(type, id) | |||
"feed:#{type}:#{id}" | |||
end | |||
def self.filter_status?(status, follower) | |||
def filter_status?(status, follower) | |||
replied_to_user = status.reply? ? status.thread.account : nil | |||
(status.reply? && !(follower.id = replied_to_user.id || follower.following?(replied_to_user))) | |||
end | |||
@ -0,0 +1,47 @@ | |||
require 'singleton' | |||
class Formatter | |||
include Singleton | |||
include ActionView::Helpers::TextHelper | |||
include ActionView::Helpers::SanitizeHelper | |||
def format(status) | |||
return reformat(status) unless status.local? | |||
html = status.text | |||
html = encode(html) | |||
html = link_urls(html) | |||
html = link_mentions(html, status.mentions) | |||
html.html_safe | |||
end | |||
def reformat(status) | |||
sanitize(status.content, tags: %w(a br p), attributes: %w(href rel)) | |||
end | |||
private | |||
def encode(html) | |||
HTMLEntities.new.encode(html) | |||
end | |||
def link_urls(html) | |||
auto_link(html, link: :urls, html: { rel: 'nofollow noopener' }) | |||
end | |||
def link_mentions(html, mentions) | |||
html.gsub(Account::MENTION_RE) do |match| | |||
acct = Account::MENTION_RE.match(match)[1] | |||
mention = mentions.find { |mention| mention.account.acct.eql?(acct) } | |||
return match if mention.nil? | |||
mention_html(match, mention.account) | |||
end | |||
end | |||
def mention_html(match, account) | |||
"#{match.split('@').first}<a href=\"#{TagManager.instance.url_for(account)}\" class=\"mention\">@<span>#{account.acct}</span></a>" | |||
end | |||
end |
@ -0,0 +1,41 @@ | |||
require 'singleton' | |||
class TagManager | |||
include Singleton | |||
include RoutingHelper | |||
def unique_tag(date, id, type) | |||
"tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}" | |||
end | |||
def unique_tag_to_local_id(tag, expected_type) | |||
matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag) | |||
return matches[1] unless matches.nil? | |||
end | |||
def local_id?(id) | |||
id.start_with?("tag:#{Rails.configuration.x.local_domain}") | |||
end | |||
def uri_for(target) | |||
return target.uri if target.respond_to?(:local?) && !target.local? | |||
case target.object_type | |||
when :person | |||
account_url(target) | |||
else | |||
unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type) | |||
end | |||
end | |||
def url_for(target) | |||
return target.url if target.respond_to?(:local?) && !target.local? | |||
case target.object_type | |||
when :person | |||
account_url(target) | |||
else | |||
account_stream_entry_url(target.account, target.stream_entry) | |||
end | |||
end | |||
end |
@ -1,59 +1,5 @@ | |||
require 'rails_helper' | |||
RSpec.describe ApplicationHelper, type: :helper do | |||
let(:local_domain) { Rails.configuration.x.local_domain } | |||
describe '#unique_tag' do | |||
it 'returns a string' do | |||
expect(helper.unique_tag(Time.now, 12, 'Status')).to be_a String | |||
end | |||
end | |||
describe '#unique_tag_to_local_id' do | |||
it 'returns the ID part' do | |||
expect(helper.unique_tag_to_local_id("tag:#{local_domain};objectId=12:objectType=Status", 'Status')).to eql '12' | |||
end | |||
end | |||
describe '#local_id?' do | |||
it 'returns true for a local ID' do | |||
expect(helper.local_id?("tag:#{local_domain};objectId=12:objectType=Status")).to be true | |||
end | |||
it 'returns false for a foreign ID' do | |||
expect(helper.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false | |||
end | |||
end | |||
describe '#linkify' do | |||
let(:alice) { Fabricate(:account, username: 'alice') } | |||
let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com', url: 'http://example.com/bob') } | |||
it 'turns mention of remote user into link' do | |||
status = Fabricate(:status, text: 'Hello @bob@example.com', account: bob) | |||
status.mentions.create(account: bob) | |||
expect(helper.linkify(status)).to match('<a href="http://example.com/bob" class="mention">@<span>bob@example.com</span></a>') | |||
end | |||
it 'turns mention of local user into link' do | |||
status = Fabricate(:status, text: 'Hello @alice', account: bob) | |||
status.mentions.create(account: alice) | |||
expect(helper.linkify(status)).to match('<a href="http://test.host/users/alice" class="mention">@<span>alice</span></a>') | |||
end | |||
it 'leaves mention of unresolvable user alone' do | |||
status = Fabricate(:status, text: 'Hello @foo', account: bob) | |||
expect(helper.linkify(status)).to match('Hello @foo') | |||
end | |||
end | |||
describe '#account_from_mentions' do | |||
let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com') } | |||
let(:status) { Fabricate(:status, text: 'Hello @bob@example.com', account: bob) } | |||
let(:mentions) { [Mention.create(status: status, account: bob)] } | |||
it 'returns account' do | |||
expect(helper.account_from_mentions('bob@example.com', mentions)).to eq bob | |||
end | |||
end | |||
end |
@ -0,0 +1,23 @@ | |||
require 'rails_helper' | |||
RSpec.describe FeedManager do | |||
describe '#key' do | |||
subject { FeedManager.instance.key(:home, 1) } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
describe '#filter_status?' do | |||
let(:followee) { Fabricate(:account, username: 'alice') } | |||
let(:status) { Fabricate(:status, text: 'Hello world', account: followee) } | |||
let(:follower) { Fabricate(:account, username: 'bob') } | |||
subject { FeedManager.instance.filter_status?(status, follower) } | |||
it 'returns a boolean value' do | |||
expect(subject).to be false | |||
end | |||
end | |||
end |
@ -0,0 +1,39 @@ | |||
require 'rails_helper' | |||
RSpec.describe Formatter do | |||
let(:account) { Fabricate(:account, username: 'alice') } | |||
let(:local_status) { Fabricate(:status, text: 'Hello world http://google.com', account: account) } | |||
let(:remote_status) { Fabricate(:status, text: '<script>alert("Hello")</script> Beep boop', uri: 'beepboop', account: account) } | |||
describe '#format' do | |||
subject { Formatter.instance.format(local_status) } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
it 'contains plain text' do | |||
expect(subject).to match('Hello world') | |||
end | |||
it 'contains a link' do | |||
expect(subject).to match('<a rel="nofollow noopener" href="http://google.com">http://google.com</a>') | |||
end | |||
end | |||
describe '#reformat' do | |||
subject { Formatter.instance.format(remote_status) } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
it 'contains plain text' do | |||
expect(subject).to match('Beep boop') | |||
end | |||
it 'does not contain scripts' do | |||
expect(subject).to_not match('<script>alert("Hello")</script>') | |||
end | |||
end | |||
end |
@ -0,0 +1,107 @@ | |||
require 'rails_helper' | |||
RSpec.describe TagManager do | |||
let(:local_domain) { Rails.configuration.x.local_domain } | |||
describe '#unique_tag' do | |||
it 'returns a string' do | |||
expect(TagManager.instance.unique_tag(Time.now, 12, 'Status')).to be_a String | |||
end | |||
end | |||
describe '#unique_tag_to_local_id' do | |||
it 'returns the ID part' do | |||
expect(TagManager.instance.unique_tag_to_local_id("tag:#{local_domain};objectId=12:objectType=Status", 'Status')).to eql '12' | |||
end | |||
end | |||
describe '#local_id?' do | |||
it 'returns true for a local ID' do | |||
expect(TagManager.instance.local_id?("tag:#{local_domain};objectId=12:objectType=Status")).to be true | |||
end | |||
it 'returns false for a foreign ID' do | |||
expect(TagManager.instance.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false | |||
end | |||
end | |||
describe '#uri_for' do | |||
let(:alice) { Fabricate(:account, username: 'alice') } | |||
let(:bob) { Fabricate(:account, username: 'bob') } | |||
let(:status) { Fabricate(:status, text: 'Hello world', account: alice) } | |||
subject { TagManager.instance.uri_for(target) } | |||
context 'Account' do | |||
let(:target) { alice } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
context 'Status' do | |||
let(:target) { status } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
context 'Follow' do | |||
let(:target) { Fabricate(:follow, account: alice, target_account: bob) } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
context 'Favourite' do | |||
let(:target) { Fabricate(:favourite, account: bob, status: status) } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
end | |||
describe '#url_for' do | |||
let(:alice) { Fabricate(:account, username: 'alice') } | |||
let(:bob) { Fabricate(:account, username: 'bob') } | |||
let(:status) { Fabricate(:status, text: 'Hello world', account: alice) } | |||
subject { TagManager.instance.url_for(target) } | |||
context 'Account' do | |||
let(:target) { alice } | |||
it 'returns a URL' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
context 'Status' do | |||
let(:target) { status } | |||
it 'returns a URL' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
context 'Follow' do | |||
let(:target) { Fabricate(:follow, account: alice, target_account: bob) } | |||
it 'returns a URL' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
context 'Favourite' do | |||
let(:target) { Fabricate(:favourite, account: bob, status: status) } | |||
it 'returns a URL' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
end | |||
end |
@ -1,5 +1,5 @@ | |||
require 'rails_helper' | |||
RSpec.describe MediaAttachment, type: :model do | |||
pending "add some examples to (or delete) #{__FILE__}" | |||
end |