diaspora/lib/diaspora/message_renderer.rb
Dennis Schubert fd63fca7d5 Add Camo to MessageRenderer#markdownified
for mobile views
2014-11-09 07:29:36 +01:00

248 lines
7.8 KiB
Ruby

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
def escape
if options[:escape]
@message = ERB::Util.html_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(/&amp;(#[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
def camo_urls
@message = Diaspora::Camo::from_markdown(@message)
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 plain_text_for_json opts={}
process(opts) {
camo_urls if AppConfig.privacy.camo.proxy_markdown_images?
}
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
camo_urls if AppConfig.privacy.camo.proxy_markdown_images?
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
# Extracts all the urls from the raw message and return them in the form of a string
# Different URLs are seperated with a space
def urls
@urls ||= Twitter::Extractor.extract_urls(@raw_message)
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