actiontext 8.0.3 → 8.1.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,10 +48,13 @@ module ActionText
48
48
  ##
49
49
  # :method: embeds
50
50
  #
51
- # Returns the ActiveStorage::Blob records of the embedded files.
51
+ # Returns the ActiveStorage::Attachment records from the embedded files.
52
+ #
53
+ # Attached ActiveStorage::Blob records are extracted from the `body`
54
+ # in a {before_validation}[rdoc-ref:ActiveModel::Validations::Callbacks::ClassMethods#before_validation] callback.
52
55
  has_many_attached :embeds
53
56
 
54
- before_save do
57
+ before_validation do
55
58
  self.embeds = body.attachables.grep(ActiveStorage::Blob).uniq if body.present?
56
59
  end
57
60
 
@@ -123,10 +123,11 @@ module ActionText
123
123
  # content.to_plain_text # => "safeunsafe"
124
124
  #
125
125
  # NOTE: that the returned string is not HTML safe and should not be rendered in
126
- # browsers.
126
+ # browsers without additional sanitization.
127
127
  #
128
128
  # content = ActionText::Content.new("<script>alert()</script>")
129
129
  # content.to_plain_text # => "<script>alert()</script>"
130
+ # ActionText::ContentHelper.sanitizer.sanitize(content.to_plain_text) # => ""
130
131
  def to_plain_text
131
132
  render_attachments(with_full_attributes: false, &:to_plain_text).fragment.to_plain_text
132
133
  end
@@ -8,6 +8,7 @@ require "active_record/railtie"
8
8
  require "active_storage/engine"
9
9
 
10
10
  require "action_text"
11
+ require "action_text/trix"
11
12
 
12
13
  module ActionText
13
14
  class Engine < Rails::Engine
@@ -34,7 +35,7 @@ module ActionText
34
35
 
35
36
  initializer "action_text.asset" do
36
37
  if Rails.application.config.respond_to?(:assets)
37
- Rails.application.config.assets.precompile += %w( actiontext.js actiontext.esm.js trix.js trix.css )
38
+ Rails.application.config.assets.precompile += %w( actiontext.js actiontext.esm.js )
38
39
  end
39
40
  end
40
41
 
@@ -62,7 +62,7 @@ module ActionText
62
62
  signed_global_id = ActiveRecord::FixtureSet.signed_global_id fixture_set_name, label,
63
63
  column_type: column_type, for: ActionText::Attachable::LOCATOR_NAME
64
64
 
65
- %(<action-text-attachment sgid="#{signed_global_id}"></action-text-attachment>)
65
+ %(<#{Attachment.tag_name} sgid="#{signed_global_id}"></#{Attachment.tag_name}>)
66
66
  end
67
67
  end
68
68
  end
@@ -10,9 +10,9 @@ module ActionText
10
10
 
11
11
  module VERSION
12
12
  MAJOR = 8
13
- MINOR = 0
14
- TINY = 3
15
- PRE = nil
13
+ MINOR = 1
14
+ TINY = 0
15
+ PRE = "rc1"
16
16
 
17
17
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
18
18
  end
@@ -7,64 +7,70 @@ module ActionText
7
7
  extend self
8
8
 
9
9
  def node_to_plain_text(node)
10
- remove_trailing_newlines(plain_text_for_node(node))
10
+ BottomUpReducer.new(node).reduce do |n, child_values|
11
+ plain_text_for_node(n, child_values)
12
+ end.then(&method(:remove_trailing_newlines))
11
13
  end
12
14
 
13
15
  private
14
- def plain_text_for_node(node, index = 0)
16
+ def plain_text_for_node(node, child_values)
15
17
  if respond_to?(plain_text_method_for_node(node), true)
16
- send(plain_text_method_for_node(node), node, index)
18
+ send(plain_text_method_for_node(node), node, child_values)
17
19
  else
18
- plain_text_for_node_children(node)
20
+ plain_text_for_child_values(child_values)
19
21
  end
20
22
  end
21
23
 
22
- def plain_text_for_node_children(node)
23
- texts = []
24
- node.children.each_with_index do |child, index|
25
- texts << plain_text_for_node(child, index)
26
- end
27
- texts.join
28
- end
29
-
30
24
  def plain_text_method_for_node(node)
31
25
  :"plain_text_for_#{node.name}_node"
32
26
  end
33
27
 
34
- def plain_text_for_block(node, index = 0)
35
- "#{remove_trailing_newlines(plain_text_for_node_children(node))}\n\n"
28
+ def plain_text_for_child_values(child_values)
29
+ child_values.join
30
+ end
31
+
32
+ def plain_text_for_unsupported_node(node, _child_values)
33
+ ""
34
+ end
35
+
36
+ %i[ script style].each do |element|
37
+ alias_method :"plain_text_for_#{element}_node", :plain_text_for_unsupported_node
38
+ end
39
+
40
+ def plain_text_for_block(node, child_values)
41
+ "#{remove_trailing_newlines(plain_text_for_child_values(child_values))}\n\n"
36
42
  end
37
43
 
38
44
  %i[ h1 p ].each do |element|
39
45
  alias_method :"plain_text_for_#{element}_node", :plain_text_for_block
40
46
  end
41
47
 
42
- def plain_text_for_list(node, index)
43
- "#{break_if_nested_list(node, plain_text_for_block(node))}"
48
+ def plain_text_for_list(node, child_values)
49
+ "#{break_if_nested_list(node, plain_text_for_block(node, child_values))}"
44
50
  end
45
51
 
46
52
  %i[ ul ol ].each do |element|
47
53
  alias_method :"plain_text_for_#{element}_node", :plain_text_for_list
48
54
  end
49
55
 
50
- def plain_text_for_br_node(node, index)
56
+ def plain_text_for_br_node(node, _child_values)
51
57
  "\n"
52
58
  end
53
59
 
54
- def plain_text_for_text_node(node, index)
60
+ def plain_text_for_text_node(node, _child_values)
55
61
  remove_trailing_newlines(node.text)
56
62
  end
57
63
 
58
- def plain_text_for_div_node(node, index)
59
- "#{remove_trailing_newlines(plain_text_for_node_children(node))}\n"
64
+ def plain_text_for_div_node(node, child_values)
65
+ "#{remove_trailing_newlines(plain_text_for_child_values(child_values))}\n"
60
66
  end
61
67
 
62
- def plain_text_for_figcaption_node(node, index)
63
- "[#{remove_trailing_newlines(plain_text_for_node_children(node))}]"
68
+ def plain_text_for_figcaption_node(node, child_values)
69
+ "[#{remove_trailing_newlines(plain_text_for_child_values(child_values))}]"
64
70
  end
65
71
 
66
- def plain_text_for_blockquote_node(node, index)
67
- text = plain_text_for_block(node)
72
+ def plain_text_for_blockquote_node(node, child_values)
73
+ text = plain_text_for_block(node, child_values)
68
74
  return "“”" if text.blank?
69
75
 
70
76
  text = text.dup
@@ -73,9 +79,9 @@ module ActionText
73
79
  text
74
80
  end
75
81
 
76
- def plain_text_for_li_node(node, index)
77
- bullet = bullet_for_li_node(node, index)
78
- text = remove_trailing_newlines(plain_text_for_node_children(node))
82
+ def plain_text_for_li_node(node, child_values)
83
+ bullet = bullet_for_li_node(node)
84
+ text = remove_trailing_newlines(plain_text_for_child_values(child_values))
79
85
  indentation = indentation_for_li_node(node)
80
86
 
81
87
  "#{indentation}#{bullet} #{text}\n"
@@ -85,8 +91,9 @@ module ActionText
85
91
  text.chomp("")
86
92
  end
87
93
 
88
- def bullet_for_li_node(node, index)
94
+ def bullet_for_li_node(node)
89
95
  if list_node_name_for_li_node(node) == "ol"
96
+ index = node.parent.elements.index(node)
90
97
  "#{index + 1}."
91
98
  else
92
99
  "•"
@@ -115,5 +122,33 @@ module ActionText
115
122
  text
116
123
  end
117
124
  end
125
+
126
+ class BottomUpReducer # :nodoc:
127
+ def initialize(node)
128
+ @node = node
129
+ @values = {}
130
+ end
131
+
132
+ def reduce(&block)
133
+ traverse_bottom_up(@node) do |n|
134
+ child_values = @values.values_at(*n.children)
135
+ @values[n] = block.call(n, child_values)
136
+ end
137
+ @values[@node]
138
+ end
139
+
140
+ private
141
+ def traverse_bottom_up(node, &block)
142
+ call_stack, processing_stack = [ node ], []
143
+
144
+ until call_stack.empty?
145
+ node = call_stack.pop
146
+ processing_stack.push(node)
147
+ call_stack.concat node.children
148
+ end
149
+
150
+ processing_stack.reverse_each(&block)
151
+ end
152
+ end
118
153
  end
119
154
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  # :markup: markdown
4
4
 
5
- require "active_support/concern"
6
5
  require "active_support/core_ext/module/attribute_accessors_per_thread"
7
6
 
8
7
  module ActionText
@@ -7,12 +7,14 @@ module ActionText
7
7
  # Locates a Trix editor and fills it in with the given HTML.
8
8
  #
9
9
  # The editor can be found by:
10
+ #
10
11
  # * its `id`
11
12
  # * its `placeholder`
12
13
  # * the text from its `label` element
13
14
  # * its `aria-label`
14
15
  # * the `name` of its input
15
16
  #
17
+ # Additional options are forwarded to Capybara as filters
16
18
  #
17
19
  # Examples:
18
20
  #
@@ -32,8 +34,14 @@ module ActionText
32
34
  # # <input id="trix_input_1" name="message[content]" type="hidden">
33
35
  # # <trix-editor input="trix_input_1"></trix-editor>
34
36
  # fill_in_rich_textarea "message[content]", with: "Hello <em>world!</em>"
35
- def fill_in_rich_textarea(locator = nil, with:)
36
- find(:rich_textarea, locator).execute_script("this.editor.loadHTML(arguments[0])", with.to_s)
37
+ def fill_in_rich_textarea(locator = nil, with:, **)
38
+ find(:rich_textarea, locator, **).execute_script(<<~JS, with.to_s)
39
+ if ("value" in this) {
40
+ this.value = arguments[0]
41
+ } else {
42
+ this.editor.loadHTML(arguments[0])
43
+ }
44
+ JS
37
45
  end
38
46
  alias_method :fill_in_rich_text_area, :fill_in_rich_textarea
39
47
  end
@@ -43,13 +51,18 @@ end
43
51
  Capybara.add_selector rich_textarea do
44
52
  label "rich-text area"
45
53
  xpath do |locator|
54
+ xpath = XPath.descendant[[
55
+ XPath.attribute(:role) == "textbox",
56
+ (XPath.attribute(:contenteditable) == "") | (XPath.attribute(:contenteditable) == "true")
57
+ ].reduce(:&)]
58
+
46
59
  if locator.nil?
47
- XPath.descendant(:"trix-editor")
60
+ xpath
48
61
  else
49
62
  input_located_by_name = XPath.anywhere(:input).where(XPath.attr(:name) == locator).attr(:id)
50
63
  input_located_by_label = XPath.anywhere(:label).where(XPath.string.n.is(locator)).attr(:for)
51
64
 
52
- XPath.descendant(:"trix-editor").where \
65
+ xpath.where \
53
66
  XPath.attr(:id).equals(locator) |
54
67
  XPath.attr(:placeholder).equals(locator) |
55
68
  XPath.attr(:"aria-label").equals(locator) |
@@ -47,18 +47,6 @@ module ActionText
47
47
  "app/views/layouts/action_text/contents/_content.html.erb"
48
48
  end
49
49
 
50
- def enable_image_processing_gem
51
- if (gemfile_path = Pathname(destination_root).join("Gemfile")).exist?
52
- say "Ensure image_processing gem has been enabled so image uploads will work (remember to bundle!)"
53
- image_processing_regex = /gem ["']image_processing["']/
54
- if File.readlines(gemfile_path).grep(image_processing_regex).any?
55
- uncomment_lines gemfile_path, image_processing_regex
56
- else
57
- run "bundle add --skip-install image_processing"
58
- end
59
- end
60
- end
61
-
62
50
  def create_migrations
63
51
  rails_command "railties:install:migrations FROM=active_storage,action_text", inline: true
64
52
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rails/actiontext",
3
- "version": "8.0.300",
3
+ "version": "8.1.0-rc1",
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": ">= 8.0.0-alpha"
25
+ "@rails/activestorage": ">= 8.1.0-alpha"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "trix": "^2.0.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actiontext
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.3
4
+ version: 8.1.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Javan Makhmali
@@ -17,56 +17,56 @@ dependencies:
17
17
  requirements:
18
18
  - - '='
19
19
  - !ruby/object:Gem::Version
20
- version: 8.0.3
20
+ version: 8.1.0.rc1
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - '='
26
26
  - !ruby/object:Gem::Version
27
- version: 8.0.3
27
+ version: 8.1.0.rc1
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: activerecord
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - '='
33
33
  - !ruby/object:Gem::Version
34
- version: 8.0.3
34
+ version: 8.1.0.rc1
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - '='
40
40
  - !ruby/object:Gem::Version
41
- version: 8.0.3
41
+ version: 8.1.0.rc1
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: activestorage
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - '='
47
47
  - !ruby/object:Gem::Version
48
- version: 8.0.3
48
+ version: 8.1.0.rc1
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - '='
54
54
  - !ruby/object:Gem::Version
55
- version: 8.0.3
55
+ version: 8.1.0.rc1
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: actionpack
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - '='
61
61
  - !ruby/object:Gem::Version
62
- version: 8.0.3
62
+ version: 8.1.0.rc1
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - '='
68
68
  - !ruby/object:Gem::Version
69
- version: 8.0.3
69
+ version: 8.1.0.rc1
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: nokogiri
72
72
  requirement: !ruby/object:Gem::Requirement
@@ -95,6 +95,20 @@ dependencies:
95
95
  - - ">="
96
96
  - !ruby/object:Gem::Version
97
97
  version: 0.6.0
98
+ - !ruby/object:Gem::Dependency
99
+ name: action_text-trix
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: 2.1.15
105
+ type: :runtime
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: 2.1.15
98
112
  description: Edit and display rich text in Rails applications.
99
113
  email:
100
114
  - javan@javan.us
@@ -109,8 +123,6 @@ files:
109
123
  - README.md
110
124
  - app/assets/javascripts/actiontext.esm.js
111
125
  - app/assets/javascripts/actiontext.js
112
- - app/assets/javascripts/trix.js
113
- - app/assets/stylesheets/trix.css
114
126
  - app/helpers/action_text/content_helper.rb
115
127
  - app/helpers/action_text/tag_helper.rb
116
128
  - app/javascript/actiontext/attachment_upload.js
@@ -162,10 +174,10 @@ licenses:
162
174
  - MIT
163
175
  metadata:
164
176
  bug_tracker_uri: https://github.com/rails/rails/issues
165
- changelog_uri: https://github.com/rails/rails/blob/v8.0.3/actiontext/CHANGELOG.md
166
- documentation_uri: https://api.rubyonrails.org/v8.0.3/
177
+ changelog_uri: https://github.com/rails/rails/blob/v8.1.0.rc1/actiontext/CHANGELOG.md
178
+ documentation_uri: https://api.rubyonrails.org/v8.1.0.rc1/
167
179
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
168
- source_code_uri: https://github.com/rails/rails/tree/v8.0.3/actiontext
180
+ source_code_uri: https://github.com/rails/rails/tree/v8.1.0.rc1/actiontext
169
181
  rubygems_mfa_required: 'true'
170
182
  rdoc_options: []
171
183
  require_paths: