Class: ActionText::Content

Inherits:
Object
  • Object
show all
Includes:
ContentHelper, Rendering, Serialization
Defined in:
lib/action_text/content.rb

Overview

# Action Text Content

The ‘ActionText::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 `ActionText::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!"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Serialization

#_dump

Methods included from ContentHelper

#render_action_text_attachment, #render_action_text_attachments, #render_action_text_content, #sanitize_action_text_content, #sanitize_content_attachment, #sanitizer_allowed_attributes, #sanitizer_allowed_tags

Constructor Details

#initialize(content = nil, options = {}) ⇒ Content

Returns a new instance of Content.



40
41
42
43
44
45
46
47
48
# File 'lib/action_text/content.rb', line 40

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

Instance Attribute Details

#fragmentObject (readonly)

Returns the value of attribute fragment.



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

def fragment
  @fragment
end

Class Method Details

.fragment_by_canonicalizing_content(content) ⇒ Object



33
34
35
36
37
# File 'lib/action_text/content.rb', line 33

def fragment_by_canonicalizing_content(content)
  fragment = ActionText::Attachment.fragment_by_canonicalizing_attachments(content)
  fragment = ActionText::AttachmentGallery.fragment_by_canonicalizing_attachment_galleries(fragment)
  fragment
end

Instance Method Details

#==(other) ⇒ Object



169
170
171
172
173
174
175
# File 'lib/action_text/content.rb', line 169

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) ⇒ Object



93
94
95
96
# File 'lib/action_text/content.rb', line 93

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

#as_jsonObject



161
162
163
# File 'lib/action_text/content.rb', line 161

def as_json(*)
  to_html
end

#attachablesObject

Extracts ActionText::Attachables 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]


87
88
89
90
91
# File 'lib/action_text/content.rb', line 87

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

#attachment_galleriesObject



71
72
73
74
75
# File 'lib/action_text/content.rb', line 71

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

#attachmentsObject

Extracts ActionText::Attachments 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...


65
66
67
68
69
# File 'lib/action_text/content.rb', line 65

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


77
78
79
# File 'lib/action_text/content.rb', line 77

def gallery_attachments
  @gallery_attachments ||= attachment_galleries.flat_map(&:attachments)
end

#inspectObject



165
166
167
# File 'lib/action_text/content.rb', line 165

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

Extracts links from the HTML fragment:

html = '<a href="http://example.com/">Example</a>'
content = ActionText::Content.new(html)
content.links # => ["http://example.com/"]


55
56
57
# File 'lib/action_text/content.rb', line 55

def links
  @links ||= fragment.find_all("a[href]").map { |a| a["href"] }.uniq
end

#render_attachment_galleries(&block) ⇒ Object



109
110
111
112
113
114
# File 'lib/action_text/content.rb', line 109

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) ⇒ Object



98
99
100
101
102
103
104
105
106
107
# File 'lib/action_text/content.rb', line 98

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_htmlObject



138
139
140
# File 'lib/action_text/content.rb', line 138

def to_html
  fragment.to_html
end

#to_partial_pathObject



146
147
148
# File 'lib/action_text/content.rb', line 146

def to_partial_path
  "action_text/contents/content"
end

#to_plain_textObject

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 # => "safeunsafe"

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

content = ActionText::Content.new("&lt;script&gt;alert()&lt;/script&gt;")
content.to_plain_text # => "<script>alert()</script>"


130
131
132
# File '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_layoutObject



142
143
144
# File 'lib/action_text/content.rb', line 142

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

#to_sObject

Safely transforms Content into an HTML String.

content = ActionText::Content.new(content: "<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>"


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

def to_s
  to_rendered_html_with_layout
end

#to_trix_htmlObject



134
135
136
# File 'lib/action_text/content.rb', line 134

def to_trix_html
  render_attachments(&:to_trix_attachment).to_html
end