as well as formatting of contentclosed-social-glitch-2
@ -1,54 +1,4 @@ | |||||
module ApplicationHelper | 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) | def active_nav_class(path) | ||||
current_page?(path) ? 'active' : '' | current_page?(path) ? 'active' : '' | ||||
end | end | ||||
@ -1,11 +1,15 @@ | |||||
require 'singleton' | |||||
class FeedManager | class FeedManager | ||||
include Singleton | |||||
MAX_ITEMS = 800 | MAX_ITEMS = 800 | ||||
def self.key(type, id) | |||||
def key(type, id) | |||||
"feed:#{type}:#{id}" | "feed:#{type}:#{id}" | ||||
end | end | ||||
def self.filter_status?(status, follower) | |||||
def filter_status?(status, follower) | |||||
replied_to_user = status.reply? ? status.thread.account : nil | replied_to_user = status.reply? ? status.thread.account : nil | ||||
(status.reply? && !(follower.id = replied_to_user.id || follower.following?(replied_to_user))) | (status.reply? && !(follower.id = replied_to_user.id || follower.following?(replied_to_user))) | ||||
end | 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' | require 'rails_helper' | ||||
RSpec.describe ApplicationHelper, type: :helper do | 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 | 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' | require 'rails_helper' | ||||
RSpec.describe MediaAttachment, type: :model do | RSpec.describe MediaAttachment, type: :model do | ||||
pending "add some examples to (or delete) #{__FILE__}" | |||||
end | end |