1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
# frozen_string_literal: true
require "active_support/core_ext/module/attribute_accessors_per_thread"
module ActionText
class Content
include Serialization
thread_cattr_accessor :renderer
attr_reader :fragment
delegate :blank?, :empty?, :html_safe, :present?, to: :to_html # Delegating to to_html to avoid including the layout
class << self
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
end
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
def links
@links ||= fragment.find_all("a[href]").map { |a| a["href"] }.uniq
end
def attachments
@attachments ||= attachment_nodes.map do |node|
attachment_for_node(node)
end
end
def attachment_galleries
@attachment_galleries ||= attachment_gallery_nodes.map do |node|
attachment_gallery_for_node(node)
end
end
def gallery_attachments
@gallery_attachments ||= attachment_galleries.flat_map(&:attachments)
end
def attachables
@attachables ||= attachment_nodes.map do |node|
ActionText::Attachable.from_node(node)
end
end
def append_attachables(attachables)
attachments = ActionText::Attachment.from_attachables(attachables)
self.class.new([self.to_s.presence, *attachments].compact.join("\n"))
end
def render_attachments(**options, &block)
content = fragment.replace(ActionText::Attachment::SELECTOR) do |node|
block.call(attachment_for_node(node, **options))
end
self.class.new(content, canonicalize: false)
end
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
def to_plain_text
render_attachments(with_full_attributes: false, &:to_plain_text).fragment.to_plain_text
end
def to_trix_html
render_attachments(&:to_trix_attachment).to_html
end
def to_html
fragment.to_html
end
def to_rendered_html_with_layout
renderer.render(partial: "action_text/content/layout", locals: { content: self })
end
def to_s
to_rendered_html_with_layout
end
def as_json(*)
to_html
end
def inspect
"#<#{self.class.name} #{to_s.truncate(25).inspect}>"
end
def ==(other)
if other.is_a?(self.class)
to_s == other.to_s
end
end
private
def attachment_nodes
@attachment_nodes ||= fragment.find_all(ActionText::Attachment::SELECTOR)
end
def attachment_gallery_nodes
@attachment_gallery_nodes ||= ActionText::AttachmentGallery.find_attachment_gallery_nodes(fragment)
end
def attachment_for_node(node, with_full_attributes: true)
attachment = ActionText::Attachment.from_node(node)
with_full_attributes ? attachment.with_full_attributes : attachment
end
def attachment_gallery_for_node(node)
ActionText::AttachmentGallery.from_node(node)
end
end
end
ActiveSupport.run_load_hooks :action_text_content, ActionText::Content
|