actiontext 7.1.3 → 7.2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -73
- data/app/assets/javascripts/trix.js +92 -30
- data/app/assets/stylesheets/trix.css +3 -1
- data/app/helpers/action_text/content_helper.rb +11 -0
- data/app/helpers/action_text/tag_helper.rb +38 -28
- data/app/models/action_text/encrypted_rich_text.rb +2 -2
- data/app/models/action_text/record.rb +2 -0
- data/app/models/action_text/rich_text.rb +58 -26
- data/db/migrate/20180528164100_create_action_text_tables.rb +1 -1
- data/lib/action_text/attachable.rb +35 -33
- data/lib/action_text/attachables/content_attachment.rb +2 -0
- data/lib/action_text/attachables/missing_attachable.rb +2 -0
- data/lib/action_text/attachables/remote_image.rb +2 -0
- data/lib/action_text/attachment.rb +27 -25
- data/lib/action_text/attachment_gallery.rb +2 -0
- data/lib/action_text/attachments/caching.rb +2 -0
- data/lib/action_text/attachments/minification.rb +2 -0
- data/lib/action_text/attachments/trix_conversion.rb +2 -0
- data/lib/action_text/attribute.rb +36 -22
- data/lib/action_text/content.rb +51 -27
- data/lib/action_text/deprecator.rb +2 -0
- data/lib/action_text/encryption.rb +2 -0
- data/lib/action_text/engine.rb +2 -0
- data/lib/action_text/fixture_set.rb +34 -34
- data/lib/action_text/fragment.rb +4 -0
- data/lib/action_text/gem_version.rb +6 -4
- data/lib/action_text/html_conversion.rb +2 -0
- data/lib/action_text/plain_text_conversion.rb +8 -1
- data/lib/action_text/rendering.rb +2 -0
- data/lib/action_text/serialization.rb +2 -0
- data/lib/action_text/system_test_helper.rb +20 -17
- data/lib/action_text/trix_attachment.rb +2 -0
- data/lib/action_text/version.rb +3 -1
- data/lib/action_text.rb +1 -1
- data/lib/generators/action_text/install/install_generator.rb +10 -3
- data/lib/rails/generators/test_unit/install_generator.rb +2 -0
- data/package.json +2 -2
- metadata +18 -18
@@ -1,55 +1,87 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionText
|
4
|
-
#
|
6
|
+
# # Action Text RichText
|
5
7
|
#
|
6
|
-
# The RichText record holds the content produced by the Trix editor in a
|
7
|
-
# It also holds all the references to the embedded
|
8
|
-
#
|
9
|
-
#
|
8
|
+
# The RichText record holds the content produced by the Trix editor in a
|
9
|
+
# serialized `body` attribute. It also holds all the references to the embedded
|
10
|
+
# files, which are stored using Active Storage. This record is then associated
|
11
|
+
# with the Active Record model the application desires to have rich text content
|
12
|
+
# using the `has_rich_text` class method.
|
10
13
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
+
# class Message < ActiveRecord::Base
|
15
|
+
# has_rich_text :content
|
16
|
+
# end
|
14
17
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
18
|
+
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
19
|
+
# message.content #=> #<ActionText::RichText....
|
20
|
+
# message.content.to_s # => "<h1>Funny times!</h1>"
|
21
|
+
# message.content.to_plain_text # => "Funny times!"
|
19
22
|
#
|
23
|
+
# message = Message.create!(content: "<div onclick='action()'>safe<script>unsafe</script></div>")
|
24
|
+
# message.content #=> #<ActionText::RichText....
|
25
|
+
# message.content.to_s # => "<div>safeunsafe</div>"
|
26
|
+
# message.content.to_plain_text # => "safeunsafe"
|
20
27
|
class RichText < Record
|
21
|
-
|
28
|
+
##
|
29
|
+
# :method: to_s
|
30
|
+
#
|
31
|
+
# Safely transforms RichText into an HTML String.
|
32
|
+
#
|
33
|
+
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
34
|
+
# message.content.to_s # => "<h1>Funny times!</h1>"
|
35
|
+
#
|
36
|
+
# message = Message.create!(content: "<div onclick='action()'>safe<script>unsafe</script></div>")
|
37
|
+
# message.content.to_s # => "<div>safeunsafe</div>"
|
22
38
|
|
23
39
|
serialize :body, coder: ActionText::Content
|
24
40
|
delegate :to_s, :nil?, to: :body
|
25
41
|
|
42
|
+
##
|
43
|
+
# :method: record
|
44
|
+
#
|
45
|
+
# Returns the associated record.
|
26
46
|
belongs_to :record, polymorphic: true, touch: true
|
47
|
+
|
48
|
+
##
|
49
|
+
# :method: embeds
|
50
|
+
#
|
51
|
+
# Returns the `ActiveStorage::Blob`s of the embedded files.
|
27
52
|
has_many_attached :embeds
|
28
53
|
|
29
54
|
before_save do
|
30
55
|
self.embeds = body.attachables.grep(ActiveStorage::Blob).uniq if body.present?
|
31
56
|
end
|
32
57
|
|
33
|
-
# Returns
|
58
|
+
# Returns a plain-text version of the markup contained by the `body` attribute,
|
59
|
+
# with tags removed but HTML entities encoded.
|
60
|
+
#
|
61
|
+
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
62
|
+
# message.content.to_plain_text # => "Funny times!"
|
63
|
+
#
|
64
|
+
# NOTE: that the returned string is not HTML safe and should not be rendered in
|
65
|
+
# browsers.
|
34
66
|
#
|
35
|
-
#
|
36
|
-
#
|
67
|
+
# message = Message.create!(content: "<script>alert()</script>")
|
68
|
+
# message.content.to_plain_text # => "<script>alert()</script>"
|
37
69
|
def to_plain_text
|
38
70
|
body&.to_plain_text.to_s
|
39
71
|
end
|
40
72
|
|
41
|
-
# Returns the
|
73
|
+
# Returns the `body` attribute in a format that makes it editable in the Trix
|
42
74
|
# editor. Previews of attachments are rendered inline.
|
43
75
|
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
76
|
+
# content = "<h1>Funny Times!</h1><figure data-trix-attachment='{\"sgid\":\"..."\}'></figure>"
|
77
|
+
# message = Message.create!(content: content)
|
78
|
+
# message.content.to_trix_html # =>
|
79
|
+
# # <div class="trix-content">
|
80
|
+
# # <h1>Funny times!</h1>
|
81
|
+
# # <figure data-trix-attachment='{\"sgid\":\"..."\}'>
|
82
|
+
# # <img src="http://example.org/rails/active_storage/.../funny.jpg">
|
83
|
+
# # </figure>
|
84
|
+
# # </div>
|
53
85
|
def to_trix_html
|
54
86
|
body&.to_trix_html
|
55
87
|
end
|
@@ -20,6 +20,6 @@ class CreateActionTextTables < ActiveRecord::Migration[6.0]
|
|
20
20
|
setting = config.options[config.orm][:primary_key_type]
|
21
21
|
primary_key_type = setting || :primary_key
|
22
22
|
foreign_key_type = setting || :bigint
|
23
|
-
[primary_key_type, foreign_key_type]
|
23
|
+
[ primary_key_type, foreign_key_type ]
|
24
24
|
end
|
25
25
|
end
|
@@ -1,31 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionText
|
4
|
-
#
|
6
|
+
# # Action Text Attachable
|
5
7
|
#
|
6
8
|
# Include this module to make a record attachable to an ActionText::Content.
|
7
9
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
10
|
+
# class Person < ApplicationRecord
|
11
|
+
# include ActionText::Attachable
|
12
|
+
# end
|
11
13
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
14
|
+
# person = Person.create! name: "Javan"
|
15
|
+
# html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
|
16
|
+
# content = ActionText::Content.new(html)
|
17
|
+
# content.attachables # => [person]
|
16
18
|
module Attachable
|
17
19
|
extend ActiveSupport::Concern
|
18
20
|
|
19
21
|
LOCATOR_NAME = "attachable"
|
20
22
|
|
21
23
|
class << self
|
22
|
-
# Extracts the
|
24
|
+
# Extracts the `ActionText::Attachable` from the attachment HTML node:
|
23
25
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
26
|
+
# person = Person.create! name: "Javan"
|
27
|
+
# html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
|
28
|
+
# fragment = ActionText::Fragment.wrap(html)
|
29
|
+
# attachment_node = fragment.find_all(ActionText::Attachment.tag_name).first
|
30
|
+
# ActionText::Attachable.from_node(attachment_node) # => person
|
29
31
|
def from_node(node)
|
30
32
|
if attachable = attachable_from_sgid(node["sgid"])
|
31
33
|
attachable
|
@@ -57,23 +59,23 @@ module ActionText
|
|
57
59
|
ActionText::Attachable.from_attachable_sgid(sgid, only: self)
|
58
60
|
end
|
59
61
|
|
60
|
-
# Returns the path to the partial that is used for rendering missing
|
61
|
-
# Defaults to "action_text/attachables/missing_attachable".
|
62
|
+
# Returns the path to the partial that is used for rendering missing
|
63
|
+
# attachables. Defaults to "action_text/attachables/missing_attachable".
|
62
64
|
#
|
63
65
|
# Override to render a different partial:
|
64
66
|
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
67
|
+
# class User < ApplicationRecord
|
68
|
+
# def self.to_missing_attachable_partial_path
|
69
|
+
# "users/missing_attachable"
|
70
|
+
# end
|
68
71
|
# end
|
69
|
-
# end
|
70
72
|
def to_missing_attachable_partial_path
|
71
73
|
ActionText::Attachables::MissingAttachable::DEFAULT_PARTIAL_PATH
|
72
74
|
end
|
73
75
|
end
|
74
76
|
|
75
|
-
# Returns the Signed Global ID for the attachable. The purpose of the ID is
|
76
|
-
#
|
77
|
+
# Returns the Signed Global ID for the attachable. The purpose of the ID is set
|
78
|
+
# to 'attachable' so it can't be reused for other purposes.
|
77
79
|
def attachable_sgid
|
78
80
|
to_sgid(expires_in: nil, for: LOCATOR_NAME).to_s
|
79
81
|
end
|
@@ -98,30 +100,30 @@ module ActionText
|
|
98
100
|
false
|
99
101
|
end
|
100
102
|
|
101
|
-
# Returns the path to the partial that is used for rendering the attachable
|
102
|
-
#
|
103
|
+
# Returns the path to the partial that is used for rendering the attachable in
|
104
|
+
# Trix. Defaults to `to_partial_path`.
|
103
105
|
#
|
104
106
|
# Override to render a different partial:
|
105
107
|
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
108
|
+
# class User < ApplicationRecord
|
109
|
+
# def to_trix_content_attachment_partial_path
|
110
|
+
# "users/trix_content_attachment"
|
111
|
+
# end
|
109
112
|
# end
|
110
|
-
# end
|
111
113
|
def to_trix_content_attachment_partial_path
|
112
114
|
to_partial_path
|
113
115
|
end
|
114
116
|
|
115
117
|
# Returns the path to the partial that is used for rendering the attachable.
|
116
|
-
# Defaults to
|
118
|
+
# Defaults to `to_partial_path`.
|
117
119
|
#
|
118
120
|
# Override to render a different partial:
|
119
121
|
#
|
120
|
-
#
|
121
|
-
#
|
122
|
-
#
|
122
|
+
# class User < ApplicationRecord
|
123
|
+
# def to_attachable_partial_path
|
124
|
+
# "users/attachable"
|
125
|
+
# end
|
123
126
|
# end
|
124
|
-
# end
|
125
127
|
def to_attachable_partial_path
|
126
128
|
to_partial_path
|
127
129
|
end
|
@@ -1,19 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "active_support/core_ext/object/try"
|
4
6
|
|
5
7
|
module ActionText
|
6
|
-
#
|
8
|
+
# # Action Text Attachment
|
7
9
|
#
|
8
10
|
# Attachments serialize attachables to HTML or plain text.
|
9
11
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
12
|
+
# class Person < ApplicationRecord
|
13
|
+
# include ActionText::Attachable
|
14
|
+
# end
|
13
15
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
16
|
+
# attachable = Person.create! name: "Javan"
|
17
|
+
# attachment = ActionText::Attachment.from_attachable(attachable)
|
18
|
+
# attachment.to_html # => "<action-text-attachment sgid=\"BAh7CEk..."
|
17
19
|
class Attachment
|
18
20
|
include Attachments::TrixConversion, Attachments::Minification, Attachments::Caching
|
19
21
|
|
@@ -82,29 +84,29 @@ module ActionText
|
|
82
84
|
|
83
85
|
# Converts the attachment to plain text.
|
84
86
|
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
87
|
+
# attachable = ActiveStorage::Blob.find_by filename: "racecar.jpg"
|
88
|
+
# attachment = ActionText::Attachment.from_attachable(attachable)
|
89
|
+
# attachment.to_plain_text # => "[racecar.jpg]"
|
88
90
|
#
|
89
|
-
# Use the
|
91
|
+
# Use the `caption` when set:
|
90
92
|
#
|
91
|
-
#
|
92
|
-
#
|
93
|
+
# attachment = ActionText::Attachment.from_attachable(attachable, caption: "Vroom vroom")
|
94
|
+
# attachment.to_plain_text # => "[Vroom vroom]"
|
93
95
|
#
|
94
96
|
# The presentation can be overridden by implementing the
|
95
|
-
#
|
97
|
+
# `attachable_plain_text_representation` method:
|
96
98
|
#
|
97
|
-
#
|
98
|
-
#
|
99
|
+
# class Person < ApplicationRecord
|
100
|
+
# include ActionText::Attachable
|
99
101
|
#
|
100
|
-
#
|
101
|
-
#
|
102
|
+
# def attachable_plain_text_representation
|
103
|
+
# "[#{name}]"
|
104
|
+
# end
|
102
105
|
# end
|
103
|
-
# end
|
104
106
|
#
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
107
|
+
# attachable = Person.create! name: "Javan"
|
108
|
+
# attachment = ActionText::Attachment.from_attachable(attachable)
|
109
|
+
# attachment.to_plain_text # => "[Javan]"
|
108
110
|
def to_plain_text
|
109
111
|
if respond_to?(:attachable_plain_text_representation)
|
110
112
|
attachable_plain_text_representation(caption)
|
@@ -115,9 +117,9 @@ module ActionText
|
|
115
117
|
|
116
118
|
# Converts the attachment to HTML.
|
117
119
|
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
#
|
120
|
+
# attachable = Person.create! name: "Javan"
|
121
|
+
# attachment = ActionText::Attachment.from_attachable(attachable)
|
122
|
+
# attachment.to_html # => "<action-text-attachment sgid=\"BAh7CEk...
|
121
123
|
def to_html
|
122
124
|
HtmlConversion.node_to_html(node)
|
123
125
|
end
|
@@ -1,39 +1,52 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionText
|
4
6
|
module Attribute
|
5
7
|
extend ActiveSupport::Concern
|
6
8
|
|
7
9
|
class_methods do
|
8
|
-
# Provides access to a dependent RichText model that holds the body and
|
9
|
-
#
|
10
|
+
# Provides access to a dependent RichText model that holds the body and
|
11
|
+
# attachments for a single named rich text attribute. This dependent attribute
|
12
|
+
# is lazily instantiated and will be auto-saved when it's been changed. Example:
|
13
|
+
#
|
14
|
+
# class Message < ActiveRecord::Base
|
15
|
+
# has_rich_text :content
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
19
|
+
# message.content? #=> true
|
20
|
+
# message.content.to_s # => "<h1>Funny times!</h1>"
|
21
|
+
# message.content.to_plain_text # => "Funny times!"
|
10
22
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
23
|
+
# The dependent RichText model will also automatically process attachments links
|
24
|
+
# as sent via the Trix-powered editor. These attachments are associated with the
|
25
|
+
# RichText model using Active Storage.
|
14
26
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# message.content.to_s # => "<h1>Funny times!</h1>"
|
18
|
-
# message.content.to_plain_text # => "Funny times!"
|
27
|
+
# If you wish to preload the dependent RichText model, you can use the named
|
28
|
+
# scope:
|
19
29
|
#
|
20
|
-
#
|
21
|
-
#
|
30
|
+
# Message.all.with_rich_text_content # Avoids N+1 queries when you just want the body, not the attachments.
|
31
|
+
# Message.all.with_rich_text_content_and_embeds # Avoids N+1 queries when you just want the body and attachments.
|
32
|
+
# Message.all.with_all_rich_text # Loads all rich text associations.
|
22
33
|
#
|
23
|
-
#
|
34
|
+
# #### Options
|
24
35
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
36
|
+
# * `:encrypted` - Pass true to encrypt the rich text attribute. The
|
37
|
+
# encryption will be non-deterministic. See
|
38
|
+
# `ActiveRecord::Encryption::EncryptableRecord.encrypts`. Default: false.
|
28
39
|
#
|
29
|
-
#
|
40
|
+
# * `:strict_loading` - Pass true to force strict loading. When omitted,
|
41
|
+
# `strict_loading:` will be set to the value of the
|
42
|
+
# `strict_loading_by_default` class attribute (false by default).
|
30
43
|
#
|
31
|
-
# * <tt>:encrypted</tt> - Pass true to encrypt the rich text attribute. The encryption will be non-deterministic. See
|
32
|
-
# +ActiveRecord::Encryption::EncryptableRecord.encrypts+. Default: false.
|
33
44
|
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
45
|
+
# Note: Action Text relies on polymorphic associations, which in turn store
|
46
|
+
# class names in the database. When renaming classes that use `has_rich_text`,
|
47
|
+
# make sure to also update the class names in the
|
48
|
+
# `action_text_rich_texts.record_type` polymorphic type column of the
|
49
|
+
# corresponding rows.
|
37
50
|
def has_rich_text(name, encrypted: false, strict_loading: strict_loading_by_default)
|
38
51
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
39
52
|
def #{name}
|
@@ -60,9 +73,10 @@ module ActionText
|
|
60
73
|
|
61
74
|
# Eager load all dependent RichText models in bulk.
|
62
75
|
def with_all_rich_text
|
63
|
-
|
76
|
+
includes(rich_text_association_names)
|
64
77
|
end
|
65
78
|
|
79
|
+
# Returns the names of all rich text associations.
|
66
80
|
def rich_text_association_names
|
67
81
|
reflect_on_all_associations(:has_one).collect(&:name).select { |n| n.start_with?("rich_text_") }
|
68
82
|
end
|
data/lib/action_text/content.rb
CHANGED
@@ -1,29 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionText
|
4
|
-
#
|
6
|
+
# # Action Text Content
|
5
7
|
#
|
6
|
-
# The
|
8
|
+
# The `ActionText::Content` class wraps an HTML fragment to add support for
|
7
9
|
# parsing, rendering and serialization. It can be used to extract links and
|
8
|
-
# attachments, convert the fragment to plain text, or serialize the fragment
|
9
|
-
#
|
10
|
+
# attachments, convert the fragment to plain text, or serialize the fragment to
|
11
|
+
# the database.
|
10
12
|
#
|
11
13
|
# The ActionText::RichText record serializes the `body` attribute as
|
12
|
-
#
|
14
|
+
# `ActionText::Content`.
|
13
15
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
16
|
+
# class Message < ActiveRecord::Base
|
17
|
+
# has_rich_text :content
|
18
|
+
# end
|
17
19
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
20
|
+
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
21
|
+
# body = message.content.body # => #<ActionText::Content "<div class=\"trix-conte...">
|
22
|
+
# body.to_s # => "<h1>Funny times!</h1>"
|
23
|
+
# body.to_plain_text # => "Funny times!"
|
22
24
|
class Content
|
23
|
-
include Rendering, Serialization
|
25
|
+
include Rendering, Serialization, ContentHelper
|
24
26
|
|
25
27
|
attr_reader :fragment
|
26
28
|
|
29
|
+
delegate :deconstruct, to: :fragment
|
27
30
|
delegate :blank?, :empty?, :html_safe, :present?, to: :to_html # Delegating to to_html to avoid including the layout
|
28
31
|
|
29
32
|
class << self
|
@@ -46,19 +49,19 @@ module ActionText
|
|
46
49
|
|
47
50
|
# Extracts links from the HTML fragment:
|
48
51
|
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
+
# html = '<a href="http://example.com/">Example</a>'
|
53
|
+
# content = ActionText::Content.new(html)
|
54
|
+
# content.links # => ["http://example.com/"]
|
52
55
|
def links
|
53
56
|
@links ||= fragment.find_all("a[href]").map { |a| a["href"] }.uniq
|
54
57
|
end
|
55
58
|
|
56
59
|
# Extracts +ActionText::Attachment+s from the HTML fragment:
|
57
60
|
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
61
|
+
# attachable = ActiveStorage::Blob.first
|
62
|
+
# html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}" caption="Captioned"></action-text-attachment>)
|
63
|
+
# content = ActionText::Content.new(html)
|
64
|
+
# content.attachments # => [#<ActionText::Attachment attachable=#<ActiveStorage::Blob...
|
62
65
|
def attachments
|
63
66
|
@attachments ||= attachment_nodes.map do |node|
|
64
67
|
attachment_for_node(node)
|
@@ -77,10 +80,10 @@ module ActionText
|
|
77
80
|
|
78
81
|
# Extracts +ActionText::Attachable+s from the HTML fragment:
|
79
82
|
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
83
|
+
# attachable = ActiveStorage::Blob.first
|
84
|
+
# html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}" caption="Captioned"></action-text-attachment>)
|
85
|
+
# content = ActionText::Content.new(html)
|
86
|
+
# content.attachables # => [attachable]
|
84
87
|
def attachables
|
85
88
|
@attachables ||= attachment_nodes.map do |node|
|
86
89
|
ActionText::Attachable.from_node(node)
|
@@ -94,6 +97,10 @@ module ActionText
|
|
94
97
|
|
95
98
|
def render_attachments(**options, &block)
|
96
99
|
content = fragment.replace(ActionText::Attachment.tag_name) do |node|
|
100
|
+
if node.key?("content")
|
101
|
+
sanitized_content = sanitize_content_attachment(node.remove_attribute("content").to_s)
|
102
|
+
node["content"] = sanitized_content if sanitized_content.present?
|
103
|
+
end
|
97
104
|
block.call(attachment_for_node(node, **options))
|
98
105
|
end
|
99
106
|
self.class.new(content, canonicalize: false)
|
@@ -106,10 +113,20 @@ module ActionText
|
|
106
113
|
self.class.new(content, canonicalize: false)
|
107
114
|
end
|
108
115
|
|
109
|
-
# Returns the
|
116
|
+
# Returns a plain-text version of the markup contained by the content, with tags
|
117
|
+
# removed but HTML entities encoded.
|
118
|
+
#
|
119
|
+
# content = ActionText::Content.new("<h1>Funny times!</h1>")
|
120
|
+
# content.to_plain_text # => "Funny times!"
|
121
|
+
#
|
122
|
+
# content = ActionText::Content.new("<div onclick='action()'>safe<script>unsafe</script></div>")
|
123
|
+
# content.to_plain_text # => "safeunsafe"
|
110
124
|
#
|
111
|
-
#
|
112
|
-
#
|
125
|
+
# NOTE: that the returned string is not HTML safe and should not be rendered in
|
126
|
+
# browsers.
|
127
|
+
#
|
128
|
+
# content = ActionText::Content.new("<script>alert()</script>")
|
129
|
+
# content.to_plain_text # => "<script>alert()</script>"
|
113
130
|
def to_plain_text
|
114
131
|
render_attachments(with_full_attributes: false, &:to_plain_text).fragment.to_plain_text
|
115
132
|
end
|
@@ -130,6 +147,13 @@ module ActionText
|
|
130
147
|
"action_text/contents/content"
|
131
148
|
end
|
132
149
|
|
150
|
+
# Safely transforms Content into an HTML String.
|
151
|
+
#
|
152
|
+
# content = ActionText::Content.new(content: "<h1>Funny times!</h1>")
|
153
|
+
# content.to_s # => "<h1>Funny times!</h1>"
|
154
|
+
#
|
155
|
+
# content = ActionText::Content.new("<div onclick='action()'>safe<script>unsafe</script></div>")
|
156
|
+
# content.to_s # => "<div>safeunsafe</div>"
|
133
157
|
def to_s
|
134
158
|
to_rendered_html_with_layout
|
135
159
|
end
|