Introduce message renderer
This new class replaces all existing server side message rendering helpers and is the new global entry point for such needs. All models with relevant fields now expose an instance of MessageRenderer for those. MessageRenderer acts as gateway between the existing processing solutions for markdown, mentions and tags and provides a very flexible interface for all output needs. This makes the API to obtain a message in a certain format clear. As a result of centralizing the processing a lot of duplication is eliminated. Centralizing the message processing also makes it clear where to change its behaviour, add new representations and what options are already available.
This commit is contained in:
parent
8f67e1eb17
commit
8280556a47
40 changed files with 545 additions and 389 deletions
2
Gemfile
2
Gemfile
|
|
@ -65,7 +65,7 @@ gem 'messagebus_ruby_api', '1.0.3'
|
||||||
|
|
||||||
gem 'nokogiri', '1.6.1'
|
gem 'nokogiri', '1.6.1'
|
||||||
gem 'rails_autolink', '1.1.5'
|
gem 'rails_autolink', '1.1.5'
|
||||||
gem 'redcarpet', '3.0.0'
|
gem 'redcarpet', '3.1.1'
|
||||||
gem 'roxml', '3.1.6'
|
gem 'roxml', '3.1.6'
|
||||||
gem 'ruby-oembed', '0.8.9'
|
gem 'ruby-oembed', '0.8.9'
|
||||||
gem 'opengraph_parser', '0.2.3'
|
gem 'opengraph_parser', '0.2.3'
|
||||||
|
|
|
||||||
|
|
@ -345,7 +345,7 @@ GEM
|
||||||
rb-inotify (0.9.3)
|
rb-inotify (0.9.3)
|
||||||
rdoc (3.12.2)
|
rdoc (3.12.2)
|
||||||
json (~> 1.4)
|
json (~> 1.4)
|
||||||
redcarpet (3.0.0)
|
redcarpet (3.1.1)
|
||||||
redis (3.0.6)
|
redis (3.0.6)
|
||||||
redis-namespace (1.4.1)
|
redis-namespace (1.4.1)
|
||||||
redis (~> 3.0.4)
|
redis (~> 3.0.4)
|
||||||
|
|
@ -512,7 +512,7 @@ DEPENDENCIES
|
||||||
rails_autolink (= 1.1.5)
|
rails_autolink (= 1.1.5)
|
||||||
rb-fsevent (= 0.9.4)
|
rb-fsevent (= 0.9.4)
|
||||||
rb-inotify (= 0.9.3)
|
rb-inotify (= 0.9.3)
|
||||||
redcarpet (= 3.0.0)
|
redcarpet (= 3.1.1)
|
||||||
remotipart (= 1.2.1)
|
remotipart (= 1.2.1)
|
||||||
rmagick (= 2.13.2)
|
rmagick (= 2.13.2)
|
||||||
roxml (= 3.1.6)
|
roxml (= 3.1.6)
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,11 @@ class UsersController < ApplicationController
|
||||||
if @user = User.find_by_username(params[:username])
|
if @user = User.find_by_username(params[:username])
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.atom do
|
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
|
end
|
||||||
|
|
||||||
format.any { redirect_to person_path(@user.person) }
|
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
|
module NotifierHelper
|
||||||
|
|
||||||
# @param post [Post] The post object.
|
# @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.
|
# @return [String] The truncated and formatted post.
|
||||||
def post_message(post, opts={})
|
def post_message(post, opts={})
|
||||||
opts[:length] ||= 200
|
if post.respond_to? :message
|
||||||
if post.respond_to? :formatted_message
|
post.message.plain_text_without_markdown truncate: opts.fetch(:length, 200)
|
||||||
message = strip_markdown(post.formatted_message(:plain_text => true))
|
|
||||||
message = truncate(message, :length => opts[:length])
|
|
||||||
message = process_newlines(message) if opts[:process_newlines]
|
|
||||||
message
|
|
||||||
else
|
else
|
||||||
I18n.translate 'notifier.a_post_you_shared'
|
I18n.translate 'notifier.a_post_you_shared'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param comment [Comment] The comment to process.
|
# @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.
|
# @return [String] The truncated and formatted comment.
|
||||||
def comment_message(comment, opts={})
|
def comment_message(comment, opts={})
|
||||||
opts[:length] ||= 600
|
comment.message.plain_text_without_markdown truncate: opts.fetch(:length, 600)
|
||||||
text = strip_markdown(comment.text)
|
|
||||||
text = truncate(text, :length => opts[:length])
|
|
||||||
text = process_newlines(text) if opts[:process_newlines]
|
|
||||||
text
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,8 @@ module PostsHelper
|
||||||
elsif post.is_a?(Reshare)
|
elsif post.is_a?(Reshare)
|
||||||
I18n.t "posts.show.reshare_by", :author => post.author_name
|
I18n.t "posts.show.reshare_by", :author => post.author_name
|
||||||
else
|
else
|
||||||
if post.text.present?
|
if post.message.present?
|
||||||
if opts.has_key?(:length)
|
post.message.title opts
|
||||||
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
|
|
||||||
elsif post.respond_to?(:photos) && post.photos.present?
|
elsif post.respond_to?(:photos) && post.photos.present?
|
||||||
I18n.t "posts.show.photos_by", :count => post.photos.size, :author => post.author_name
|
I18n.t "posts.show.photos_by", :count => post.photos.size, :author => post.author_name
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
module NotificationMailers
|
module NotificationMailers
|
||||||
class AlsoCommented < NotificationMailers::Base
|
class AlsoCommented < NotificationMailers::Base
|
||||||
include ActionView::Helpers::TextHelper
|
|
||||||
include MarkdownifyHelper
|
|
||||||
|
|
||||||
attr_accessor :comment
|
attr_accessor :comment
|
||||||
delegate :post, to: :comment, prefix: true
|
delegate :post, to: :comment, prefix: true
|
||||||
|
|
||||||
|
|
@ -11,8 +8,7 @@ module NotificationMailers
|
||||||
|
|
||||||
if mail?
|
if mail?
|
||||||
@headers[:from] = "\"#{@comment.author_name} (diaspora*)\" <#{AppConfig.mail.sender_address}>"
|
@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: #{@comment.comment_email_subject}"
|
||||||
@headers[:subject] = "Re: #{@headers[:subject]}"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
module NotificationMailers
|
module NotificationMailers
|
||||||
TRUNCATION_LEN = 70
|
|
||||||
|
|
||||||
class Base
|
class Base
|
||||||
attr_accessor :recipient, :sender
|
attr_accessor :recipient, :sender
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,12 @@
|
||||||
module NotificationMailers
|
module NotificationMailers
|
||||||
class CommentOnPost < NotificationMailers::Base
|
class CommentOnPost < NotificationMailers::Base
|
||||||
include ActionView::Helpers::TextHelper
|
|
||||||
include MarkdownifyHelper
|
|
||||||
|
|
||||||
attr_accessor :comment
|
attr_accessor :comment
|
||||||
|
|
||||||
def set_headers(comment_id)
|
def set_headers(comment_id)
|
||||||
@comment = Comment.find(comment_id)
|
@comment = Comment.find(comment_id)
|
||||||
|
|
||||||
@headers[:from] = "\"#{@comment.author_name} (diaspora*)\" <#{AppConfig.mail.sender_address}>"
|
@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: #{@comment.comment_email_subject}"
|
||||||
@headers[:subject] = "Re: #{@headers[:subject]}"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
class Notifier < ActionMailer::Base
|
class Notifier < ActionMailer::Base
|
||||||
helper :application
|
helper :application
|
||||||
helper :markdownify
|
|
||||||
helper :notifier
|
helper :notifier
|
||||||
helper :people
|
helper :people
|
||||||
|
|
||||||
def self.admin(string, recipients, opts = {})
|
def self.admin(string, recipients, opts = {})
|
||||||
mails = []
|
mails = []
|
||||||
recipients.each do |rec|
|
recipients.each do |rec|
|
||||||
|
|
@ -16,7 +15,7 @@ class Notifier < ActionMailer::Base
|
||||||
def single_admin(string, recipient, opts={})
|
def single_admin(string, recipient, opts={})
|
||||||
@receiver = recipient
|
@receiver = recipient
|
||||||
@string = string.html_safe
|
@string = string.html_safe
|
||||||
|
|
||||||
if attach = opts.delete(:attachments)
|
if attach = opts.delete(:attachments)
|
||||||
attach.each{ |f|
|
attach.each{ |f|
|
||||||
attachments[f[:name]] = f[:file]
|
attachments[f[:name]] = f[:file]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
class Comment < ActiveRecord::Base
|
class Comment < ActiveRecord::Base
|
||||||
|
|
||||||
include Diaspora::Federated::Base
|
include Diaspora::Federated::Base
|
||||||
|
|
||||||
include Diaspora::Guid
|
include Diaspora::Guid
|
||||||
include Diaspora::Relayable
|
include Diaspora::Relayable
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ class Comment < ActiveRecord::Base
|
||||||
belongs_to :commentable, :touch => true, :polymorphic => true
|
belongs_to :commentable, :touch => true, :polymorphic => true
|
||||||
alias_attribute :post, :commentable
|
alias_attribute :post, :commentable
|
||||||
belongs_to :author, :class_name => 'Person'
|
belongs_to :author, :class_name => 'Person'
|
||||||
|
|
||||||
delegate :name, to: :author, prefix: true
|
delegate :name, to: :author, prefix: true
|
||||||
delegate :comment_email_subject, to: :parent
|
delegate :comment_email_subject, to: :parent
|
||||||
delegate :author_name, to: :parent, prefix: true
|
delegate :author_name, to: :parent, prefix: true
|
||||||
|
|
@ -79,6 +79,10 @@ class Comment < ActiveRecord::Base
|
||||||
self.post = parent
|
self.post = parent
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def message
|
||||||
|
@message ||= Diaspora::MessageRenderer.new text
|
||||||
|
end
|
||||||
|
|
||||||
def text= text
|
def text= text
|
||||||
self[:text] = text.to_s.strip #to_s if for nil, for whatever reason
|
self[:text] = text.to_s.strip #to_s if for nil, for whatever reason
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class Message < ActiveRecord::Base
|
||||||
validate :participant_of_parent_conversation
|
validate :participant_of_parent_conversation
|
||||||
|
|
||||||
after_create do # don't use 'after_commit' here since there is a call to 'save!'
|
after_create do # don't use 'after_commit' here since there is a call to 'save!'
|
||||||
# inside, which would cause an infinite recursion
|
# inside, which would cause an infinite recursion
|
||||||
#sign comment as commenter
|
#sign comment as commenter
|
||||||
self.author_signature = self.sign_with_key(self.author.owner.encryption_key) if self.author.owner
|
self.author_signature = self.sign_with_key(self.author.owner.encryption_key) if self.author.owner
|
||||||
|
|
||||||
|
|
@ -71,8 +71,8 @@ class Message < ActiveRecord::Base
|
||||||
Notifications::PrivateMessage unless user.person == person
|
Notifications::PrivateMessage unless user.person == person
|
||||||
end
|
end
|
||||||
|
|
||||||
def formatted_message(opts={})
|
def message
|
||||||
opts[:plain_text] ? self.text: ERB::Util.h(self.text)
|
@message ||= Diaspora::MessageRenderer.new text
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ class Profile < ActiveRecord::Base
|
||||||
def receive(user, person)
|
def receive(user, person)
|
||||||
Rails.logger.info("event=receive payload_type=profile sender=#{person} to=#{user}")
|
Rails.logger.info("event=receive payload_type=profile sender=#{person} to=#{user}")
|
||||||
profiles_attr = self.attributes.merge('tag_string' => self.tag_string).slice('diaspora_handle', 'first_name', 'last_name', 'image_url', 'image_url_small', 'image_url_medium', 'birthday', 'gender', 'bio', 'location', 'searchable', 'nsfw', 'tag_string')
|
profiles_attr = self.attributes.merge('tag_string' => self.tag_string).slice('diaspora_handle', 'first_name', 'last_name', 'image_url', 'image_url_small', 'image_url_medium', 'birthday', 'gender', 'bio', 'location', 'searchable', 'nsfw', 'tag_string')
|
||||||
person.profile.update_attributes(profiles_attr)
|
person.profile.update_attributes(profiles_attr)
|
||||||
|
|
||||||
person.profile
|
person.profile
|
||||||
end
|
end
|
||||||
|
|
@ -78,13 +78,13 @@ class Profile < ActiveRecord::Base
|
||||||
|
|
||||||
def from_omniauth_hash(omniauth_user_hash)
|
def from_omniauth_hash(omniauth_user_hash)
|
||||||
mappings = {"description" => "bio",
|
mappings = {"description" => "bio",
|
||||||
'image' => 'image_url',
|
'image' => 'image_url',
|
||||||
'name' => 'first_name',
|
'name' => 'first_name',
|
||||||
'location' => 'location',
|
'location' => 'location',
|
||||||
}
|
}
|
||||||
|
|
||||||
update_hash = Hash[ omniauth_user_hash.map {|k, v| [mappings[k], v] } ]
|
update_hash = Hash[ omniauth_user_hash.map {|k, v| [mappings[k], v] } ]
|
||||||
|
|
||||||
self.attributes.merge(update_hash){|key, old, new| old.blank? ? new : old}
|
self.attributes.merge(update_hash){|key, old, new| old.blank? ? new : old}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -132,6 +132,13 @@ class Profile < ActiveRecord::Base
|
||||||
birthday.to_s(:long).gsub(', 1000', '') if birthday.present?
|
birthday.to_s(:long).gsub(', 1000', '') if birthday.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def bio_message
|
||||||
|
@bio_message ||= Diaspora::MessageRenderer.new(bio)
|
||||||
|
end
|
||||||
|
|
||||||
|
def location_message
|
||||||
|
@location_message ||= Diaspora::MessageRenderer.new(location)
|
||||||
|
end
|
||||||
|
|
||||||
def tag_string
|
def tag_string
|
||||||
if @tag_string
|
if @tag_string
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,10 @@ class Reshare < Post
|
||||||
self.root ? root.raw_message : super
|
self.root ? root.raw_message : super
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def message
|
||||||
|
absolute_root.message if root.present?
|
||||||
|
end
|
||||||
|
|
||||||
def mentioned_people
|
def mentioned_people
|
||||||
self.root ? root.mentioned_people : super
|
self.root ? root.mentioned_people : super
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,8 @@
|
||||||
# the COPYRIGHT file.
|
# the COPYRIGHT file.
|
||||||
|
|
||||||
class Service < ActiveRecord::Base
|
class Service < ActiveRecord::Base
|
||||||
include ActionView::Helpers::TextHelper
|
|
||||||
include MarkdownifyHelper
|
|
||||||
|
|
||||||
attr_accessor :provider, :info, :access_level
|
attr_accessor :provider, :info, :access_level
|
||||||
|
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
validates_uniqueness_of :uid, :scope => :type
|
validates_uniqueness_of :uid, :scope => :type
|
||||||
|
|
||||||
|
|
@ -26,12 +23,12 @@ class Service < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def first_from_omniauth( auth_hash )
|
def first_from_omniauth( auth_hash )
|
||||||
@@auth = auth_hash
|
@@auth = auth_hash
|
||||||
where( type: service_type, uid: options[:uid] ).first
|
where( type: service_type, uid: options[:uid] ).first
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize_from_omniauth( auth_hash )
|
def initialize_from_omniauth( auth_hash )
|
||||||
@@auth = auth_hash
|
@@auth = auth_hash
|
||||||
service_type.constantize.new( options )
|
service_type.constantize.new( options )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -44,7 +41,7 @@ class Service < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def options
|
def options
|
||||||
{
|
{
|
||||||
nickname: auth['info']['nickname'],
|
nickname: auth['info']['nickname'],
|
||||||
access_token: auth['credentials']['token'],
|
access_token: auth['credentials']['token'],
|
||||||
access_secret: auth['credentials']['secret'],
|
access_secret: auth['credentials']['secret'],
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
class Services::Facebook < Service
|
class Services::Facebook < Service
|
||||||
include Rails.application.routes.url_helpers
|
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]
|
OVERRIDE_FIELDS_ON_FB_UPDATE = [:contact_id, :person_id, :request_id, :invitation_id, :photo_url, :name, :username]
|
||||||
MAX_CHARACTERS = 63206
|
MAX_CHARACTERS = 63206
|
||||||
|
|
||||||
def provider
|
def provider
|
||||||
"facebook"
|
"facebook"
|
||||||
|
|
@ -22,11 +21,16 @@ class Services::Facebook < Service
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_post_params(post)
|
def create_post_params(post)
|
||||||
message = strip_markdown(post.text(:plain_text => true))
|
message = post.message.plain_text_without_markdown
|
||||||
if post.photos.any?
|
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
|
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
|
end
|
||||||
|
|
||||||
def profile_photo_url
|
def profile_photo_url
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,4 @@
|
||||||
class Services::Tumblr < Service
|
class Services::Tumblr < Service
|
||||||
include ActionView::Helpers::TextHelper
|
|
||||||
include ActionView::Helpers::TagHelper
|
|
||||||
|
|
||||||
MAX_CHARACTERS = 1000
|
MAX_CHARACTERS = 1000
|
||||||
|
|
||||||
def provider
|
def provider
|
||||||
|
|
@ -38,10 +35,10 @@ class Services::Tumblr < Service
|
||||||
def tumblr_template(post, url)
|
def tumblr_template(post, url)
|
||||||
html = ''
|
html = ''
|
||||||
post.photos.each do |photo|
|
post.photos.each do |photo|
|
||||||
html += "})\n\n"
|
html << "})\n\n"
|
||||||
end
|
end
|
||||||
html += post.text
|
html << post.message.html(mentioned_people: [])
|
||||||
html += "\n\n[original post](#{url})"
|
html << "\n\n[original post](#{url})"
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_post(post)
|
def delete_post(post)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
class Services::Twitter < Service
|
class Services::Twitter < Service
|
||||||
include ActionView::Helpers::TextHelper
|
|
||||||
include Rails.application.routes.url_helpers
|
include Rails.application.routes.url_helpers
|
||||||
include MarkdownifyHelper
|
|
||||||
|
|
||||||
MAX_CHARACTERS = 140
|
MAX_CHARACTERS = 140
|
||||||
SHORTENED_URL_LENGTH = 21
|
SHORTENED_URL_LENGTH = 21
|
||||||
|
|
@ -40,7 +38,7 @@ class Services::Twitter < Service
|
||||||
|
|
||||||
def attempt_post post, retry_count=0
|
def attempt_post post, retry_count=0
|
||||||
message = build_twitter_post post, retry_count
|
message = build_twitter_post post, retry_count
|
||||||
tweet = client.update message
|
client.update message
|
||||||
rescue Twitter::Error::Forbidden => e
|
rescue Twitter::Error::Forbidden => e
|
||||||
if ! e.message.include? 'is over 140' || retry_count == 20
|
if ! e.message.include? 'is over 140' || retry_count == 20
|
||||||
raise e
|
raise e
|
||||||
|
|
@ -52,7 +50,7 @@ class Services::Twitter < Service
|
||||||
def build_twitter_post post, retry_count=0
|
def build_twitter_post post, retry_count=0
|
||||||
max_characters = MAX_CHARACTERS - retry_count
|
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
|
truncate_and_add_post_link post, post_text, max_characters
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -65,7 +63,7 @@ class Services::Twitter < Service
|
||||||
host: AppConfig.pod_uri.authority
|
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 = restore_truncated_url truncated_text, post_text, max_characters
|
||||||
|
|
||||||
"#{truncated_text} #{post_url}"
|
"#{truncated_text} #{post_url}"
|
||||||
|
|
@ -95,11 +93,9 @@ class Services::Twitter < Service
|
||||||
return truncated_text if truncated_text !~ /#{LINK_PATTERN}\Z/
|
return truncated_text if truncated_text !~ /#{LINK_PATTERN}\Z/
|
||||||
|
|
||||||
url = post_text.match(LINK_PATTERN, truncated_text.rindex('http'))[0]
|
url = post_text.match(LINK_PATTERN, truncated_text.rindex('http'))[0]
|
||||||
truncated_text = truncate(
|
truncated_text = post_text.truncate(
|
||||||
post_text,
|
max_characters - SHORTENED_URL_LENGTH + 2,
|
||||||
length: max_characters - SHORTENED_URL_LENGTH + 2,
|
separator: ' ', omission: ''
|
||||||
separator: ' ',
|
|
||||||
omission: ''
|
|
||||||
)
|
)
|
||||||
|
|
||||||
"#{truncated_text} #{url} ..."
|
"#{truncated_text} #{url} ..."
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,30 @@
|
||||||
class Services::Wordpress < Service
|
class Services::Wordpress < Service
|
||||||
include ActionView::Helpers::TextHelper
|
|
||||||
include MarkdownifyHelper
|
|
||||||
|
|
||||||
MAX_CHARACTERS = 1000
|
MAX_CHARACTERS = 1000
|
||||||
|
|
||||||
attr_accessor :username, :password, :host, :path
|
attr_accessor :username, :password, :host, :path
|
||||||
|
|
||||||
# uid = blog_id
|
# uid = blog_id
|
||||||
|
|
||||||
def provider
|
def provider
|
||||||
"wordpress"
|
"wordpress"
|
||||||
end
|
end
|
||||||
|
|
||||||
def post(post, url='')
|
def post post, url=''
|
||||||
res = Faraday.new(:url => "https://public-api.wordpress.com").post do |req|
|
res = Faraday.new(url: "https://public-api.wordpress.com").post do |req|
|
||||||
req.url "/rest/v1/sites/#{self.uid}/posts/new"
|
req.url "/rest/v1/sites/#{self.uid}/posts/new"
|
||||||
req.body = post_body(post).to_json
|
req.body = post_body(post).to_json
|
||||||
req.headers['Authorization'] = "Bearer #{self.access_token}"
|
req.headers['Authorization'] = "Bearer #{self.access_token}"
|
||||||
req.headers['Content-Type'] = 'application/json'
|
req.headers['Content-Type'] = 'application/json'
|
||||||
end
|
end
|
||||||
|
|
||||||
JSON.parse res.env[:body]
|
JSON.parse res.env[:body]
|
||||||
end
|
end
|
||||||
|
|
||||||
def post_body(post, url='')
|
def post_body post
|
||||||
post_text = markdownify(post.text)
|
{
|
||||||
post_title = truncate(strip_markdown(post.text(:plain_text => true)), :length => 40, :separator => ' ')
|
title: post.message.title(length: 40),
|
||||||
|
content: post.message.markdownified(disable_hovercards: true)
|
||||||
{:title => post_title, :content => post_text.html_safe}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
class StatusMessage < Post
|
class StatusMessage < Post
|
||||||
include Diaspora::Taggable
|
include Diaspora::Taggable
|
||||||
|
|
||||||
include ActionView::Helpers::TextHelper
|
|
||||||
include PeopleHelper
|
include PeopleHelper
|
||||||
|
|
||||||
acts_as_taggable_on :tags
|
acts_as_taggable_on :tags
|
||||||
|
|
@ -56,10 +55,6 @@ class StatusMessage < Post
|
||||||
tag_stream(tag_ids)
|
tag_stream(tag_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
def text(opts = {})
|
|
||||||
self.formatted_message(opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def raw_message
|
def raw_message
|
||||||
read_attribute(:text)
|
read_attribute(:text)
|
||||||
end
|
end
|
||||||
|
|
@ -77,12 +72,8 @@ class StatusMessage < Post
|
||||||
self.raw_message.match(/#nsfw/i) || super
|
self.raw_message.match(/#nsfw/i) || super
|
||||||
end
|
end
|
||||||
|
|
||||||
def formatted_message(opts={})
|
def message
|
||||||
return self.raw_message unless self.raw_message
|
@message ||= Diaspora::MessageRenderer.new raw_message, mentioned_people: mentioned_people
|
||||||
|
|
||||||
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))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def mentioned_people
|
def mentioned_people
|
||||||
|
|
@ -134,7 +125,7 @@ class StatusMessage < Post
|
||||||
end
|
end
|
||||||
|
|
||||||
def comment_email_subject
|
def comment_email_subject
|
||||||
formatted_message(:plain_text => true)
|
message.title length: 70
|
||||||
end
|
end
|
||||||
|
|
||||||
def first_photo_url(*args)
|
def first_photo_url(*args)
|
||||||
|
|
@ -142,7 +133,7 @@ class StatusMessage < Post
|
||||||
end
|
end
|
||||||
|
|
||||||
def text_and_photos_blank?
|
def text_and_photos_blank?
|
||||||
self.text.blank? && self.photos.blank?
|
self.raw_message.blank? && self.photos.blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
def queue_gather_oembed_data
|
def queue_gather_oembed_data
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
class OEmbedPresenter
|
class OEmbedPresenter
|
||||||
include PostsHelper
|
include PostsHelper
|
||||||
include ActionView::Helpers::TextHelper
|
|
||||||
|
|
||||||
def initialize(post, opts = {})
|
def initialize(post, opts = {})
|
||||||
@post = post
|
@post = post
|
||||||
|
|
@ -13,14 +12,14 @@ class OEmbedPresenter
|
||||||
|
|
||||||
def as_json(opts={})
|
def as_json(opts={})
|
||||||
{
|
{
|
||||||
:provider_name => "Diaspora",
|
:provider_name => "Diaspora",
|
||||||
:provider_url => AppConfig.pod_uri.to_s,
|
:provider_url => AppConfig.pod_uri.to_s,
|
||||||
:type => 'rich',
|
:type => 'rich',
|
||||||
:version => '1.0',
|
:version => '1.0',
|
||||||
:title => post_title,
|
:title => post_title,
|
||||||
:author_name => post_author,
|
:author_name => post_author,
|
||||||
:author_url => post_author_url,
|
:author_url => post_author_url,
|
||||||
:width => @opts.fetch(:maxwidth, 516),
|
:width => @opts.fetch(:maxwidth, 516),
|
||||||
:height => @opts.fetch(:maxheight, 320),
|
:height => @opts.fetch(:maxheight, 320),
|
||||||
:html => iframe_html
|
:html => iframe_html
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
class PostPresenter
|
class PostPresenter
|
||||||
include PostsHelper
|
include PostsHelper
|
||||||
include ActionView::Helpers::TextHelper
|
|
||||||
|
|
||||||
attr_accessor :post, :current_user
|
attr_accessor :post, :current_user
|
||||||
|
|
||||||
|
|
@ -47,7 +46,7 @@ class PostPresenter
|
||||||
end
|
end
|
||||||
|
|
||||||
def title
|
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
|
end
|
||||||
|
|
||||||
def root
|
def root
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,4 @@
|
||||||
= timeago(comment.created_at ? comment.created_at : Time.now)
|
= timeago(comment.created_at ? comment.created_at : Time.now)
|
||||||
|
|
||||||
%div{:class => direction_for(comment.text)}
|
%div{:class => direction_for(comment.text)}
|
||||||
= markdownify(comment)
|
= comment.message.markdownified
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,4 @@
|
||||||
= timeago(message.created_at)
|
= timeago(message.created_at)
|
||||||
|
|
||||||
%div{ :class => direction_for(message.text) }
|
%div{ :class => direction_for(message.text) }
|
||||||
= markdownify(message, :oembed => true)
|
= message.message.markdownified
|
||||||
|
|
|
||||||
|
|
@ -15,35 +15,35 @@
|
||||||
.profile_button
|
.profile_button
|
||||||
= link_to content_tag(:div, nil, :class => 'icons-mention', :title => t('people.show.mention'), :id => 'mention_button'), new_status_message_path(:person_id => @person.id), :rel => 'facebox'
|
= link_to content_tag(:div, nil, :class => 'icons-mention', :title => t('people.show.mention'), :id => 'mention_button'), new_status_message_path(:person_id => @person.id), :rel => 'facebox'
|
||||||
.white_bar
|
.white_bar
|
||||||
|
|
||||||
- if @contact.mutual?
|
- if @contact.mutual?
|
||||||
|
|
||||||
|
|
||||||
.profile_button
|
.profile_button
|
||||||
= link_to content_tag(:div, nil, :class => 'icons-message', :title => t('people.show.message'), :id => 'message_button'), new_conversation_path(:contact_id => @contact.id, :name => @contact.person.name), :rel => 'facebox'
|
= link_to content_tag(:div, nil, :class => 'icons-message', :title => t('people.show.message'), :id => 'message_button'), new_conversation_path(:contact_id => @contact.id, :name => @contact.person.name), :rel => 'facebox'
|
||||||
.white_bar
|
.white_bar
|
||||||
|
|
||||||
.profile_button
|
.profile_button
|
||||||
= link_to content_tag(:div, nil, :class => 'icons-ignoreuser block_user', :title => t('ignore'), :id => 'block_user_button', :data => { :person_id => @person.id }), '#', :rel => "nofollow" if @block.blank?
|
= link_to content_tag(:div, nil, :class => 'icons-ignoreuser block_user', :title => t('ignore'), :id => 'block_user_button', :data => { :person_id => @person.id }), '#', :rel => "nofollow" if @block.blank?
|
||||||
|
|
||||||
|
|
||||||
%br
|
|
||||||
|
%br
|
||||||
|
|
||||||
-if contact.sharing? || person == current_user.person
|
-if contact.sharing? || person == current_user.person
|
||||||
%ul#profile_information
|
%ul#profile_information
|
||||||
|
|
||||||
- unless person.bio.blank?
|
- unless person.bio.blank?
|
||||||
%li
|
%li
|
||||||
%h4
|
%h4
|
||||||
=t('.bio')
|
=t('.bio')
|
||||||
%div{ :class => direction_for(person.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?
|
- unless person.profile.location.blank?
|
||||||
%li
|
%li
|
||||||
%h4
|
%h4
|
||||||
=t('.location')
|
=t('.location')
|
||||||
%div{ :class => direction_for(person.location) }
|
%div{ :class => direction_for(person.location) }
|
||||||
= markdownify(person.location, :oembed => true, :newlines => true)
|
= person.profile.location_message.markdownified
|
||||||
|
|
||||||
- unless person.gender.blank?
|
- unless person.gender.blank?
|
||||||
%li
|
%li
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
= image_tag(photo.url(:thumb_small))
|
= image_tag(photo.url(:thumb_small))
|
||||||
%br
|
%br
|
||||||
= link_to t('layouts.header.view_all'), person_photos_path(person)
|
= link_to t('layouts.header.view_all'), person_photos_path(person)
|
||||||
|
|
||||||
- if person == current_user.person
|
- if person == current_user.person
|
||||||
%li.image_list
|
%li.image_list
|
||||||
%h4
|
%h4
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
= image_tag post.image_url
|
= image_tag post.image_url
|
||||||
|
|
||||||
%div{:class => direction_for(post.text)}
|
%div{:class => direction_for(post.text)}
|
||||||
!= markdownify(post)
|
!= post.message.markdownified
|
||||||
- if post.o_embed_cache
|
- if post.o_embed_cache
|
||||||
!= o_embed_html post.o_embed_cache
|
!= o_embed_html post.o_embed_cache
|
||||||
-if post.open_graph_cache
|
-if post.open_graph_cache
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,11 @@ atom_feed({'xmlns:thr' => 'http://purl.org/syndication/thread/1.0',
|
||||||
end
|
end
|
||||||
|
|
||||||
@posts.each do |post|
|
@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}",
|
feed.entry post, :url => "#{@user.url}p/#{post.id}",
|
||||||
:id => "#{@user.url}p/#{post.id}" do |entry|
|
:id => "#{@user.url}p/#{post.id}" do |entry|
|
||||||
|
|
||||||
entry.title post_page_title(post)
|
entry.title post.message.title
|
||||||
entry.content markdownify(post), :type => 'html'
|
entry.content post.message.markdownified(disable_hovercards: true), :type => 'html'
|
||||||
entry.tag! 'activity:verb', 'http://activitystrea.ms/schema/1.0/post'
|
entry.tag! 'activity:verb', 'http://activitystrea.ms/schema/1.0/post'
|
||||||
entry.tag! 'activity:object-type', 'http://activitystrea.ms/schema/1.0/note'
|
entry.tag! 'activity:object-type', 'http://activitystrea.ms/schema/1.0/note'
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,6 @@ require 'typhoeus'
|
||||||
# Presenters
|
# Presenters
|
||||||
require 'post_presenter'
|
require 'post_presenter'
|
||||||
|
|
||||||
# Helpers
|
|
||||||
require 'markdownify_helper'
|
|
||||||
|
|
||||||
# Our libs
|
# Our libs
|
||||||
require 'collect_user_photos'
|
require 'collect_user_photos'
|
||||||
require 'diaspora'
|
require 'diaspora'
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ module Diaspora
|
||||||
require 'diaspora/parser'
|
require 'diaspora/parser'
|
||||||
require 'diaspora/fetcher'
|
require 'diaspora/fetcher'
|
||||||
require 'diaspora/markdownify'
|
require 'diaspora/markdownify'
|
||||||
|
require 'diaspora/message_renderer'
|
||||||
require 'diaspora/mentionable'
|
require 'diaspora/mentionable'
|
||||||
require 'diaspora/exporter'
|
require 'diaspora/exporter'
|
||||||
require 'diaspora/federated'
|
require 'diaspora/federated'
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,10 @@ module Diaspora
|
||||||
module Markdownify
|
module Markdownify
|
||||||
class HTML < Redcarpet::Render::HTML
|
class HTML < Redcarpet::Render::HTML
|
||||||
include ActionView::Helpers::TextHelper
|
include ActionView::Helpers::TextHelper
|
||||||
include ActionView::Helpers::TagHelper
|
|
||||||
|
|
||||||
def autolink(link, type)
|
def autolink link, type
|
||||||
auto_link(link, :link => :urls, :html => { :target => "_blank" })
|
auto_link(link, link: :urls, html: { target: "_blank" })
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
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'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe NotifierHelper do
|
describe NotifierHelper do
|
||||||
include MarkdownifyHelper
|
|
||||||
|
|
||||||
describe '#post_message' do
|
describe '#post_message' do
|
||||||
before do
|
before do
|
||||||
# post for truncate test
|
# post for truncate test
|
||||||
|
|
|
||||||
|
|
@ -12,30 +12,11 @@ describe PostsHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with posts with text' do
|
context 'with posts with text' do
|
||||||
context 'when :length is passed in parameters' do
|
it "delegates to message.title" do
|
||||||
it 'returns string of size less or equal to :length' do
|
message = double
|
||||||
@sm = double(:text => "## My title\n Post content...")
|
message.should_receive(:title)
|
||||||
string_size = 12
|
post = double(message: message)
|
||||||
post_page_title(@sm, :length => string_size ).size.should <= string_size
|
post_page_title(post)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
end
|
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'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Notifier do
|
describe Notifier do
|
||||||
include ActionView::Helpers::TextHelper
|
|
||||||
include MarkdownifyHelper
|
|
||||||
|
|
||||||
let(:person) { FactoryGirl.create(:person) }
|
let(:person) { FactoryGirl.create(:person) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
|
@ -120,7 +117,7 @@ describe Notifier do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'BODY: contains the truncated original post' do
|
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
|
end
|
||||||
|
|
||||||
it 'BODY: contains the name of person liking' do
|
it 'BODY: contains the name of person liking' do
|
||||||
|
|
@ -150,7 +147,7 @@ describe Notifier do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'BODY: contains the truncated original post' do
|
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
|
end
|
||||||
|
|
||||||
it 'BODY: contains the name of person liking' do
|
it 'BODY: contains the name of person liking' do
|
||||||
|
|
@ -224,7 +221,7 @@ describe Notifier do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'SUBJECT: has a snippet of the post contents, without markdown and without newlines' do
|
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
|
end
|
||||||
|
|
||||||
context 'BODY' do
|
context 'BODY' do
|
||||||
|
|
@ -265,7 +262,7 @@ describe Notifier do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'SUBJECT: has a snippet of the post contents, without markdown and without newlines' do
|
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
|
end
|
||||||
|
|
||||||
context 'BODY' do
|
context 'BODY' do
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,17 @@ describe Services::Facebook do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes text formatting markdown from post text' do
|
it 'removes text formatting markdown from post text' do
|
||||||
message = "Text with some **bolded** and _italic_ parts."
|
message = double
|
||||||
post = double(:text => message, :photos => [])
|
message.should_receive(:plain_text_without_markdown).and_return("")
|
||||||
|
post = double(message: message, photos: [])
|
||||||
post_params = @service.create_post_params(post)
|
post_params = @service.create_post_params(post)
|
||||||
post_params[:message].should match "Text with some bolded and italic parts."
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not add post link when no photos' do
|
it 'does not add post link when no photos' do
|
||||||
message = "Text with some **bolded** and _italic_ parts."
|
message = "Some text."
|
||||||
post = double(:text => message, :photos => [])
|
post = double(message: double(plain_text_without_markdown: message), photos: [])
|
||||||
post_params = @service.create_post_params(post)
|
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
|
end
|
||||||
|
|
||||||
it 'sets facebook id on post' do
|
it 'sets facebook id on post' do
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,10 @@ describe Services::Twitter do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes text formatting markdown from post text' do
|
it 'removes text formatting markdown from post text' do
|
||||||
message = "Text with some **bolded** and _italic_ parts."
|
message = double
|
||||||
post = double(:text => message, :photos => [])
|
message.should_receive(:plain_text_without_markdown).and_return("")
|
||||||
@service.send(:build_twitter_post, post).should match "Text with some bolded and italic parts."
|
post = double(message: message, photos: [])
|
||||||
|
@service.send(:build_twitter_post, post)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -53,19 +54,19 @@ describe Services::Twitter do
|
||||||
|
|
||||||
it "should not truncate a short message" do
|
it "should not truncate a short message" do
|
||||||
short_message = SecureRandom.hex(20)
|
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
|
@service.send(:build_twitter_post, short_post).should match short_message
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should truncate a long message" do
|
it "should truncate a long message" do
|
||||||
long_message = SecureRandom.hex(220)
|
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
|
@service.send(:build_twitter_post, long_post).length.should be < long_message.length
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not truncate a long message with an http url" do
|
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_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
|
@post.text = long_message
|
||||||
answer = @service.send(:build_twitter_post, @post)
|
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
|
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_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 = @service.send(:build_twitter_post, long_post)
|
||||||
|
|
||||||
answer.should match /\.\.\./
|
answer.should match /\.\.\./
|
||||||
|
|
@ -83,7 +84,7 @@ describe Services::Twitter do
|
||||||
|
|
||||||
it "should append the otherwise-cut link when truncating a post" 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_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 = @service.send(:build_twitter_post, long_post)
|
||||||
|
|
||||||
answer.should match /\.\.\./
|
answer.should match /\.\.\./
|
||||||
|
|
@ -99,7 +100,7 @@ describe Services::Twitter do
|
||||||
|
|
||||||
it "should truncate a long message with an ftp url" 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_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 = @service.send(:build_twitter_post, long_post)
|
||||||
|
|
||||||
answer.should match /\.\.\./
|
answer.should match /\.\.\./
|
||||||
|
|
@ -107,7 +108,7 @@ describe Services::Twitter do
|
||||||
|
|
||||||
it "should not truncate a message of maximum length" do
|
it "should not truncate a message of maximum length" do
|
||||||
exact_size_message = SecureRandom.hex(70)
|
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 = @service.send(:build_twitter_post, exact_size_post)
|
||||||
|
|
||||||
answer.should match exact_size_message
|
answer.should match exact_size_message
|
||||||
|
|
|
||||||
|
|
@ -150,18 +150,6 @@ STR
|
||||||
@sm = FactoryGirl.create(:status_message, :text => @test_string )
|
@sm = FactoryGirl.create(:status_message, :text => @test_string )
|
||||||
end
|
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
|
describe '#create_mentions' do
|
||||||
it 'creates a mention for everyone mentioned in the message' do
|
it 'creates a mention for everyone mentioned in the message' do
|
||||||
Diaspora::Mentionable.should_receive(:people_from_string).and_return(@people)
|
Diaspora::Mentionable.should_receive(:people_from_string).and_return(@people)
|
||||||
|
|
|
||||||
|
|
@ -65,32 +65,17 @@ describe PostPresenter do
|
||||||
|
|
||||||
describe '#title' do
|
describe '#title' do
|
||||||
context 'with posts with text' do
|
context 'with posts with text' do
|
||||||
context 'with a Markdown header of less than 200 characters on first line'do
|
it "delegates to message.title" do
|
||||||
it 'returns atx style header' do
|
message = double(present?: true)
|
||||||
@sm = double(:text => "## My title\n Post content...")
|
message.should_receive(:title)
|
||||||
@presenter.post = @sm
|
@presenter.post = double(message: message)
|
||||||
@presenter.title.should == "## My title"
|
@presenter.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
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with posts without text' do
|
context 'with posts without text' do
|
||||||
it ' displays a messaage with the post class' 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.post = @sm
|
||||||
@presenter.title.should == "A post from #{@sm.author.name}"
|
@presenter.title.should == "A post from #{@sm.author.name}"
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue