Module: ApplicationHTMLFormattersHelper

Included in:
ApplicationFormattersHelper
Defined in:
app/helpers/application_html_formatters_helper.rb

Constant Summary collapse

DefaultPipelineOptions =
{
  css_class: 'codehilite',
  replace_br: true
}.freeze
DefaultPipeline =

The default pipeline, used by both text and HTML pipelines.

HTML::Pipeline.new([
  HTML::Pipeline::AutolinkFilter,
  HTML::Pipeline::RougeFilter
], DefaultPipelineOptions)
VIDEO_URL_WHITELIST =

List of video hosting site URLs to allow

Regexp.union(
  /\A(?:https?:)?\/\/(?:www\.)?youtube\.com\//,
  /\A(?:https?:)?\/\/(?:www\.)?youtu.be\//,
  /\A(?:https?:)?\/\/(?:www\.)?vimeo\.com\//,
  /\A(?:https?:)?\/\/(?:www\.)?vine\.co\//,
  /\A(?:https?:)?\/\/(?:www\.)?instagram\.com\//,
  /\A(?:https?:)?\/\/(?:www\.)?dailymotion\.com\//,
  /\A(?:https?:)?\/\/(?:www\.)?youku\.com\//
)
VIDEO_WHITELIST_TRANSFORMER =

Transformer to whitelist iframes containing embedded video content

lambda do |env|
  node, node_name = env[:node], env[:node_name]

  return if env[:is_whitelisted] || !node.element?

  return unless node_name == 'iframe'
  return unless node['src']&.match VIDEO_URL_WHITELIST

  Sanitize.node!(node, elements: ['iframe'],
                       attributes: {
                         'iframe' => ['allowfullscreen', 'frameborder', 'height', 'src', 'width']
                       })

  { node_whitelist: [node] }
end
SANITIZATION_FILTER_WHITELIST =

SanitizationFilter Custom Options

begin
  list = HTML::Pipeline::SanitizationFilter::WHITELIST
  list[:protocols]['img']['src'] |= ['data']
  list[:elements] |= ['span', 'font', 'u']
  list[:attributes][:all] |= ['style']
  list[:attributes]['font'] = ['face']
  list[:attributes]['table'] = ['class']
  list[:css] = { properties: [
    'background-color', 'color', 'float', 'font-family', 'height', 'margin',
    'margin-bottom', 'margin-left', 'margin-right', 'margin-top', 'text-align', 'width'
  ] }
  list[:transformers] |= [VIDEO_WHITELIST_TRANSFORMER]
  list
end
HTMLSanitizerOptions =

The HTML sanitizer options to use.

{
  whitelist: SANITIZATION_FILTER_WHITELIST
}.freeze
HTMLSanitizerPipeline =

The HTML sanitizer to use.

HTML::Pipeline.new([HTML::Pipeline::SanitizationFilter],
HTMLSanitizerOptions)
DefaultHTMLPipelineOptions =

The default HTML pipeline options.

DefaultPipelineOptions.merge(HTMLSanitizerOptions).freeze
DefaultHTMLPipeline =

The default HTML pipeline.

HTML::Pipeline.new(HTMLSanitizerPipeline.filters +
DefaultPipeline.filters,
DefaultHTMLPipelineOptions)
DefaultCodePipelineOptions =

The Code formatter options to use.

DefaultPipelineOptions.merge(css_table_class: 'table').freeze
MAX_CODE_SIZE =

Constants that defines the size/lines limit of the code

50 * 1024
MAX_CODE_LINES =

50 KB

1000

Instance Method Summary collapse

Instance Method Details

#default_code_pipeline(starting_line_number = 1) ⇒ HTML::Pipeline

The Code formatter pipeline.

Parameters:

  • starting_line_number (Integer) (defaults to: 1)

    The line number of the first line, default is 1.

Returns:

  • (HTML::Pipeline)

90
91
92
93
94
# File 'app/helpers/application_html_formatters_helper.rb', line 90

def default_code_pipeline(starting_line_number = 1)
  HTML::Pipeline.new(DefaultPipeline.filters +
                       [PreformattedTextLineNumbersFilter],
                     DefaultCodePipelineOptions.merge(line_start: starting_line_number))
end

#format_code_block(code, language = nil, start_line = 1) ⇒ Object

Syntax highlights and adds lines numbers to the given code fragment.

This filter will normalise all line endings to Unix format (\n) for use with the Rouge highlighter.

Parameters:

  • code (String)

    The code to syntax highlight.

  • language (Coursemology::Polyglot::Language) (defaults to: nil)

    The language to highlight the code block with.

  • start_line (Integer) (defaults to: 1)

    The line number of the first line, default is 1. This should be provided if the code fragment does not start on the first line.


119
120
121
122
123
124
125
126
127
# File 'app/helpers/application_html_formatters_helper.rb', line 119

def format_code_block(code, language = nil, start_line = 1)
  if code_size_exceeds_limit?(code)
    (:div, class: 'alert alert-warning') do
      I18n.t('layouts.code_formatter.size_too_big')
    end
  else
    sanitize_and_format_code(code, language, start_line)
  end
end

#format_html(text) ⇒ String

Sanitises and formats the given user-input string. The string is assumed to contain HTML markup.

Parameters:

  • text (String)

    The text to display

Returns:

  • (String)

105
106
107
# File 'app/helpers/application_html_formatters_helper.rb', line 105

def format_html(text)
  format_with_pipeline(DefaultHTMLPipeline, text)
end

#highlight_code_block(code, language = nil) ⇒ Object

Syntax highlights the given code fragment without adding line numbers.

This filter will normalise all line endings to Unix format (\n) for use with the Rouge highlighter.

Parameters:

  • code (String)

    The code to syntax highlight.

  • language (Coursemology::Polyglot::Language) (defaults to: nil)

    The language to highlight the code block with.


137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'app/helpers/application_html_formatters_helper.rb', line 137

def highlight_code_block(code, language = nil)
  return if code_size_exceeds_limit?(code)

  code = html_escape(code) unless code.html_safe?
  code = code.gsub(/\r\n|\r/, "\n").html_safe

  code = (:pre, lang: language ? language.rouge_lexer : nil) do
    (:code) { code }
  end

  pipeline = HTML::Pipeline.new(DefaultPipeline.filters +
                                [PreformattedTextLineSplitFilter],
                                DefaultCodePipelineOptions)
  format_with_pipeline(pipeline, code)
end

#sanitize(text) ⇒ Object

Replaces the Rails sanitizer with the one configured with HTML Pipeline.


97
98
99
# File 'app/helpers/application_html_formatters_helper.rb', line 97

def sanitize(text)
  format_with_pipeline(HTMLSanitizerPipeline, text)
end