as well as formatting of contentmaster
parent
735b4cc62e
commit
3cc47beb6e
@ -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 |
||||
|
Loading…
Reference in new issue