Merge pull request #4836 from MrZYX/message_renderer
Introduce message renderer
This commit is contained in:
commit
356096a7eb
40 changed files with 545 additions and 389 deletions
2
Gemfile
2
Gemfile
|
|
@ -68,7 +68,7 @@ gem 'messagebus_ruby_api', '1.0.3'
|
|||
|
||||
gem 'nokogiri', '1.6.1'
|
||||
gem 'rails_autolink', '1.1.5'
|
||||
gem 'redcarpet', '3.0.0'
|
||||
gem 'redcarpet', '3.1.1'
|
||||
gem 'roxml', '3.1.6'
|
||||
gem 'ruby-oembed', '0.8.9'
|
||||
gem 'opengraph_parser', '0.2.3'
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ GEM
|
|||
rb-inotify (0.9.3)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
redcarpet (3.0.0)
|
||||
redcarpet (3.1.1)
|
||||
redis (3.0.6)
|
||||
redis-namespace (1.4.1)
|
||||
redis (~> 3.0.4)
|
||||
|
|
@ -516,7 +516,7 @@ DEPENDENCIES
|
|||
rails_autolink (= 1.1.5)
|
||||
rb-fsevent (= 0.9.4)
|
||||
rb-inotify (= 0.9.3)
|
||||
redcarpet (= 3.0.0)
|
||||
redcarpet (= 3.1.1)
|
||||
remotipart (= 1.2.1)
|
||||
roxml (= 3.1.6)
|
||||
rspec-instafail (= 0.2.4)
|
||||
|
|
|
|||
|
|
@ -98,7 +98,11 @@ class UsersController < ApplicationController
|
|||
if @user = User.find_by_username(params[:username])
|
||||
respond_to do |format|
|
||||
format.atom do
|
||||
@posts = Post.where(:author_id => @user.person_id, :public => true).order('created_at DESC').limit(25)
|
||||
@posts = Post.where(author_id: @user.person_id, public: true)
|
||||
.order('created_at DESC')
|
||||
.limit(25)
|
||||
.map {|post| post.is_a?(Reshare) ? post.absolute_root : post }
|
||||
.compact
|
||||
end
|
||||
|
||||
format.any { redirect_to person_path(@user.person) }
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
# Copyright (c) 2010-2011, Diaspora Inc. This file is
|
||||
# licensed under the Affero General Public License version 3 or later. See
|
||||
# the COPYRIGHT file.
|
||||
|
||||
module MarkdownifyHelper
|
||||
|
||||
def markdown_options
|
||||
{
|
||||
:autolink => true,
|
||||
:fenced_code_blocks => true,
|
||||
:space_after_headers => true,
|
||||
:strikethrough => true,
|
||||
:tables => true,
|
||||
:no_intra_emphasis => true,
|
||||
}
|
||||
end
|
||||
|
||||
def markdownify(target, render_options={})
|
||||
|
||||
render_options[:filter_html] = true
|
||||
render_options[:hard_wrap] ||= true
|
||||
render_options[:safe_links_only] = true
|
||||
|
||||
# This ugly little hack basically means
|
||||
# "Give me the rawest contents of target available"
|
||||
if target.respond_to?(:raw_message)
|
||||
message = target.raw_message
|
||||
elsif target.respond_to?(:text)
|
||||
message = target.text
|
||||
else
|
||||
message = target.to_s
|
||||
end
|
||||
|
||||
return '' if message.blank?
|
||||
|
||||
renderer = Diaspora::Markdownify::HTML.new(render_options)
|
||||
markdown = Redcarpet::Markdown.new(renderer, markdown_options)
|
||||
|
||||
message = markdown.render(message).html_safe
|
||||
|
||||
if target.respond_to?(:mentioned_people)
|
||||
message = Diaspora::Mentionable.format(message, target.mentioned_people)
|
||||
end
|
||||
|
||||
message = Diaspora::Taggable.format_tags(message, :no_escape => true)
|
||||
|
||||
return message.html_safe
|
||||
end
|
||||
|
||||
def strip_markdown(text)
|
||||
renderer = Redcarpet::Markdown.new(Redcarpet::Render::StripDown, markdown_options)
|
||||
renderer.render(text).strip
|
||||
end
|
||||
|
||||
def process_newlines(message)
|
||||
# in very clear cases, let newlines become <br /> tags
|
||||
# Grabbed from Github flavored Markdown
|
||||
message.gsub(/^[\w\<][^\n]*\n+/) do |x|
|
||||
x =~ /\n{2}/ ? x : (x.strip!; x << " \n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,28 +1,20 @@
|
|||
module NotifierHelper
|
||||
|
||||
# @param post [Post] The post object.
|
||||
# @param opts [Hash] Optional hash. Accepts :length and :process_newlines parameters.
|
||||
# @param opts [Hash] Optional hash. Accepts :length parameters.
|
||||
# @return [String] The truncated and formatted post.
|
||||
def post_message(post, opts={})
|
||||
opts[:length] ||= 200
|
||||
if post.respond_to? :formatted_message
|
||||
message = strip_markdown(post.formatted_message(:plain_text => true))
|
||||
message = truncate(message, :length => opts[:length])
|
||||
message = process_newlines(message) if opts[:process_newlines]
|
||||
message
|
||||
if post.respond_to? :message
|
||||
post.message.plain_text_without_markdown truncate: opts.fetch(:length, 200)
|
||||
else
|
||||
I18n.translate 'notifier.a_post_you_shared'
|
||||
end
|
||||
end
|
||||
|
||||
# @param comment [Comment] The comment to process.
|
||||
# @param opts [Hash] Optional hash. Accepts :length and :process_newlines parameters.
|
||||
# @param opts [Hash] Optional hash. Accepts :length parameters.
|
||||
# @return [String] The truncated and formatted comment.
|
||||
def comment_message(comment, opts={})
|
||||
opts[:length] ||= 600
|
||||
text = strip_markdown(comment.text)
|
||||
text = truncate(text, :length => opts[:length])
|
||||
text = process_newlines(text) if opts[:process_newlines]
|
||||
text
|
||||
comment.message.plain_text_without_markdown truncate: opts.fetch(:length, 600)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,19 +9,8 @@ module PostsHelper
|
|||
elsif post.is_a?(Reshare)
|
||||
I18n.t "posts.show.reshare_by", :author => post.author_name
|
||||
else
|
||||
if post.text.present?
|
||||
if opts.has_key?(:length)
|
||||
truncate(post.text(:plain_text => true), :length => opts.fetch(:length))
|
||||
elsif /\A(?: # Regexp to match a Markdown header present on first line :
|
||||
(?<setext_content>.{1,200}\n(?:={1,200}|-{1,200}))(?:\r?\n|$) # Setext-style header
|
||||
| # or
|
||||
(?<atx_content>\#{1,6}\s.{1,200})(?:\r?\n|$) # Atx-style header
|
||||
)/x =~ post.text(:plain_text => true)
|
||||
return setext_content unless setext_content.nil?
|
||||
return atx_content unless atx_content.nil?
|
||||
else
|
||||
truncate(post.text(:plain_text => true), :length => 20 )
|
||||
end
|
||||
if post.message.present?
|
||||
post.message.title opts
|
||||
elsif post.respond_to?(:photos) && post.photos.present?
|
||||
I18n.t "posts.show.photos_by", :count => post.photos.size, :author => post.author_name
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
module NotificationMailers
|
||||
class AlsoCommented < NotificationMailers::Base
|
||||
include ActionView::Helpers::TextHelper
|
||||
include MarkdownifyHelper
|
||||
|
||||
attr_accessor :comment
|
||||
delegate :post, to: :comment, prefix: true
|
||||
|
||||
|
|
@ -11,8 +8,7 @@ module NotificationMailers
|
|||
|
||||
if mail?
|
||||
@headers[:from] = "\"#{@comment.author_name} (diaspora*)\" <#{AppConfig.mail.sender_address}>"
|
||||
@headers[:subject] = truncate(strip_markdown(@comment.comment_email_subject.squish), :length => TRUNCATION_LEN)
|
||||
@headers[:subject] = "Re: #{@headers[:subject]}"
|
||||
@headers[:subject] = "Re: #{@comment.comment_email_subject}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
module NotificationMailers
|
||||
TRUNCATION_LEN = 70
|
||||
|
||||
class Base
|
||||
attr_accessor :recipient, :sender
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
module NotificationMailers
|
||||
class CommentOnPost < NotificationMailers::Base
|
||||
include ActionView::Helpers::TextHelper
|
||||
include MarkdownifyHelper
|
||||
|
||||
attr_accessor :comment
|
||||
|
||||
def set_headers(comment_id)
|
||||
@comment = Comment.find(comment_id)
|
||||
|
||||
@headers[:from] = "\"#{@comment.author_name} (diaspora*)\" <#{AppConfig.mail.sender_address}>"
|
||||
@headers[:subject] = truncate(strip_markdown(@comment.comment_email_subject.squish), :length => TRUNCATION_LEN)
|
||||
@headers[:subject] = "Re: #{@headers[:subject]}"
|
||||
@headers[:subject] = "Re: #{@comment.comment_email_subject}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
class Notifier < ActionMailer::Base
|
||||
helper :application
|
||||
helper :markdownify
|
||||
helper :notifier
|
||||
helper :people
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,10 @@ class Comment < ActiveRecord::Base
|
|||
self.post = parent
|
||||
end
|
||||
|
||||
def message
|
||||
@message ||= Diaspora::MessageRenderer.new text
|
||||
end
|
||||
|
||||
def text= text
|
||||
self[:text] = text.to_s.strip #to_s if for nil, for whatever reason
|
||||
end
|
||||
|
|
|
|||
|
|
@ -71,8 +71,8 @@ class Message < ActiveRecord::Base
|
|||
Notifications::PrivateMessage unless user.person == person
|
||||
end
|
||||
|
||||
def formatted_message(opts={})
|
||||
opts[:plain_text] ? self.text: ERB::Util.h(self.text)
|
||||
def message
|
||||
@message ||= Diaspora::MessageRenderer.new text
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -132,6 +132,13 @@ class Profile < ActiveRecord::Base
|
|||
birthday.to_s(:long).gsub(', 1000', '') if birthday.present?
|
||||
end
|
||||
|
||||
def bio_message
|
||||
@bio_message ||= Diaspora::MessageRenderer.new(bio)
|
||||
end
|
||||
|
||||
def location_message
|
||||
@location_message ||= Diaspora::MessageRenderer.new(location)
|
||||
end
|
||||
|
||||
def tag_string
|
||||
if @tag_string
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ class Reshare < Post
|
|||
self.root ? root.raw_message : super
|
||||
end
|
||||
|
||||
def message
|
||||
absolute_root.message if root.present?
|
||||
end
|
||||
|
||||
def mentioned_people
|
||||
self.root ? root.mentioned_people : super
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@
|
|||
# the COPYRIGHT file.
|
||||
|
||||
class Service < ActiveRecord::Base
|
||||
include ActionView::Helpers::TextHelper
|
||||
include MarkdownifyHelper
|
||||
|
||||
attr_accessor :provider, :info, :access_level
|
||||
|
||||
belongs_to :user
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
class Services::Facebook < Service
|
||||
include Rails.application.routes.url_helpers
|
||||
include MarkdownifyHelper
|
||||
|
||||
OVERRIDE_FIELDS_ON_FB_UPDATE = [:contact_id, :person_id, :request_id, :invitation_id, :photo_url, :name, :username]
|
||||
MAX_CHARACTERS = 63206
|
||||
|
|
@ -22,11 +21,16 @@ class Services::Facebook < Service
|
|||
end
|
||||
|
||||
def create_post_params(post)
|
||||
message = strip_markdown(post.text(:plain_text => true))
|
||||
message = post.message.plain_text_without_markdown
|
||||
if post.photos.any?
|
||||
message += " " + Rails.application.routes.url_helpers.short_post_url(post, :protocol => AppConfig.pod_uri.scheme, :host => AppConfig.pod_uri.authority)
|
||||
message += " " + short_post_url(post, protocol: AppConfig.pod_uri.scheme,
|
||||
host: AppConfig.pod_uri.authority)
|
||||
end
|
||||
{:message => message, :access_token => self.access_token, :link => URI.extract(message, ['https', 'http']).first}
|
||||
|
||||
{message: message,
|
||||
access_token: access_token,
|
||||
link: URI.extract(message, ['https', 'http']).first
|
||||
}
|
||||
end
|
||||
|
||||
def profile_photo_url
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
class Services::Tumblr < Service
|
||||
include ActionView::Helpers::TextHelper
|
||||
include ActionView::Helpers::TagHelper
|
||||
|
||||
MAX_CHARACTERS = 1000
|
||||
|
||||
def provider
|
||||
|
|
@ -38,10 +35,10 @@ class Services::Tumblr < Service
|
|||
def tumblr_template(post, url)
|
||||
html = ''
|
||||
post.photos.each do |photo|
|
||||
html += "})\n\n"
|
||||
html << "})\n\n"
|
||||
end
|
||||
html += post.text
|
||||
html += "\n\n[original post](#{url})"
|
||||
html << post.message.html(mentioned_people: [])
|
||||
html << "\n\n[original post](#{url})"
|
||||
end
|
||||
|
||||
def delete_post(post)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
class Services::Twitter < Service
|
||||
include ActionView::Helpers::TextHelper
|
||||
include Rails.application.routes.url_helpers
|
||||
include MarkdownifyHelper
|
||||
|
||||
MAX_CHARACTERS = 140
|
||||
SHORTENED_URL_LENGTH = 21
|
||||
|
|
@ -40,7 +38,7 @@ class Services::Twitter < Service
|
|||
|
||||
def attempt_post post, retry_count=0
|
||||
message = build_twitter_post post, retry_count
|
||||
tweet = client.update message
|
||||
client.update message
|
||||
rescue Twitter::Error::Forbidden => e
|
||||
if ! e.message.include? 'is over 140' || retry_count == 20
|
||||
raise e
|
||||
|
|
@ -52,7 +50,7 @@ class Services::Twitter < Service
|
|||
def build_twitter_post post, retry_count=0
|
||||
max_characters = MAX_CHARACTERS - retry_count
|
||||
|
||||
post_text = strip_markdown post.text(plain_text: true)
|
||||
post_text = post.message.plain_text_without_markdown
|
||||
truncate_and_add_post_link post, post_text, max_characters
|
||||
end
|
||||
|
||||
|
|
@ -65,7 +63,7 @@ class Services::Twitter < Service
|
|||
host: AppConfig.pod_uri.authority
|
||||
)
|
||||
|
||||
truncated_text = truncate post_text, length: max_characters - SHORTENED_URL_LENGTH + 1
|
||||
truncated_text = post_text.truncate max_characters - SHORTENED_URL_LENGTH + 1
|
||||
truncated_text = restore_truncated_url truncated_text, post_text, max_characters
|
||||
|
||||
"#{truncated_text} #{post_url}"
|
||||
|
|
@ -95,11 +93,9 @@ class Services::Twitter < Service
|
|||
return truncated_text if truncated_text !~ /#{LINK_PATTERN}\Z/
|
||||
|
||||
url = post_text.match(LINK_PATTERN, truncated_text.rindex('http'))[0]
|
||||
truncated_text = truncate(
|
||||
post_text,
|
||||
length: max_characters - SHORTENED_URL_LENGTH + 2,
|
||||
separator: ' ',
|
||||
omission: ''
|
||||
truncated_text = post_text.truncate(
|
||||
max_characters - SHORTENED_URL_LENGTH + 2,
|
||||
separator: ' ', omission: ''
|
||||
)
|
||||
|
||||
"#{truncated_text} #{url} ..."
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
class Services::Wordpress < Service
|
||||
include ActionView::Helpers::TextHelper
|
||||
include MarkdownifyHelper
|
||||
|
||||
MAX_CHARACTERS = 1000
|
||||
|
||||
attr_accessor :username, :password, :host, :path
|
||||
|
|
@ -12,21 +9,22 @@ class Services::Wordpress < Service
|
|||
"wordpress"
|
||||
end
|
||||
|
||||
def post(post, url='')
|
||||
res = Faraday.new(:url => "https://public-api.wordpress.com").post do |req|
|
||||
def post post, url=''
|
||||
res = Faraday.new(url: "https://public-api.wordpress.com").post do |req|
|
||||
req.url "/rest/v1/sites/#{self.uid}/posts/new"
|
||||
req.body = post_body(post).to_json
|
||||
req.headers['Authorization'] = "Bearer #{self.access_token}"
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
end
|
||||
|
||||
JSON.parse res.env[:body]
|
||||
end
|
||||
|
||||
def post_body(post, url='')
|
||||
post_text = markdownify(post.text)
|
||||
post_title = truncate(strip_markdown(post.text(:plain_text => true)), :length => 40, :separator => ' ')
|
||||
|
||||
{:title => post_title, :content => post_text.html_safe}
|
||||
def post_body post
|
||||
{
|
||||
title: post.message.title(length: 40),
|
||||
content: post.message.markdownified(disable_hovercards: true)
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
class StatusMessage < Post
|
||||
include Diaspora::Taggable
|
||||
|
||||
include ActionView::Helpers::TextHelper
|
||||
include PeopleHelper
|
||||
|
||||
acts_as_taggable_on :tags
|
||||
|
|
@ -59,10 +58,6 @@ class StatusMessage < Post
|
|||
tag_stream(tag_ids)
|
||||
end
|
||||
|
||||
def text(opts = {})
|
||||
self.formatted_message(opts)
|
||||
end
|
||||
|
||||
def raw_message
|
||||
read_attribute(:text)
|
||||
end
|
||||
|
|
@ -80,12 +75,8 @@ class StatusMessage < Post
|
|||
self.raw_message.match(/#nsfw/i) || super
|
||||
end
|
||||
|
||||
def formatted_message(opts={})
|
||||
return self.raw_message unless self.raw_message
|
||||
|
||||
escaped_message = opts[:plain_text] ? self.raw_message : ERB::Util.h(self.raw_message)
|
||||
mentioned_message = Diaspora::Mentionable.format(escaped_message, self.mentioned_people, opts)
|
||||
Diaspora::Taggable.format_tags(mentioned_message, opts.merge(:no_escape => true))
|
||||
def message
|
||||
@message ||= Diaspora::MessageRenderer.new raw_message, mentioned_people: mentioned_people
|
||||
end
|
||||
|
||||
def mentioned_people
|
||||
|
|
@ -137,7 +128,7 @@ class StatusMessage < Post
|
|||
end
|
||||
|
||||
def comment_email_subject
|
||||
formatted_message(:plain_text => true)
|
||||
message.title length: 70
|
||||
end
|
||||
|
||||
def first_photo_url(*args)
|
||||
|
|
@ -145,7 +136,7 @@ class StatusMessage < Post
|
|||
end
|
||||
|
||||
def text_and_photos_blank?
|
||||
self.text.blank? && self.photos.blank?
|
||||
self.raw_message.blank? && self.photos.blank?
|
||||
end
|
||||
|
||||
def queue_gather_oembed_data
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
class OEmbedPresenter
|
||||
include PostsHelper
|
||||
include ActionView::Helpers::TextHelper
|
||||
|
||||
def initialize(post, opts = {})
|
||||
@post = post
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
class PostPresenter
|
||||
include PostsHelper
|
||||
include ActionView::Helpers::TextHelper
|
||||
|
||||
attr_accessor :post, :current_user
|
||||
|
||||
|
|
@ -49,7 +48,7 @@ class PostPresenter
|
|||
end
|
||||
|
||||
def title
|
||||
@post.text.present? ? post_page_title(@post) : I18n.translate('posts.presenter.title', :name => @post.author_name)
|
||||
@post.message.present? ? @post.message.title : I18n.t('posts.presenter.title', name: @post.author_name)
|
||||
end
|
||||
|
||||
def root
|
||||
|
|
|
|||
|
|
@ -16,4 +16,4 @@
|
|||
= timeago(comment.created_at ? comment.created_at : Time.now)
|
||||
|
||||
%div{:class => direction_for(comment.text)}
|
||||
= markdownify(comment)
|
||||
= comment.message.markdownified
|
||||
|
|
|
|||
|
|
@ -11,4 +11,4 @@
|
|||
= timeago(message.created_at)
|
||||
|
||||
%div{ :class => direction_for(message.text) }
|
||||
= markdownify(message, :oembed => true)
|
||||
= message.message.markdownified
|
||||
|
|
|
|||
|
|
@ -37,13 +37,13 @@
|
|||
%h4
|
||||
=t('.bio')
|
||||
%div{ :class => direction_for(person.bio) }
|
||||
= markdownify(person.profile.bio, :oembed => true, :newlines => true)
|
||||
= person.profile.bio_message.markdownified
|
||||
- unless person.profile.location.blank?
|
||||
%li
|
||||
%h4
|
||||
=t('.location')
|
||||
%div{ :class => direction_for(person.location) }
|
||||
= markdownify(person.location, :oembed => true, :newlines => true)
|
||||
= person.profile.location_message.markdownified
|
||||
|
||||
- unless person.gender.blank?
|
||||
%li
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
= image_tag post.image_url
|
||||
|
||||
%div{:class => direction_for(post.text)}
|
||||
!= markdownify(post)
|
||||
!= post.message.markdownified
|
||||
- if post.o_embed_cache
|
||||
!= o_embed_html post.o_embed_cache
|
||||
-if post.open_graph_cache
|
||||
|
|
|
|||
|
|
@ -27,12 +27,11 @@ atom_feed({'xmlns:thr' => 'http://purl.org/syndication/thread/1.0',
|
|||
end
|
||||
|
||||
@posts.each do |post|
|
||||
post = post.absolute_root unless post.absolute_root.nil? if post.is_a? Reshare
|
||||
feed.entry post, :url => "#{@user.url}p/#{post.id}",
|
||||
:id => "#{@user.url}p/#{post.id}" do |entry|
|
||||
|
||||
entry.title post_page_title(post)
|
||||
entry.content markdownify(post), :type => 'html'
|
||||
entry.title post.message.title
|
||||
entry.content post.message.markdownified(disable_hovercards: true), :type => 'html'
|
||||
entry.tag! 'activity:verb', 'http://activitystrea.ms/schema/1.0/post'
|
||||
entry.tag! 'activity:object-type', 'http://activitystrea.ms/schema/1.0/note'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,9 +13,6 @@ require 'typhoeus'
|
|||
# Presenters
|
||||
require 'post_presenter'
|
||||
|
||||
# Helpers
|
||||
require 'markdownify_helper'
|
||||
|
||||
# Our libs
|
||||
require 'collect_user_photos'
|
||||
require 'diaspora'
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ module Diaspora
|
|||
require 'diaspora/parser'
|
||||
require 'diaspora/fetcher'
|
||||
require 'diaspora/markdownify'
|
||||
require 'diaspora/message_renderer'
|
||||
require 'diaspora/mentionable'
|
||||
require 'diaspora/exporter'
|
||||
require 'diaspora/federated'
|
||||
|
|
|
|||
|
|
@ -2,12 +2,10 @@ module Diaspora
|
|||
module Markdownify
|
||||
class HTML < Redcarpet::Render::HTML
|
||||
include ActionView::Helpers::TextHelper
|
||||
include ActionView::Helpers::TagHelper
|
||||
|
||||
def autolink(link, type)
|
||||
auto_link(link, :link => :urls, :html => { :target => "_blank" })
|
||||
end
|
||||
|
||||
def autolink link, type
|
||||
auto_link(link, link: :urls, html: { target: "_blank" })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
233
lib/diaspora/message_renderer.rb
Normal file
233
lib/diaspora/message_renderer.rb
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
module Diaspora
|
||||
# Takes a raw message text and converts it to
|
||||
# various desired target formats, respecting
|
||||
# all possible formatting options supported
|
||||
# by Diaspora
|
||||
class MessageRenderer
|
||||
class Processor
|
||||
class << self
|
||||
private :new
|
||||
|
||||
def process message, options, &block
|
||||
return '' if message.blank? # Optimize for empty message
|
||||
processor = new message, options
|
||||
processor.instance_exec(&block)
|
||||
processor.message
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :message, :options
|
||||
|
||||
def initialize message, options
|
||||
@message = message
|
||||
@options = options
|
||||
end
|
||||
|
||||
def squish
|
||||
@message = message.squish if options[:squish]
|
||||
end
|
||||
|
||||
def append_and_truncate
|
||||
if options[:truncate]
|
||||
@message = message.truncate options[:truncate]-options[:append].to_s.size
|
||||
end
|
||||
|
||||
message << options[:append].to_s
|
||||
message << options[:append_after_truncate].to_s
|
||||
end
|
||||
|
||||
include ActionView::Helpers::TagHelper
|
||||
def escape
|
||||
if options[:escape]
|
||||
# TODO: On Rails 4 port change this to ERB::Util.html_escape_once
|
||||
# and remove the include
|
||||
@message = escape_once message
|
||||
|
||||
# Special case Hex entities since escape_once
|
||||
# doesn't catch them.
|
||||
# TODO: Watch for https://github.com/rails/rails/pull/9102
|
||||
# on whether this can be removed
|
||||
@message = message.gsub(/&(#[xX][\dA-Fa-f]{1,4});/, '&\1;')
|
||||
end
|
||||
end
|
||||
|
||||
def strip_markdown
|
||||
renderer = Redcarpet::Markdown.new Redcarpet::Render::StripDown, options[:markdown_options]
|
||||
@message = renderer.render(message).strip
|
||||
end
|
||||
|
||||
def markdownify
|
||||
renderer = Diaspora::Markdownify::HTML.new options[:markdown_render_options]
|
||||
markdown = Redcarpet::Markdown.new renderer, options[:markdown_options]
|
||||
|
||||
@message = markdown.render message
|
||||
end
|
||||
|
||||
# In very clear cases, let newlines become <br /> tags
|
||||
# Grabbed from Github flavored Markdown
|
||||
def process_newlines
|
||||
message.gsub(/^[\w\<][^\n]*\n+/) do |x|
|
||||
x =~ /\n{2}/ ? x : (x.strip!; x << " \n")
|
||||
end
|
||||
end
|
||||
|
||||
def render_mentions
|
||||
unless options[:disable_hovercards] || options[:mentioned_people].empty?
|
||||
@message = Diaspora::Mentionable.format message, options[:mentioned_people]
|
||||
end
|
||||
|
||||
if options[:disable_hovercards] || options[:link_all_mentions]
|
||||
@message = Diaspora::Mentionable.filter_for_aspects message, nil
|
||||
else
|
||||
make_mentions_plain_text
|
||||
end
|
||||
end
|
||||
|
||||
def make_mentions_plain_text
|
||||
@message = Diaspora::Mentionable.format message, [], plain_text: true
|
||||
end
|
||||
|
||||
def render_tags
|
||||
@message = Diaspora::Taggable.format_tags message, no_escape: !options[:escape_tags]
|
||||
end
|
||||
end
|
||||
|
||||
DEFAULTS = {mentioned_people: [],
|
||||
link_all_mentions: false,
|
||||
disable_hovercards: false,
|
||||
truncate: false,
|
||||
append: nil,
|
||||
append_after_truncate: nil,
|
||||
squish: false,
|
||||
escape: true,
|
||||
escape_tags: false,
|
||||
markdown_options: {
|
||||
autolink: true,
|
||||
fenced_code_blocks: true,
|
||||
space_after_headers: true,
|
||||
strikethrough: true,
|
||||
tables: true,
|
||||
no_intra_emphasis: true,
|
||||
},
|
||||
markdown_render_options: {
|
||||
filter_html: true,
|
||||
hard_wrap: true,
|
||||
safe_links_only: true
|
||||
}}.freeze
|
||||
|
||||
delegate :empty?, :blank?, :present?, to: :raw
|
||||
|
||||
# @param [String] raw_message Raw input text
|
||||
# @param [Hash] opts Global options affecting output
|
||||
# @option opts [Array<Person>] :mentioned_people ([]) List of people
|
||||
# allowed to mention
|
||||
# @option opts [Boolean] :link_all_mentions (false) Whether to link
|
||||
# all mentions. This makes plain links to profiles for people not in
|
||||
# :mentioned_people
|
||||
# @option opts [Boolean] :disable_hovercards (true) Render all mentions
|
||||
# as profile links. This implies :link_all_mentions and ignores
|
||||
# :mentioned_people
|
||||
# @option opts [#to_i, Boolean] :truncate (false) Truncate message to
|
||||
# the specified length
|
||||
# @option opts [String] :append (nil) Append text to the end of
|
||||
# the (truncated) message, counts into truncation length
|
||||
# @option opts [String] :append_after_truncate (nil) Append text to the end
|
||||
# of the (truncated) message, doesn't count into the truncation length
|
||||
# @option opts [Boolean] :squish (false) Squish the message, that is
|
||||
# remove all surrounding and consecutive whitespace
|
||||
# @option opts [Boolean] :escape (true) Escape HTML relevant characters
|
||||
# in the message. Note that his option is ignored in the plaintext
|
||||
# renderers.
|
||||
# @option opts [Boolean] :escape_tags (false) Escape HTML relevant
|
||||
# characters in tags when rendering them
|
||||
# @option opts [Hash] :markdown_options Override default options passed
|
||||
# to Redcarpet
|
||||
# @option opts [Hash] :markdown_render_options Override default options
|
||||
# passed to the Redcarpet renderer
|
||||
def initialize raw_message, opts={}
|
||||
@raw_message = raw_message
|
||||
@options = DEFAULTS.deep_merge opts
|
||||
end
|
||||
|
||||
# @param [Hash] opts Override global output options, see {#initialize}
|
||||
def plain_text opts={}
|
||||
process(opts) {
|
||||
make_mentions_plain_text
|
||||
squish
|
||||
append_and_truncate
|
||||
}
|
||||
end
|
||||
|
||||
# @param [Hash] opts Override global output options, see {#initialize}
|
||||
def plain_text_without_markdown opts={}
|
||||
process(opts) {
|
||||
make_mentions_plain_text
|
||||
strip_markdown
|
||||
squish
|
||||
append_and_truncate
|
||||
}
|
||||
end
|
||||
|
||||
# @param [Hash] opts Override global output options, see {#initialize}
|
||||
def html opts={}
|
||||
process(opts) {
|
||||
escape
|
||||
render_mentions
|
||||
render_tags
|
||||
squish
|
||||
append_and_truncate
|
||||
}.html_safe
|
||||
end
|
||||
|
||||
# @param [Hash] opts Override global output options, see {#initialize}
|
||||
def markdownified opts={}
|
||||
process(opts) {
|
||||
process_newlines
|
||||
markdownify
|
||||
render_mentions
|
||||
render_tags
|
||||
squish
|
||||
append_and_truncate
|
||||
}.html_safe
|
||||
end
|
||||
|
||||
# Get a short summary of the message
|
||||
# @param [Hash] opts Additional options
|
||||
# @option opts [Integer] :length (20 | first heading) Truncate the title to
|
||||
# this length. If not given defaults to 20 and to not truncate
|
||||
# if a heading is found.
|
||||
def title opts={}
|
||||
# Setext-style header
|
||||
heading = if /\A(?<setext_content>.{1,200})\n(?:={1,200}|-{1,200})(?:\r?\n|$)/ =~ @raw_message.lstrip
|
||||
setext_content
|
||||
# Atx-style header
|
||||
elsif /\A\#{1,6}\s+(?<atx_content>.{1,200}?)(?:\s+#+)?(?:\r?\n|$)/ =~ @raw_message.lstrip
|
||||
atx_content
|
||||
end
|
||||
|
||||
heading &&= heading.strip
|
||||
|
||||
if heading && opts[:length]
|
||||
heading.truncate opts[:length]
|
||||
elsif heading
|
||||
heading
|
||||
else
|
||||
plain_text_without_markdown squish: true, truncate: opts.fetch(:length, 20)
|
||||
end
|
||||
end
|
||||
|
||||
def raw
|
||||
@raw_message
|
||||
end
|
||||
|
||||
def to_s
|
||||
plain_text
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process opts, &block
|
||||
Processor.process(@raw_message, @options.deep_merge(opts), &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
# Copyright (c) 2010-2011, Diaspora Inc. This file is
|
||||
# licensed under the Affero General Public License version 3 or later. See
|
||||
# the COPYRIGHT file.
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe MarkdownifyHelper do
|
||||
describe "#markdownify" do
|
||||
describe "not doing something dumb" do
|
||||
it "strips out script tags" do
|
||||
markdownify("<script>alert('XSS is evil')</script>").should ==
|
||||
"<p>alert('XSS is evil')</p>\n"
|
||||
end
|
||||
|
||||
it 'strips onClick handlers from links' do
|
||||
omghax = '[XSS](http://joindiaspora.com/" onClick="$\(\'a\'\).remove\(\))'
|
||||
markdownify(omghax).should_not match(/ onClick/i)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not barf if message is nil' do
|
||||
markdownify(nil).should == ''
|
||||
end
|
||||
|
||||
it 'autolinks standard url links' do
|
||||
markdownified = markdownify("http://joindiaspora.com/")
|
||||
|
||||
doc = Nokogiri.parse(markdownified)
|
||||
|
||||
link = doc.css("a")
|
||||
|
||||
link.attr("href").value.should == "http://joindiaspora.com/"
|
||||
end
|
||||
|
||||
context 'when formatting status messages' do
|
||||
it "should leave tags intact" do
|
||||
message = FactoryGirl.create(:status_message,
|
||||
:author => alice.person,
|
||||
:text => "I love #markdown")
|
||||
formatted = markdownify(message)
|
||||
formatted.should =~ %r{<a href="/tags/markdown" class="tag">#markdown</a>}
|
||||
end
|
||||
|
||||
it 'should leave multi-underscore tags intact' do
|
||||
message = FactoryGirl.create(
|
||||
:status_message,
|
||||
:author => alice.person,
|
||||
:text => "Here is a #multi_word tag"
|
||||
)
|
||||
formatted = markdownify(message)
|
||||
formatted.should =~ %r{Here is a <a href="/tags/multi_word" class="tag">#multi_word</a> tag}
|
||||
|
||||
message = FactoryGirl.create(
|
||||
:status_message,
|
||||
:author => alice.person,
|
||||
:text => "Here is a #multi_word_tag yo"
|
||||
)
|
||||
formatted = markdownify(message)
|
||||
formatted.should =~ %r{Here is a <a href="/tags/multi_word_tag" class="tag">#multi_word_tag</a> yo}
|
||||
end
|
||||
|
||||
it "should leave mentions intact" do
|
||||
message = FactoryGirl.create(:status_message,
|
||||
:author => alice.person,
|
||||
:text => "Hey @{Bob; #{bob.diaspora_handle}}!")
|
||||
formatted = markdownify(message)
|
||||
formatted.should =~ /hovercard/
|
||||
end
|
||||
|
||||
it "should leave mentions intact for real diaspora handles" do
|
||||
new_person = FactoryGirl.create(:person, :diaspora_handle => 'maxwell@joindiaspora.com')
|
||||
message = FactoryGirl.create(:status_message,
|
||||
:author => alice.person,
|
||||
:text => "Hey @{maxwell@joindiaspora.com; #{new_person.diaspora_handle}}!")
|
||||
formatted = markdownify(message)
|
||||
formatted.should =~ /hovercard/
|
||||
end
|
||||
|
||||
it 'should process text with both a hashtag and a link' do
|
||||
message = FactoryGirl.create(:status_message,
|
||||
:author => alice.person,
|
||||
:text => "Test #tag?\nhttps://joindiaspora.com\n")
|
||||
formatted = markdownify(message)
|
||||
formatted.should == %{<p>Test <a href="/tags/tag" class="tag">#tag</a>?<br>\n<a href="https://joindiaspora.com" target="_blank">https://joindiaspora.com</a></p>\n}
|
||||
end
|
||||
|
||||
it 'should process text with a header' do
|
||||
message = "# I love markdown"
|
||||
markdownify(message).should match "I love markdown"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#strip_markdown" do
|
||||
it 'does not remove markdown in links' do
|
||||
message = "some text and here comes http://exampe.org/foo_bar_baz a link"
|
||||
strip_markdown(message).should match message
|
||||
end
|
||||
|
||||
it 'does not destroy hashtag that starts a line' do
|
||||
message = "#hashtag message"
|
||||
strip_markdown(message).should match message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,8 +5,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe NotifierHelper do
|
||||
include MarkdownifyHelper
|
||||
|
||||
describe '#post_message' do
|
||||
before do
|
||||
# post for truncate test
|
||||
|
|
|
|||
|
|
@ -12,30 +12,11 @@ describe PostsHelper do
|
|||
end
|
||||
|
||||
context 'with posts with text' do
|
||||
context 'when :length is passed in parameters' do
|
||||
it 'returns string of size less or equal to :length' do
|
||||
@sm = double(:text => "## My title\n Post content...")
|
||||
string_size = 12
|
||||
post_page_title(@sm, :length => string_size ).size.should <= string_size
|
||||
end
|
||||
end
|
||||
context 'when :length is not passed in parameters' do
|
||||
context 'with a Markdown header of less than 200 characters on first line'do
|
||||
it 'returns atx style header' do
|
||||
@sm = double(:text => "## My title\n Post content...")
|
||||
post_page_title(@sm).should == "## My title"
|
||||
end
|
||||
it 'returns setext style header' do
|
||||
@sm = double(:text => "My title \n======\n Post content...")
|
||||
post_page_title(@sm).should == "My title \n======"
|
||||
end
|
||||
end
|
||||
context 'without a Markdown header of less than 200 characters on first line 'do
|
||||
it 'truncates posts to the 20 first characters' do
|
||||
@sm = double(:text => "Very, very, very long post")
|
||||
post_page_title(@sm).should == "Very, very, very ..."
|
||||
end
|
||||
end
|
||||
it "delegates to message.title" do
|
||||
message = double
|
||||
message.should_receive(:title)
|
||||
post = double(message: message)
|
||||
post_page_title(post)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
175
spec/lib/diaspora/message_renderer_spec.rb
Normal file
175
spec/lib/diaspora/message_renderer_spec.rb
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Diaspora::MessageRenderer do
|
||||
def message text, opts={}
|
||||
Diaspora::MessageRenderer.new(text, opts)
|
||||
end
|
||||
|
||||
describe '#title' do
|
||||
context 'when :length is passed in parameters' do
|
||||
it 'returns string of size less or equal to :length' do
|
||||
string_size = 12
|
||||
title = message("## My title\n Post content...").title(length: string_size)
|
||||
expect(title.size).to be <= string_size
|
||||
end
|
||||
end
|
||||
|
||||
context 'when :length is not passed in parameters' do
|
||||
context 'with a Markdown header of less than 200 characters on first line' do
|
||||
it 'returns atx style header' do
|
||||
expect(message("## My title\n Post content...").title).to eq "My title"
|
||||
expect(message("## My title ##\n Post content...").title).to eq "My title"
|
||||
end
|
||||
|
||||
it 'returns setext style header' do
|
||||
expect(message("My title \n======\n Post content...").title).to eq "My title"
|
||||
end
|
||||
end
|
||||
|
||||
context 'without a Markdown header of less than 200 characters on first line ' do
|
||||
it 'truncates posts to the 20 first characters' do
|
||||
expect(message("Very, very, very long post").title).to eq "Very, very, very ..."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#html' do
|
||||
it 'escapes the message' do
|
||||
xss = "</a> <script> alert('hey'); </script>"
|
||||
|
||||
expect(message(xss).html).to_not include xss
|
||||
end
|
||||
|
||||
it 'is html_safe' do
|
||||
expect(message("hey guys").html).to be_html_safe
|
||||
end
|
||||
|
||||
it 'should leave HTML entities intact' do
|
||||
entities = '& ß ' ' "'
|
||||
expect(message(entities).html).to eq entities
|
||||
end
|
||||
|
||||
context 'with mentions' do
|
||||
it 'makes hovercard links for mentioned people' do
|
||||
expect(
|
||||
message(
|
||||
"@{Bob; #{bob.person.diaspora_handle}}",
|
||||
mentioned_people: [bob.person]
|
||||
).html
|
||||
).to include 'hovercard'
|
||||
end
|
||||
|
||||
it 'makes plaintext out of mentions of people not in the posts aspects' do
|
||||
expect(
|
||||
message("@{Bob; #{bob.person.diaspora_handle}}").html
|
||||
).to_not include 'href'
|
||||
end
|
||||
|
||||
context 'linking all mentions' do
|
||||
it 'makes plain links for people not in the post aspects' do
|
||||
message = message("@{Bob; #{bob.person.diaspora_handle}}", link_all_mentions: true).html
|
||||
expect(message).to_not include 'hovercard'
|
||||
expect(message).to include '/u/bob'
|
||||
end
|
||||
|
||||
it "makes no hovercards if they're disabled" do
|
||||
message = message(
|
||||
"@{Bob; #{bob.person.diaspora_handle}}",
|
||||
mentioned_people: [bob.person],
|
||||
disable_hovercards: true
|
||||
).html
|
||||
expect(message).to_not include 'hovercard'
|
||||
expect(message).to include '/u/bob'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#markdownified" do
|
||||
describe "not doing something dumb" do
|
||||
it "strips out script tags" do
|
||||
expect(
|
||||
message("<script>alert('XSS is evil')</script>").markdownified
|
||||
).to eq "<p>alert('XSS is evil')</p>\n"
|
||||
end
|
||||
|
||||
it 'strips onClick handlers from links' do
|
||||
expect(
|
||||
message('[XSS](http://joindiaspora.com/" onClick="$\(\'a\'\).remove\(\))').markdownified
|
||||
).to_not match(/ onClick/i)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not barf if message is nil' do
|
||||
expect(message(nil).markdownified).to eq ''
|
||||
end
|
||||
|
||||
it 'autolinks standard url links' do
|
||||
expect(
|
||||
message("http://joindiaspora.com/"
|
||||
).markdownified).to include 'href="http://joindiaspora.com/"'
|
||||
end
|
||||
|
||||
context 'when formatting status messages' do
|
||||
it "should leave tags intact" do
|
||||
expect(
|
||||
message("I love #markdown").markdownified
|
||||
).to match %r{<a href="/tags/markdown" class="tag">#markdown</a>}
|
||||
end
|
||||
|
||||
it 'should leave multi-underscore tags intact' do
|
||||
expect(
|
||||
message("Here is a #multi_word tag").markdownified
|
||||
).to match %r{Here is a <a href="/tags/multi_word" class="tag">#multi_word</a> tag}
|
||||
|
||||
expect(
|
||||
message("Here is a #multi_word_tag yo").markdownified
|
||||
).to match %r{Here is a <a href="/tags/multi_word_tag" class="tag">#multi_word_tag</a> yo}
|
||||
end
|
||||
|
||||
it "should leave mentions intact" do
|
||||
expect(
|
||||
message("Hey @{Bob; #{bob.diaspora_handle}}!", mentioned_people: [bob.person]).markdownified
|
||||
).to match(/hovercard/)
|
||||
end
|
||||
|
||||
it "should leave mentions intact for real diaspora handles" do
|
||||
new_person = FactoryGirl.create(:person, diaspora_handle: 'maxwell@joindiaspora.com')
|
||||
expect(
|
||||
message(
|
||||
"Hey @{maxwell@joindiaspora.com; #{new_person.diaspora_handle}}!",
|
||||
mentioned_people: [new_person]
|
||||
).markdownified
|
||||
).to match(/hovercard/)
|
||||
end
|
||||
|
||||
it 'should process text with both a hashtag and a link' do
|
||||
expect(
|
||||
message("Test #tag?\nhttps://joindiaspora.com\n").markdownified
|
||||
).to eq %{<p>Test <a href="/tags/tag" class="tag">#tag</a>?<br>\n<a href="https://joindiaspora.com" target="_blank">https://joindiaspora.com</a></p>\n}
|
||||
end
|
||||
|
||||
it 'should process text with a header' do
|
||||
expect(message("# I love markdown").markdownified).to match "I love markdown"
|
||||
end
|
||||
|
||||
it 'should leave HTML entities intact' do
|
||||
entities = '& ß ' ' "'
|
||||
expect(message(entities).markdownified).to eq "<p>#{entities}</p>\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#plain_text_without_markdown" do
|
||||
it 'does not remove markdown in links' do
|
||||
text = "some text and here comes http://exampe.org/foo_bar_baz a link"
|
||||
expect(message(text).plain_text_without_markdown).to eq text
|
||||
end
|
||||
|
||||
it 'does not destroy hashtag that starts a line' do
|
||||
text = "#hashtag message"
|
||||
expect(message(text).plain_text_without_markdown).to eq text
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Notifier do
|
||||
include ActionView::Helpers::TextHelper
|
||||
include MarkdownifyHelper
|
||||
|
||||
let(:person) { FactoryGirl.create(:person) }
|
||||
|
||||
before do
|
||||
|
|
@ -120,7 +117,7 @@ describe Notifier do
|
|||
end
|
||||
|
||||
it 'BODY: contains the truncated original post' do
|
||||
@mail.body.encoded.should include(@sm.formatted_message)
|
||||
@mail.body.encoded.should include(@sm.message.plain_text)
|
||||
end
|
||||
|
||||
it 'BODY: contains the name of person liking' do
|
||||
|
|
@ -150,7 +147,7 @@ describe Notifier do
|
|||
end
|
||||
|
||||
it 'BODY: contains the truncated original post' do
|
||||
@mail.body.encoded.should include(@sm.formatted_message)
|
||||
@mail.body.encoded.should include(@sm.message.plain_text)
|
||||
end
|
||||
|
||||
it 'BODY: contains the name of person liking' do
|
||||
|
|
@ -224,7 +221,7 @@ describe Notifier do
|
|||
end
|
||||
|
||||
it 'SUBJECT: has a snippet of the post contents, without markdown and without newlines' do
|
||||
comment_mail.subject.should == "Re: Headline It's really sunny outside today, and this is a super long ..."
|
||||
comment_mail.subject.should == "Re: Headline"
|
||||
end
|
||||
|
||||
context 'BODY' do
|
||||
|
|
@ -265,7 +262,7 @@ describe Notifier do
|
|||
end
|
||||
|
||||
it 'SUBJECT: has a snippet of the post contents, without markdown and without newlines' do
|
||||
comment_mail.subject.should == "Re: Headline It's really sunny outside today, and this is a super long ..."
|
||||
comment_mail.subject.should == "Re: Headline"
|
||||
end
|
||||
|
||||
context 'BODY' do
|
||||
|
|
|
|||
|
|
@ -25,17 +25,17 @@ describe Services::Facebook do
|
|||
end
|
||||
|
||||
it 'removes text formatting markdown from post text' do
|
||||
message = "Text with some **bolded** and _italic_ parts."
|
||||
post = double(:text => message, :photos => [])
|
||||
message = double
|
||||
message.should_receive(:plain_text_without_markdown).and_return("")
|
||||
post = double(message: message, photos: [])
|
||||
post_params = @service.create_post_params(post)
|
||||
post_params[:message].should match "Text with some bolded and italic parts."
|
||||
end
|
||||
|
||||
it 'does not add post link when no photos' do
|
||||
message = "Text with some **bolded** and _italic_ parts."
|
||||
post = double(:text => message, :photos => [])
|
||||
message = "Some text."
|
||||
post = double(message: double(plain_text_without_markdown: message), photos: [])
|
||||
post_params = @service.create_post_params(post)
|
||||
post_params[:message].should match "Text with some bolded and italic parts."
|
||||
post_params[:message].should_not include "http"
|
||||
end
|
||||
|
||||
it 'sets facebook id on post' do
|
||||
|
|
|
|||
|
|
@ -38,9 +38,10 @@ describe Services::Twitter do
|
|||
end
|
||||
|
||||
it 'removes text formatting markdown from post text' do
|
||||
message = "Text with some **bolded** and _italic_ parts."
|
||||
post = double(:text => message, :photos => [])
|
||||
@service.send(:build_twitter_post, post).should match "Text with some bolded and italic parts."
|
||||
message = double
|
||||
message.should_receive(:plain_text_without_markdown).and_return("")
|
||||
post = double(message: message, photos: [])
|
||||
@service.send(:build_twitter_post, post)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -53,19 +54,19 @@ describe Services::Twitter do
|
|||
|
||||
it "should not truncate a short message" do
|
||||
short_message = SecureRandom.hex(20)
|
||||
short_post = double(:text => short_message, :photos => [])
|
||||
short_post = double(message: double(plain_text_without_markdown: short_message), photos: [])
|
||||
@service.send(:build_twitter_post, short_post).should match short_message
|
||||
end
|
||||
|
||||
it "should truncate a long message" do
|
||||
long_message = SecureRandom.hex(220)
|
||||
long_post = double(:text => long_message, :id => 1, :photos => [])
|
||||
long_post = double(message: double(plain_text_without_markdown: long_message), id: 1, photos: [])
|
||||
@service.send(:build_twitter_post, long_post).length.should be < long_message.length
|
||||
end
|
||||
|
||||
it "should not truncate a long message with an http url" do
|
||||
long_message = " http://joindiaspora.com/a-very-long-url-name-that-will-be-shortened.html " + @long_message_end
|
||||
long_post = double(:text => long_message, :id => 1, :photos => [])
|
||||
long_post = double(message: double(plain_text_without_markdown: long_message), id: 1, photos: [])
|
||||
@post.text = long_message
|
||||
answer = @service.send(:build_twitter_post, @post)
|
||||
|
||||
|
|
@ -74,7 +75,7 @@ describe Services::Twitter do
|
|||
|
||||
it "should not cut links when truncating a post" do
|
||||
long_message = SecureRandom.hex(40) + " http://joindiaspora.com/a-very-long-url-name-that-will-be-shortened.html " + SecureRandom.hex(55)
|
||||
long_post = double(:text => long_message, :id => 1, :photos => [])
|
||||
long_post = double(message: double(plain_text_without_markdown: long_message), id: 1, photos: [])
|
||||
answer = @service.send(:build_twitter_post, long_post)
|
||||
|
||||
answer.should match /\.\.\./
|
||||
|
|
@ -83,7 +84,7 @@ describe Services::Twitter do
|
|||
|
||||
it "should append the otherwise-cut link when truncating a post" do
|
||||
long_message = "http://joindiaspora.com/a-very-long-decoy-url.html " + SecureRandom.hex(20) + " http://joindiaspora.com/a-very-long-url-name-that-will-be-shortened.html " + SecureRandom.hex(55) + " http://joindiaspora.com/a-very-long-decoy-url-part-2.html"
|
||||
long_post = double(:text => long_message, :id => 1, :photos => [])
|
||||
long_post = double(message: double(plain_text_without_markdown: long_message), id: 1, photos: [])
|
||||
answer = @service.send(:build_twitter_post, long_post)
|
||||
|
||||
answer.should match /\.\.\./
|
||||
|
|
@ -99,7 +100,7 @@ describe Services::Twitter do
|
|||
|
||||
it "should truncate a long message with an ftp url" do
|
||||
long_message = @long_message_start + " ftp://joindiaspora.com/a-very-long-url-name-that-will-be-shortened.html " + @long_message_end
|
||||
long_post = double(:text => long_message, :id => 1, :photos => [])
|
||||
long_post = double(message: double(plain_text_without_markdown: long_message), id: 1, photos: [])
|
||||
answer = @service.send(:build_twitter_post, long_post)
|
||||
|
||||
answer.should match /\.\.\./
|
||||
|
|
@ -107,7 +108,7 @@ describe Services::Twitter do
|
|||
|
||||
it "should not truncate a message of maximum length" do
|
||||
exact_size_message = SecureRandom.hex(70)
|
||||
exact_size_post = double(:text => exact_size_message, :id => 1, :photos => [])
|
||||
exact_size_post = double(message: double(plain_text_without_markdown: exact_size_message), id: 1, photos: [])
|
||||
answer = @service.send(:build_twitter_post, exact_size_post)
|
||||
|
||||
answer.should match exact_size_message
|
||||
|
|
|
|||
|
|
@ -150,18 +150,6 @@ STR
|
|||
@sm = FactoryGirl.create(:status_message, :text => @test_string )
|
||||
end
|
||||
|
||||
describe '#formatted_message' do
|
||||
it 'escapes the message' do
|
||||
xss = "</a> <script> alert('hey'); </script>"
|
||||
@sm.text << xss
|
||||
|
||||
@sm.formatted_message.should_not include xss
|
||||
end
|
||||
it 'is html_safe' do
|
||||
@sm.formatted_message.html_safe?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create_mentions' do
|
||||
it 'creates a mention for everyone mentioned in the message' do
|
||||
Diaspora::Mentionable.should_receive(:people_from_string).and_return(@people)
|
||||
|
|
|
|||
|
|
@ -66,32 +66,17 @@ describe PostPresenter do
|
|||
|
||||
describe '#title' do
|
||||
context 'with posts with text' do
|
||||
context 'with a Markdown header of less than 200 characters on first line'do
|
||||
it 'returns atx style header' do
|
||||
@sm = double(:text => "## My title\n Post content...")
|
||||
@presenter.post = @sm
|
||||
@presenter.title.should == "## My title"
|
||||
end
|
||||
|
||||
it 'returns setext style header' do
|
||||
@sm = double(:text => "My title \n======\n Post content...")
|
||||
@presenter.post = @sm
|
||||
@presenter.title.should == "My title \n======"
|
||||
end
|
||||
end
|
||||
|
||||
context 'without a Markdown header of less than 200 characters on first line 'do
|
||||
it 'truncates post to the 20 first characters' do
|
||||
@sm = double(:text => "Very, very, very long post")
|
||||
@presenter.post = @sm
|
||||
@presenter.title.should == "Very, very, very ..."
|
||||
end
|
||||
it "delegates to message.title" do
|
||||
message = double(present?: true)
|
||||
message.should_receive(:title)
|
||||
@presenter.post = double(message: message)
|
||||
@presenter.title
|
||||
end
|
||||
end
|
||||
|
||||
context 'with posts without text' do
|
||||
it ' displays a messaage with the post class' do
|
||||
@sm = double(:text => "", :author => bob.person, :author_name => bob.person.name)
|
||||
@sm = double(message: double(present?: false), author: bob.person, author_name: bob.person.name)
|
||||
@presenter.post = @sm
|
||||
@presenter.title.should == "A post from #{@sm.author.name}"
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue