actiontext 7.1.3.4 → 7.2.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actiontext might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -95
- data/app/helpers/action_text/content_helper.rb +2 -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 +46 -26
- 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 +2 -0
- 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 +15 -15
@@ -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
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)
|
@@ -107,10 +110,20 @@ module ActionText
|
|
107
110
|
self.class.new(content, canonicalize: false)
|
108
111
|
end
|
109
112
|
|
110
|
-
# Returns the
|
113
|
+
# Returns a plain-text version of the markup contained by the content, with tags
|
114
|
+
# removed but HTML entities encoded.
|
115
|
+
#
|
116
|
+
# content = ActionText::Content.new("<h1>Funny times!</h1>")
|
117
|
+
# content.to_plain_text # => "Funny times!"
|
118
|
+
#
|
119
|
+
# content = ActionText::Content.new("<div onclick='action()'>safe<script>unsafe</script></div>")
|
120
|
+
# content.to_plain_text # => "safeunsafe"
|
111
121
|
#
|
112
|
-
#
|
113
|
-
#
|
122
|
+
# NOTE: that the returned string is not HTML safe and should not be rendered in
|
123
|
+
# browsers.
|
124
|
+
#
|
125
|
+
# content = ActionText::Content.new("<script>alert()</script>")
|
126
|
+
# content.to_plain_text # => "<script>alert()</script>"
|
114
127
|
def to_plain_text
|
115
128
|
render_attachments(with_full_attributes: false, &:to_plain_text).fragment.to_plain_text
|
116
129
|
end
|
@@ -131,6 +144,13 @@ module ActionText
|
|
131
144
|
"action_text/contents/content"
|
132
145
|
end
|
133
146
|
|
147
|
+
# Safely transforms Content into an HTML String.
|
148
|
+
#
|
149
|
+
# content = ActionText::Content.new(content: "<h1>Funny times!</h1>")
|
150
|
+
# content.to_s # => "<h1>Funny times!</h1>"
|
151
|
+
#
|
152
|
+
# content = ActionText::Content.new("<div onclick='action()'>safe<script>unsafe</script></div>")
|
153
|
+
# content.to_s # => "<div>safeunsafe</div>"
|
134
154
|
def to_s
|
135
155
|
to_rendered_html_with_layout
|
136
156
|
end
|
data/lib/action_text/engine.rb
CHANGED
@@ -1,62 +1,62 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionText
|
4
|
-
#
|
6
|
+
# # Action Text FixtureSet
|
5
7
|
#
|
6
|
-
# Fixtures are a way of organizing data that you want to test against; in
|
7
|
-
#
|
8
|
+
# Fixtures are a way of organizing data that you want to test against; in short,
|
9
|
+
# sample data.
|
8
10
|
#
|
9
11
|
# To learn more about fixtures, read the ActiveRecord::FixtureSet documentation.
|
10
12
|
#
|
11
|
-
#
|
13
|
+
# ### YAML
|
12
14
|
#
|
13
15
|
# Like other Active Record-backed models, ActionText::RichText records inherit
|
14
|
-
# from ActiveRecord::Base instances and can therefore be populated by
|
15
|
-
# fixtures.
|
16
|
+
# from ActiveRecord::Base instances and can therefore be populated by fixtures.
|
16
17
|
#
|
17
|
-
# Consider an
|
18
|
+
# Consider an `Article` class:
|
18
19
|
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
20
|
+
# class Article < ApplicationRecord
|
21
|
+
# has_rich_text :content
|
22
|
+
# end
|
22
23
|
#
|
23
|
-
# To declare fixture data for the related
|
24
|
-
#
|
24
|
+
# To declare fixture data for the related `content`, first declare fixture data
|
25
|
+
# for `Article` instances in `test/fixtures/articles.yml`:
|
25
26
|
#
|
26
|
-
#
|
27
|
-
#
|
27
|
+
# first:
|
28
|
+
# title: An Article
|
28
29
|
#
|
29
30
|
# Then declare the ActionText::RichText fixture data in
|
30
|
-
#
|
31
|
-
#
|
31
|
+
# `test/fixtures/action_text/rich_texts.yml`, making sure to declare each
|
32
|
+
# entry's `record:` key as a polymorphic relationship:
|
32
33
|
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
34
|
+
# first:
|
35
|
+
# record: first (Article)
|
36
|
+
# name: content
|
37
|
+
# body: <div>Hello, world.</div>
|
37
38
|
#
|
38
39
|
# When processed, Active Record will insert database records for each fixture
|
39
40
|
# entry and will ensure the Action Text relationship is intact.
|
40
41
|
class FixtureSet
|
41
|
-
# Fixtures support Action Text attachments as part of their
|
42
|
-
# HTML.
|
42
|
+
# Fixtures support Action Text attachments as part of their `body` HTML.
|
43
43
|
#
|
44
|
-
#
|
44
|
+
# ### Examples
|
45
45
|
#
|
46
|
-
# For example, consider a second
|
47
|
-
#
|
46
|
+
# For example, consider a second `Article` fixture declared in
|
47
|
+
# `test/fixtures/articles.yml`:
|
48
48
|
#
|
49
|
-
#
|
50
|
-
#
|
49
|
+
# second:
|
50
|
+
# title: Another Article
|
51
51
|
#
|
52
|
-
# You can attach a mention of
|
53
|
-
#
|
54
|
-
# in
|
52
|
+
# You can attach a mention of `articles(:first)` to `second`'s `content` by
|
53
|
+
# embedding a call to `ActionText::FixtureSet.attachment` in the `body:` value
|
54
|
+
# in `test/fixtures/action_text/rich_texts.yml`:
|
55
55
|
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
56
|
+
# second:
|
57
|
+
# record: second (Article)
|
58
|
+
# name: content
|
59
|
+
# body: <div>Hello, <%= ActionText::FixtureSet.attachment("articles", :first) %></div>
|
60
60
|
#
|
61
61
|
def self.attachment(fixture_set_name, label, column_type: :integer)
|
62
62
|
signed_global_id = ActiveRecord::FixtureSet.signed_global_id fixture_set_name, label,
|
data/lib/action_text/fragment.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionText
|
4
6
|
class Fragment
|
5
7
|
class << self
|
@@ -21,6 +23,8 @@ module ActionText
|
|
21
23
|
|
22
24
|
attr_reader :source
|
23
25
|
|
26
|
+
delegate :deconstruct, to: "source.elements"
|
27
|
+
|
24
28
|
def initialize(source)
|
25
29
|
@source = source
|
26
30
|
end
|
@@ -1,16 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionText
|
4
|
-
# Returns the currently loaded version of Action Text as a
|
6
|
+
# Returns the currently loaded version of Action Text as a `Gem::Version`.
|
5
7
|
def self.gem_version
|
6
8
|
Gem::Version.new VERSION::STRING
|
7
9
|
end
|
8
10
|
|
9
11
|
module VERSION
|
10
12
|
MAJOR = 7
|
11
|
-
MINOR =
|
12
|
-
TINY =
|
13
|
-
PRE = "
|
13
|
+
MINOR = 2
|
14
|
+
TINY = 0
|
15
|
+
PRE = "beta2"
|
14
16
|
|
15
17
|
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
16
18
|
end
|
@@ -1,34 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionText
|
4
6
|
module SystemTestHelper
|
5
7
|
# Locates a Trix editor and fills it in with the given HTML.
|
6
8
|
#
|
7
9
|
# The editor can be found by:
|
8
|
-
# *
|
9
|
-
# *
|
10
|
-
# *
|
11
|
-
# *
|
12
|
-
# *
|
10
|
+
# * its `id`
|
11
|
+
# * its `placeholder`
|
12
|
+
# * the text from its `label` element
|
13
|
+
# * its `aria-label`
|
14
|
+
# * the `name` of its input
|
15
|
+
#
|
13
16
|
#
|
14
17
|
# Examples:
|
15
18
|
#
|
16
|
-
#
|
17
|
-
#
|
19
|
+
# # <trix-editor id="message_content" ...></trix-editor>
|
20
|
+
# fill_in_rich_text_area "message_content", with: "Hello <em>world!</em>"
|
18
21
|
#
|
19
|
-
#
|
20
|
-
#
|
22
|
+
# # <trix-editor placeholder="Your message here" ...></trix-editor>
|
23
|
+
# fill_in_rich_text_area "Your message here", with: "Hello <em>world!</em>"
|
21
24
|
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
+
# # <label for="message_content">Message content</label>
|
26
|
+
# # <trix-editor id="message_content" ...></trix-editor>
|
27
|
+
# fill_in_rich_text_area "Message content", with: "Hello <em>world!</em>"
|
25
28
|
#
|
26
|
-
#
|
27
|
-
#
|
29
|
+
# # <trix-editor aria-label="Message content" ...></trix-editor>
|
30
|
+
# fill_in_rich_text_area "Message content", with: "Hello <em>world!</em>"
|
28
31
|
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
+
# # <input id="trix_input_1" name="message[content]" type="hidden">
|
33
|
+
# # <trix-editor input="trix_input_1"></trix-editor>
|
34
|
+
# fill_in_rich_text_area "message[content]", with: "Hello <em>world!</em>"
|
32
35
|
def fill_in_rich_text_area(locator = nil, with:)
|
33
36
|
find(:rich_text_area, locator).execute_script("this.editor.loadHTML(arguments[0])", with.to_s)
|
34
37
|
end
|
data/lib/action_text/version.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require_relative "gem_version"
|
4
6
|
|
5
7
|
module ActionText
|
6
|
-
# Returns the currently loaded version of Action Text as a
|
8
|
+
# Returns the currently loaded version of Action Text as a `Gem::Version`.
|
7
9
|
def self.version
|
8
10
|
gem_version
|
9
11
|
end
|
data/lib/action_text.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "pathname"
|
4
6
|
require "json"
|
5
7
|
|
@@ -60,7 +62,12 @@ module ActionText
|
|
60
62
|
def enable_image_processing_gem
|
61
63
|
if (gemfile_path = Pathname(destination_root).join("Gemfile")).exist?
|
62
64
|
say "Ensure image_processing gem has been enabled so image uploads will work (remember to bundle!)"
|
63
|
-
|
65
|
+
image_processing_regex = /gem ["']image_processing["']/
|
66
|
+
if File.readlines(gemfile_path).grep(image_processing_regex).any?
|
67
|
+
uncomment_lines gemfile_path, image_processing_regex
|
68
|
+
else
|
69
|
+
run "bundle add --skip-install image_processing"
|
70
|
+
end
|
64
71
|
end
|
65
72
|
end
|
66
73
|
|
@@ -73,8 +80,8 @@ module ActionText
|
|
73
80
|
end
|
74
81
|
|
75
82
|
def using_bun?
|
76
|
-
# Cannot assume yarn.lock has been generated yet so we look for
|
77
|
-
#
|
83
|
+
# Cannot assume yarn.lock has been generated yet so we look for a file known to
|
84
|
+
# be generated by the jsbundling-rails gem
|
78
85
|
@using_bun ||= using_js_runtime? && Pathname(destination_root).join("bun.config.js").exist?
|
79
86
|
end
|
80
87
|
|
data/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@rails/actiontext",
|
3
|
-
"version": "7.
|
3
|
+
"version": "7.2.0-beta2",
|
4
4
|
"description": "Edit and display rich text in Rails applications",
|
5
5
|
"module": "app/assets/javascripts/actiontext.esm.js",
|
6
6
|
"main": "app/assets/javascripts/actiontext.js",
|
@@ -22,7 +22,7 @@
|
|
22
22
|
],
|
23
23
|
"license": "MIT",
|
24
24
|
"dependencies": {
|
25
|
-
"@rails/activestorage": ">= 7.
|
25
|
+
"@rails/activestorage": ">= 7.2.0-alpha"
|
26
26
|
},
|
27
27
|
"peerDependencies": {
|
28
28
|
"trix": "^2.0.0"
|