123456789_123456789_123456789_123456789_123456789_

Class: ActionText::Content

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
self, Rendering, Serialization, ContentHelper
Inherits: Object
Defined in: actiontext/lib/action_text/content.rb

Overview

The Content class wraps an HTML fragment to add support for parsing, rendering and serialization. It can be used to extract links and attachments, convert the fragment to plain text, or serialize the fragment to the database.

The ActionText::RichText record serializes the body attribute as Content.

class Message < ActiveRecord::Base
  has_rich_text :content
end

message = Message.create!(content: "<h1>Funny times!</h1>")
body = message.content.body # => #<ActionText::Content "<div class=\"trix-conte...">
body.to_s # => "<h1>Funny times!</h1>"
body.to_plain_text # => "Funny times!"

Rendering - Attributes & Methods

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Serialization - Included

Constructor Details

.new(content = nil, options = {}) ⇒ Content

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 39

def initialize(content = nil, options = {})
  options.with_defaults! canonicalize: true

  if options[:canonicalize]
    @fragment = self.class.fragment_by_canonicalizing_content(content)
  else
    @fragment = ActionText::Fragment.wrap(content)
  end
end

Class Method Details

.fragment_by_canonicalizing_content(content)

[ GitHub ]

Instance Attribute Details

#deconstruct (readonly)

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 27

delegate :deconstruct, to: :fragment

#fragment (readonly)

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 25

attr_reader :fragment

#html_safe (readonly)

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 28

delegate :blank?, :empty?, :html_safe, :present?, to: :to_html # Delegating to to_html to avoid including the layout

#render (readonly)

[ GitHub ]

  
# File 'actiontext/lib/action_text/rendering.rb', line 11

delegate :render, to: :class

Instance Method Details

#==(other)

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 202

def ==(other)
  if self.class == other.class
    to_html == other.to_html
  elsif other.is_a?(self.class)
    to_s == other.to_s
  end
end

#append_attachables(attachables)

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 92

def append_attachables(attachables)
  attachments = ActionText::Attachment.from_attachables(attachables)
  self.class.new([self.to_s.presence, *attachments].compact.join("\n"))
end

#as_json

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 194

def as_json(*)
  to_html
end

#attachables

Extracts Attachable objects from the HTML fragment:

attachable = ActiveStorage::Blob.first
html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}" caption="Captioned"></action-text-attachment>)
content = ActionText::Content.new(html)
content.attachables # => [attachable]
[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 86

def attachables
  @attachables ||= attachment_nodes.map do |node|
    ActionText::Attachable.from_node(node)
  end
end

#attachment_for_node(node, with_full_attributes: true) (private)

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 219

def attachment_for_node(node, with_full_attributes: true)
  attachment = ActionText::Attachment.from_node(node)
  with_full_attributes ? attachment.with_full_attributes : attachment
end

#attachment_galleries

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 70

def attachment_galleries
  @attachment_galleries ||= attachment_gallery_nodes.map do |node|
    attachment_gallery_for_node(node)
  end
end

#attachment_nodes (private)

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 211

def attachment_nodes
  @attachment_nodes ||= fragment.find_all(ActionText::Attachment.tag_name)
end

#attachments

Extracts Attachment objects from the HTML fragment:

attachable = ActiveStorage::Blob.first
html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}" caption="Captioned"></action-text-attachment>)
content = ActionText::Content.new(html)
content.attachments # => [#<ActionText::Attachment attachable=#<ActiveStorage::Blob...
[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 64

def attachments
  @attachments ||= attachment_nodes.map do |node|
    attachment_for_node(node)
  end
end

#blank? ⇒ Boolean

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 28

delegate :blank?, :empty?, :html_safe, :present?, to: :to_html # Delegating to to_html to avoid including the layout

#empty? ⇒ Boolean

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 28

delegate :blank?, :empty?, :html_safe, :present?, to: :to_html # Delegating to to_html to avoid including the layout

#inspect

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 198

def inspect
  "#<#{self.class.name} #{to_html.truncate(25).inspect}>"
end

#present? ⇒ Boolean

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 28

delegate :blank?, :empty?, :html_safe, :present?, to: :to_html # Delegating to to_html to avoid including the layout

#render_attachment_galleries(&block)

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 108

def render_attachment_galleries(&block)
  content = ActionText::AttachmentGallery.fragment_by_replacing_attachment_gallery_nodes(fragment) do |node|
    block.call(attachment_gallery_for_node(node))
  end
  self.class.new(content, canonicalize: false)
end

#render_attachments(**options, &block)

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 97

def render_attachments(**options, &block)
  content = fragment.replace(ActionText::Attachment.tag_name) do |node|
    if node.key?("content")
      sanitized_content = sanitize_content_attachment(node.remove_attribute("content").to_s)
      node["content"] = sanitized_content if sanitized_content.present?
    end
    block.call(attachment_for_node(node, **options))
  end
  self.class.new(content, canonicalize: false)
end

#to_editor_html

This method is for internal use only.
[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 164

def to_editor_html # :nodoc:
  canonical_content = render_attachments(&:to_editor_attachment)
  canonical_fragment = Fragment.wrap(canonical_content.fragment)

  RichText.editor.as_editable(canonical_fragment).to_html
end

#to_html

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 171

def to_html
  fragment.to_html
end

#to_markdown(attachment_links: false)

Returns a Markdown version of the markup contained by the content.

content = ActionText::Content.new("<h1>Funny times!</h1>")
content.to_markdown # => "# Funny times!"

content = ActionText::Content.new("<p>Hello <strong>world</strong></p>")
content.to_markdown # => "Hello **world**"

When attachment_links is true, ::ActiveStorage blob attachments generate Markdown links with URLs. This requires a rendering context (e.g., controller or mailer action) and will raise if URL generation fails.

NOTE: The returned string is not HTML safe and should not be rendered in browsers without additional sanitization.

NOTE: Links with unsafe protocols like javascript: are stripped from the returned Markdown on a best-effort basis. Markdown renderers do not all parse Markdown the same way, so HTML rendered from the Markdown should not be displayed in browsers without additional sanitization.

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 153

def to_markdown(attachment_links: false)
  render_attachments(with_full_attributes: false) { |attachment|
    ActionText::MarkdownConversion.render_attachment(attachment, attachment_links: attachment_links)
  }.fragment.to_markdown
end

#to_partial_path

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 179

def to_partial_path
  "action_text/contents/content"
end

#to_plain_text

Returns a plain-text version of the markup contained by the content, with tags removed but HTML entities encoded.

content = ActionText::Content.new("<h1>Funny times!</h1>")
content.to_plain_text # => "Funny times!"

content = ActionText::Content.new("<div onclick='action()'>safe<script>unsafe</script></div>")
content.to_plain_text # => "safe"

NOTE: that the returned string is not HTML safe and should not be rendered in browsers without additional sanitization.

content = ActionText::Content.new("&lt;script&gt;alert()&lt;/script&gt;")
content.to_plain_text # => "<script>alert()</script>"
ActionText::ContentHelper.sanitizer.sanitize(content.to_plain_text) # => ""
[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 130

def to_plain_text
  render_attachments(with_full_attributes: false, &:to_plain_text).fragment.to_plain_text
end

#to_rendered_html_with_layout

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 175

def to_rendered_html_with_layout
  render layout: "action_text/contents/content", partial: to_partial_path, formats: :html, locals: { content: self }
end

#to_s

Safely transforms Content into an HTML String.

content = ActionText::Content.new("<h1>Funny times!</h1>")
content.to_s # => "<h1>Funny times!</h1>"

content = ActionText::Content.new("<div onclick='action()'>safe<script>unsafe</script></div>")
content.to_s # => "<div>safeunsafe</div>"
[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 190

def to_s
  to_rendered_html_with_layout
end

#to_trix_html

[ GitHub ]

  
# File 'actiontext/lib/action_text/content.rb', line 159

def to_trix_html
  to_editor_html
end