From 8280556a476295f05acdd6dfed15380645a4ea98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonne=20Ha=C3=9F?= Date: Sat, 8 Mar 2014 23:49:14 +0100 Subject: [PATCH] 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. --- Gemfile | 2 +- Gemfile.lock | 4 +- app/controllers/users_controller.rb | 6 +- app/helpers/markdownify_helper.rb | 62 ----- app/helpers/notifier_helper.rb | 20 +- app/helpers/posts_helper.rb | 15 +- .../notification_mailers/also_commented.rb | 6 +- app/mailers/notification_mailers/base.rb | 2 - .../notification_mailers/comment_on_post.rb | 6 +- app/mailers/notifier.rb | 5 +- app/models/comment.rb | 8 +- app/models/message.rb | 6 +- app/models/profile.rb | 15 +- app/models/reshare.rb | 4 + app/models/service.rb | 11 +- app/models/services/facebook.rb | 14 +- app/models/services/tumblr.rb | 9 +- app/models/services/twitter.rb | 16 +- app/models/services/wordpress.rb | 30 ++- app/models/status_message.rb | 17 +- app/presenters/o_embed_presenter.rb | 5 +- app/presenters/post_presenter.rb | 3 +- app/views/comments/_comment.mobile.haml | 2 +- app/views/messages/_message.haml | 2 +- app/views/people/_profile_sidebar.html.haml | 20 +- .../_status_message.mobile.haml | 2 +- app/views/users/public.atom.builder | 5 +- config/initializers/load_libraries.rb | 3 - lib/diaspora.rb | 1 + lib/diaspora/markdownify/html.rb | 6 +- lib/diaspora/message_renderer.rb | 233 ++++++++++++++++++ spec/helpers/markdownify_helper_spec.rb | 105 -------- spec/helpers/notifier_helper_spec.rb | 2 - spec/helpers/posts_helper_spec.rb | 29 +-- spec/lib/diaspora/message_renderer_spec.rb | 175 +++++++++++++ spec/mailers/notifier_spec.rb | 11 +- spec/models/services/facebook_spec.rb | 12 +- spec/models/services/twitter_spec.rb | 21 +- spec/models/status_message_spec.rb | 12 - spec/presenters/post_presenter_spec.rb | 27 +- 40 files changed, 545 insertions(+), 389 deletions(-) delete mode 100644 app/helpers/markdownify_helper.rb create mode 100644 lib/diaspora/message_renderer.rb delete mode 100644 spec/helpers/markdownify_helper_spec.rb create mode 100644 spec/lib/diaspora/message_renderer_spec.rb diff --git a/Gemfile b/Gemfile index 219dc2532..28406380e 100644 --- a/Gemfile +++ b/Gemfile @@ -65,7 +65,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' diff --git a/Gemfile.lock b/Gemfile.lock index b6c729d42..cd593f48b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -345,7 +345,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) @@ -512,7 +512,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) rmagick (= 2.13.2) roxml (= 3.1.6) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 97ed376eb..886b905b1 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -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) } diff --git a/app/helpers/markdownify_helper.rb b/app/helpers/markdownify_helper.rb deleted file mode 100644 index b59537a1e..000000000 --- a/app/helpers/markdownify_helper.rb +++ /dev/null @@ -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
tags - # Grabbed from Github flavored Markdown - message.gsub(/^[\w\<][^\n]*\n+/) do |x| - x =~ /\n{2}/ ? x : (x.strip!; x << " \n") - end - end -end diff --git a/app/helpers/notifier_helper.rb b/app/helpers/notifier_helper.rb index d2d56d7bd..44de54f1c 100644 --- a/app/helpers/notifier_helper.rb +++ b/app/helpers/notifier_helper.rb @@ -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 diff --git a/app/helpers/posts_helper.rb b/app/helpers/posts_helper.rb index ddd8d19d1..6a4758a73 100644 --- a/app/helpers/posts_helper.rb +++ b/app/helpers/posts_helper.rb @@ -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 : - (?.{1,200}\n(?:={1,200}|-{1,200}))(?:\r?\n|$) # Setext-style header - | # or - (?\#{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 diff --git a/app/mailers/notification_mailers/also_commented.rb b/app/mailers/notification_mailers/also_commented.rb index c0957e5aa..d5ef2d3f4 100644 --- a/app/mailers/notification_mailers/also_commented.rb +++ b/app/mailers/notification_mailers/also_commented.rb @@ -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 diff --git a/app/mailers/notification_mailers/base.rb b/app/mailers/notification_mailers/base.rb index 06d21ad5d..ec2950bfb 100644 --- a/app/mailers/notification_mailers/base.rb +++ b/app/mailers/notification_mailers/base.rb @@ -1,6 +1,4 @@ module NotificationMailers - TRUNCATION_LEN = 70 - class Base attr_accessor :recipient, :sender diff --git a/app/mailers/notification_mailers/comment_on_post.rb b/app/mailers/notification_mailers/comment_on_post.rb index 18faa2df7..de01804bb 100644 --- a/app/mailers/notification_mailers/comment_on_post.rb +++ b/app/mailers/notification_mailers/comment_on_post.rb @@ -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 diff --git a/app/mailers/notifier.rb b/app/mailers/notifier.rb index c922f2744..5abce9ee0 100644 --- a/app/mailers/notifier.rb +++ b/app/mailers/notifier.rb @@ -1,9 +1,8 @@ class Notifier < ActionMailer::Base helper :application - helper :markdownify helper :notifier helper :people - + def self.admin(string, recipients, opts = {}) mails = [] recipients.each do |rec| @@ -16,7 +15,7 @@ class Notifier < ActionMailer::Base def single_admin(string, recipient, opts={}) @receiver = recipient @string = string.html_safe - + if attach = opts.delete(:attachments) attach.each{ |f| attachments[f[:name]] = f[:file] diff --git a/app/models/comment.rb b/app/models/comment.rb index 92c56929c..5fe754c8b 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -5,7 +5,7 @@ class Comment < ActiveRecord::Base include Diaspora::Federated::Base - + include Diaspora::Guid include Diaspora::Relayable @@ -22,7 +22,7 @@ class Comment < ActiveRecord::Base belongs_to :commentable, :touch => true, :polymorphic => true alias_attribute :post, :commentable belongs_to :author, :class_name => 'Person' - + delegate :name, to: :author, prefix: true delegate :comment_email_subject, to: :parent delegate :author_name, to: :parent, prefix: true @@ -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 diff --git a/app/models/message.rb b/app/models/message.rb index 60de53d3d..f73fc87b9 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -18,7 +18,7 @@ class Message < ActiveRecord::Base validate :participant_of_parent_conversation 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 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 end - def formatted_message(opts={}) - opts[:plain_text] ? self.text: ERB::Util.h(self.text) + def message + @message ||= Diaspora::MessageRenderer.new text end private diff --git a/app/models/profile.rb b/app/models/profile.rb index 8224a973b..3e0e425e1 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -55,7 +55,7 @@ class Profile < ActiveRecord::Base def receive(user, person) 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') - person.profile.update_attributes(profiles_attr) + person.profile.update_attributes(profiles_attr) person.profile end @@ -78,13 +78,13 @@ class Profile < ActiveRecord::Base def from_omniauth_hash(omniauth_user_hash) mappings = {"description" => "bio", - 'image' => 'image_url', - 'name' => 'first_name', + 'image' => 'image_url', + 'name' => 'first_name', 'location' => 'location', } update_hash = Hash[ omniauth_user_hash.map {|k, v| [mappings[k], v] } ] - + self.attributes.merge(update_hash){|key, old, new| old.blank? ? new : old} end @@ -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 diff --git a/app/models/reshare.rb b/app/models/reshare.rb index b597dea71..da902dc3d 100644 --- a/app/models/reshare.rb +++ b/app/models/reshare.rb @@ -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 diff --git a/app/models/service.rb b/app/models/service.rb index 463bd8389..4e443cf50 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -3,11 +3,8 @@ # the COPYRIGHT file. class Service < ActiveRecord::Base - include ActionView::Helpers::TextHelper - include MarkdownifyHelper - attr_accessor :provider, :info, :access_level - + belongs_to :user validates_uniqueness_of :uid, :scope => :type @@ -26,12 +23,12 @@ class Service < ActiveRecord::Base end def first_from_omniauth( auth_hash ) - @@auth = auth_hash + @@auth = auth_hash where( type: service_type, uid: options[:uid] ).first end def initialize_from_omniauth( auth_hash ) - @@auth = auth_hash + @@auth = auth_hash service_type.constantize.new( options ) end @@ -44,7 +41,7 @@ class Service < ActiveRecord::Base end def options - { + { nickname: auth['info']['nickname'], access_token: auth['credentials']['token'], access_secret: auth['credentials']['secret'], diff --git a/app/models/services/facebook.rb b/app/models/services/facebook.rb index a8a9bf0b7..f49fd2e51 100644 --- a/app/models/services/facebook.rb +++ b/app/models/services/facebook.rb @@ -1,9 +1,8 @@ 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 + MAX_CHARACTERS = 63206 def provider "facebook" @@ -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 diff --git a/app/models/services/tumblr.rb b/app/models/services/tumblr.rb index 6fef57617..65cecedef 100644 --- a/app/models/services/tumblr.rb +++ b/app/models/services/tumblr.rb @@ -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 += "![photo](#{photo.url(:scaled_full)})\n\n" + html << "![photo](#{photo.url(:scaled_full)})\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) diff --git a/app/models/services/twitter.rb b/app/models/services/twitter.rb index efa13799e..7c5d246ec 100644 --- a/app/models/services/twitter.rb +++ b/app/models/services/twitter.rb @@ -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} ..." diff --git a/app/models/services/wordpress.rb b/app/models/services/wordpress.rb index 048cf03f5..0764f7146 100644 --- a/app/models/services/wordpress.rb +++ b/app/models/services/wordpress.rb @@ -1,32 +1,30 @@ class Services::Wordpress < Service - include ActionView::Helpers::TextHelper - include MarkdownifyHelper - MAX_CHARACTERS = 1000 - + attr_accessor :username, :password, :host, :path - + # uid = blog_id - + def provider "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 diff --git a/app/models/status_message.rb b/app/models/status_message.rb index 4e0dfd127..7d1431cf8 100644 --- a/app/models/status_message.rb +++ b/app/models/status_message.rb @@ -5,7 +5,6 @@ class StatusMessage < Post include Diaspora::Taggable - include ActionView::Helpers::TextHelper include PeopleHelper acts_as_taggable_on :tags @@ -56,10 +55,6 @@ class StatusMessage < Post tag_stream(tag_ids) end - def text(opts = {}) - self.formatted_message(opts) - end - def raw_message read_attribute(:text) end @@ -77,12 +72,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 @@ -134,7 +125,7 @@ class StatusMessage < Post end def comment_email_subject - formatted_message(:plain_text => true) + message.title length: 70 end def first_photo_url(*args) @@ -142,7 +133,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 diff --git a/app/presenters/o_embed_presenter.rb b/app/presenters/o_embed_presenter.rb index a3e875d30..b3788ca50 100644 --- a/app/presenters/o_embed_presenter.rb +++ b/app/presenters/o_embed_presenter.rb @@ -1,6 +1,5 @@ class OEmbedPresenter include PostsHelper - include ActionView::Helpers::TextHelper def initialize(post, opts = {}) @post = post @@ -13,14 +12,14 @@ class OEmbedPresenter def as_json(opts={}) { - :provider_name => "Diaspora", + :provider_name => "Diaspora", :provider_url => AppConfig.pod_uri.to_s, :type => 'rich', :version => '1.0', :title => post_title, :author_name => post_author, :author_url => post_author_url, - :width => @opts.fetch(:maxwidth, 516), + :width => @opts.fetch(:maxwidth, 516), :height => @opts.fetch(:maxheight, 320), :html => iframe_html } diff --git a/app/presenters/post_presenter.rb b/app/presenters/post_presenter.rb index fa179250a..5c6e410d2 100644 --- a/app/presenters/post_presenter.rb +++ b/app/presenters/post_presenter.rb @@ -1,6 +1,5 @@ class PostPresenter include PostsHelper - include ActionView::Helpers::TextHelper attr_accessor :post, :current_user @@ -47,7 +46,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 diff --git a/app/views/comments/_comment.mobile.haml b/app/views/comments/_comment.mobile.haml index 342d2f777..abe2dddbf 100644 --- a/app/views/comments/_comment.mobile.haml +++ b/app/views/comments/_comment.mobile.haml @@ -16,4 +16,4 @@ = timeago(comment.created_at ? comment.created_at : Time.now) %div{:class => direction_for(comment.text)} - = markdownify(comment) + = comment.message.markdownified diff --git a/app/views/messages/_message.haml b/app/views/messages/_message.haml index 8c38eafa2..94b23aaa1 100644 --- a/app/views/messages/_message.haml +++ b/app/views/messages/_message.haml @@ -11,4 +11,4 @@ = timeago(message.created_at) %div{ :class => direction_for(message.text) } - = markdownify(message, :oembed => true) + = message.message.markdownified diff --git a/app/views/people/_profile_sidebar.html.haml b/app/views/people/_profile_sidebar.html.haml index 81827ea4b..1cb71e448 100644 --- a/app/views/people/_profile_sidebar.html.haml +++ b/app/views/people/_profile_sidebar.html.haml @@ -15,35 +15,35 @@ .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' .white_bar - + - if @contact.mutual? - - + + .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' .white_bar - + .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? - - %br + + %br -if contact.sharing? || person == current_user.person %ul#profile_information - + - unless person.bio.blank? %li %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 @@ -66,7 +66,7 @@ = image_tag(photo.url(:thumb_small)) %br = link_to t('layouts.header.view_all'), person_photos_path(person) - + - if person == current_user.person %li.image_list %h4 diff --git a/app/views/status_messages/_status_message.mobile.haml b/app/views/status_messages/_status_message.mobile.haml index dbed137d2..5aba5358d 100644 --- a/app/views/status_messages/_status_message.mobile.haml +++ b/app/views/status_messages/_status_message.mobile.haml @@ -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 diff --git a/app/views/users/public.atom.builder b/app/views/users/public.atom.builder index 90cbb1422..8dfd609d1 100644 --- a/app/views/users/public.atom.builder +++ b/app/views/users/public.atom.builder @@ -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 diff --git a/config/initializers/load_libraries.rb b/config/initializers/load_libraries.rb index 327616125..29271dcfc 100644 --- a/config/initializers/load_libraries.rb +++ b/config/initializers/load_libraries.rb @@ -13,9 +13,6 @@ require 'typhoeus' # Presenters require 'post_presenter' -# Helpers -require 'markdownify_helper' - # Our libs require 'collect_user_photos' require 'diaspora' diff --git a/lib/diaspora.rb b/lib/diaspora.rb index db63f94c8..161dbae8f 100644 --- a/lib/diaspora.rb +++ b/lib/diaspora.rb @@ -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' diff --git a/lib/diaspora/markdownify/html.rb b/lib/diaspora/markdownify/html.rb index c31ddbc0c..60209dcd6 100644 --- a/lib/diaspora/markdownify/html.rb +++ b/lib/diaspora/markdownify/html.rb @@ -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" }) + def autolink link, type + auto_link(link, link: :urls, html: { target: "_blank" }) end - end end end diff --git a/lib/diaspora/message_renderer.rb b/lib/diaspora/message_renderer.rb new file mode 100644 index 000000000..9a62fe661 --- /dev/null +++ b/lib/diaspora/message_renderer.rb @@ -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
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] :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(?.{1,200})\n(?:={1,200}|-{1,200})(?:\r?\n|$)/ =~ @raw_message.lstrip + setext_content + # Atx-style header + elsif /\A\#{1,6}\s+(?.{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 diff --git a/spec/helpers/markdownify_helper_spec.rb b/spec/helpers/markdownify_helper_spec.rb deleted file mode 100644 index 130bc1048..000000000 --- a/spec/helpers/markdownify_helper_spec.rb +++ /dev/null @@ -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("").should == - "

alert('XSS is evil')

\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{#markdown} - 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 #multi_word 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 #multi_word_tag 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 == %{

Test #tag?
\nhttps://joindiaspora.com

\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 diff --git a/spec/helpers/notifier_helper_spec.rb b/spec/helpers/notifier_helper_spec.rb index 23b178101..9f3e887e9 100644 --- a/spec/helpers/notifier_helper_spec.rb +++ b/spec/helpers/notifier_helper_spec.rb @@ -5,8 +5,6 @@ require 'spec_helper' describe NotifierHelper do - include MarkdownifyHelper - describe '#post_message' do before do # post for truncate test diff --git a/spec/helpers/posts_helper_spec.rb b/spec/helpers/posts_helper_spec.rb index 3d31563cc..1d1d6edee 100644 --- a/spec/helpers/posts_helper_spec.rb +++ b/spec/helpers/posts_helper_spec.rb @@ -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 diff --git a/spec/lib/diaspora/message_renderer_spec.rb b/spec/lib/diaspora/message_renderer_spec.rb new file mode 100644 index 000000000..0366720eb --- /dev/null +++ b/spec/lib/diaspora/message_renderer_spec.rb @@ -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 = " " + + 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("").markdownified + ).to eq "

alert('XSS is evil')

\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{#markdown} + end + + it 'should leave multi-underscore tags intact' do + expect( + message("Here is a #multi_word tag").markdownified + ).to match %r{Here is a #multi_word tag} + + expect( + message("Here is a #multi_word_tag yo").markdownified + ).to match %r{Here is a #multi_word_tag 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 %{

Test #tag?
\nhttps://joindiaspora.com

\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 "

#{entities}

\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 diff --git a/spec/mailers/notifier_spec.rb b/spec/mailers/notifier_spec.rb index 47b52139e..1f59a4bb2 100644 --- a/spec/mailers/notifier_spec.rb +++ b/spec/mailers/notifier_spec.rb @@ -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 diff --git a/spec/models/services/facebook_spec.rb b/spec/models/services/facebook_spec.rb index 2c1d9d882..221ea5a3c 100644 --- a/spec/models/services/facebook_spec.rb +++ b/spec/models/services/facebook_spec.rb @@ -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 diff --git a/spec/models/services/twitter_spec.rb b/spec/models/services/twitter_spec.rb index 463ae8957..5a6bfecbd 100644 --- a/spec/models/services/twitter_spec.rb +++ b/spec/models/services/twitter_spec.rb @@ -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 diff --git a/spec/models/status_message_spec.rb b/spec/models/status_message_spec.rb index 7ee8b4bc5..221bd4966 100644 --- a/spec/models/status_message_spec.rb +++ b/spec/models/status_message_spec.rb @@ -150,18 +150,6 @@ STR @sm = FactoryGirl.create(:status_message, :text => @test_string ) end - describe '#formatted_message' do - it 'escapes the message' do - xss = " " - @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) diff --git a/spec/presenters/post_presenter_spec.rb b/spec/presenters/post_presenter_spec.rb index dca62936e..19d2bdafb 100644 --- a/spec/presenters/post_presenter_spec.rb +++ b/spec/presenters/post_presenter_spec.rb @@ -65,32 +65,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