actiontext 7.1.3.2 → 7.2.0.beta1

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.

Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -88
  3. data/app/assets/javascripts/actiontext.esm.js +3 -3
  4. data/app/assets/javascripts/actiontext.js +3 -3
  5. data/app/assets/javascripts/trix.js +92 -30
  6. data/app/assets/stylesheets/trix.css +3 -1
  7. data/app/helpers/action_text/content_helper.rb +2 -0
  8. data/app/helpers/action_text/tag_helper.rb +38 -28
  9. data/app/models/action_text/encrypted_rich_text.rb +2 -2
  10. data/app/models/action_text/record.rb +2 -0
  11. data/app/models/action_text/rich_text.rb +58 -26
  12. data/db/migrate/20180528164100_create_action_text_tables.rb +1 -1
  13. data/lib/action_text/attachable.rb +35 -33
  14. data/lib/action_text/attachables/content_attachment.rb +2 -0
  15. data/lib/action_text/attachables/missing_attachable.rb +2 -0
  16. data/lib/action_text/attachables/remote_image.rb +2 -0
  17. data/lib/action_text/attachment.rb +27 -25
  18. data/lib/action_text/attachment_gallery.rb +2 -0
  19. data/lib/action_text/attachments/caching.rb +2 -0
  20. data/lib/action_text/attachments/minification.rb +2 -0
  21. data/lib/action_text/attachments/trix_conversion.rb +2 -0
  22. data/lib/action_text/attribute.rb +36 -22
  23. data/lib/action_text/content.rb +46 -26
  24. data/lib/action_text/deprecator.rb +2 -0
  25. data/lib/action_text/encryption.rb +2 -0
  26. data/lib/action_text/engine.rb +2 -0
  27. data/lib/action_text/fixture_set.rb +34 -34
  28. data/lib/action_text/fragment.rb +4 -0
  29. data/lib/action_text/gem_version.rb +6 -4
  30. data/lib/action_text/html_conversion.rb +2 -0
  31. data/lib/action_text/plain_text_conversion.rb +2 -0
  32. data/lib/action_text/rendering.rb +2 -0
  33. data/lib/action_text/serialization.rb +2 -0
  34. data/lib/action_text/system_test_helper.rb +20 -17
  35. data/lib/action_text/trix_attachment.rb +2 -0
  36. data/lib/action_text/version.rb +3 -1
  37. data/lib/action_text.rb +1 -1
  38. data/lib/generators/action_text/install/install_generator.rb +10 -3
  39. data/lib/rails/generators/test_unit/install_generator.rb +2 -0
  40. data/package.json +1 -1
  41. metadata +15 -15
@@ -1,55 +1,87 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
- # = Action Text \RichText
6
+ # # Action Text RichText
5
7
  #
6
- # The RichText record holds the content produced by the Trix editor in a serialized +body+ attribute.
7
- # It also holds all the references to the embedded files, which are stored using Active Storage.
8
- # This record is then associated with the Active Record model the application desires to have
9
- # rich text content using the +has_rich_text+ class method.
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
- # class Message < ActiveRecord::Base
12
- # has_rich_text :content
13
- # end
14
+ # class Message < ActiveRecord::Base
15
+ # has_rich_text :content
16
+ # end
14
17
  #
15
- # message = Message.create!(content: "<h1>Funny times!</h1>")
16
- # message.content #=> #<ActionText::RichText....
17
- # message.content.to_s # => "<h1>Funny times!</h1>"
18
- # message.content.to_plain_text # => "Funny times!"
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
- self.table_name = "action_text_rich_texts"
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 the +body+ attribute as plain text with all HTML tags removed.
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
- # message = Message.create!(content: "<h1>Funny times!</h1>")
36
- # message.content.to_plain_text # => "Funny times!"
67
+ # message = Message.create!(content: "&lt;script&gt;alert()&lt;/script&gt;")
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 +body+ attribute in a format that makes it editable in the Trix
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
- # content = "<h1>Funny Times!</h1><figure data-trix-attachment='{\"sgid\":\"..."\}'></figure>"
45
- # message = Message.create!(content: content)
46
- # message.content.to_trix_html # =>
47
- # # <div class="trix-content">
48
- # # <h1>Funny times!</h1>
49
- # # <figure data-trix-attachment='{\"sgid\":\"..."\}'>
50
- # # <img src="http://example.org/rails/active_storage/.../funny.jpg">
51
- # # </figure>
52
- # # </div>
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
- # = Action Text \Attachable
6
+ # # Action Text Attachable
5
7
  #
6
8
  # Include this module to make a record attachable to an ActionText::Content.
7
9
  #
8
- # class Person < ApplicationRecord
9
- # include ActionText::Attachable
10
- # end
10
+ # class Person < ApplicationRecord
11
+ # include ActionText::Attachable
12
+ # end
11
13
  #
12
- # person = Person.create! name: "Javan"
13
- # html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
14
- # content = ActionText::Content.new(html)
15
- # content.attachables # => [person]
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 +ActionText::Attachable+ from the attachment HTML node:
24
+ # Extracts the `ActionText::Attachable` from the attachment HTML node:
23
25
  #
24
- # person = Person.create! name: "Javan"
25
- # html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
26
- # fragment = ActionText::Fragment.wrap(html)
27
- # attachment_node = fragment.find_all(ActionText::Attachment.tag_name).first
28
- # ActionText::Attachable.from_node(attachment_node) # => person
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 attachables.
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
- # class User < ApplicationRecord
66
- # def self.to_missing_attachable_partial_path
67
- # "users/missing_attachable"
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
- # set to 'attachable' so it can't be reused for other purposes.
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
- # in Trix. Defaults to +to_partial_path+.
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
- # class User < ApplicationRecord
107
- # def to_trix_content_attachment_partial_path
108
- # "users/trix_content_attachment"
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 +to_partial_path+.
118
+ # Defaults to `to_partial_path`.
117
119
  #
118
120
  # Override to render a different partial:
119
121
  #
120
- # class User < ApplicationRecord
121
- # def to_attachable_partial_path
122
- # "users/attachable"
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,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  module Attachables
5
7
  class ContentAttachment # :nodoc:
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  module Attachables
5
7
  class MissingAttachable
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  module Attachables
5
7
  class RemoteImage
@@ -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
- # = Action Text \Attachment
8
+ # # Action Text Attachment
7
9
  #
8
10
  # Attachments serialize attachables to HTML or plain text.
9
11
  #
10
- # class Person < ApplicationRecord
11
- # include ActionText::Attachable
12
- # end
12
+ # class Person < ApplicationRecord
13
+ # include ActionText::Attachable
14
+ # end
13
15
  #
14
- # attachable = Person.create! name: "Javan"
15
- # attachment = ActionText::Attachment.from_attachable(attachable)
16
- # attachment.to_html # => "<action-text-attachment sgid=\"BAh7CEk..."
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
- # attachable = ActiveStorage::Blob.find_by filename: "racecar.jpg"
86
- # attachment = ActionText::Attachment.from_attachable(attachable)
87
- # attachment.to_plain_text # => "[racecar.jpg]"
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 +caption+ when set:
91
+ # Use the `caption` when set:
90
92
  #
91
- # attachment = ActionText::Attachment.from_attachable(attachable, caption: "Vroom vroom")
92
- # attachment.to_plain_text # => "[Vroom vroom]"
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
- # +attachable_plain_text_representation+ method:
97
+ # `attachable_plain_text_representation` method:
96
98
  #
97
- # class Person < ApplicationRecord
98
- # include ActionText::Attachable
99
+ # class Person < ApplicationRecord
100
+ # include ActionText::Attachable
99
101
  #
100
- # def attachable_plain_text_representation
101
- # "[#{name}]"
102
+ # def attachable_plain_text_representation
103
+ # "[#{name}]"
104
+ # end
102
105
  # end
103
- # end
104
106
  #
105
- # attachable = Person.create! name: "Javan"
106
- # attachment = ActionText::Attachment.from_attachable(attachable)
107
- # attachment.to_plain_text # => "[Javan]"
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
- # attachable = Person.create! name: "Javan"
119
- # attachment = ActionText::Attachment.from_attachable(attachable)
120
- # attachment.to_html # => "<action-text-attachment sgid=\"BAh7CEk...
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,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  class AttachmentGallery
5
7
  include ActiveModel::Model
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  module Attachments
5
7
  module Caching
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  module Attachments
5
7
  module Minification
@@ -1,5 +1,7 @@
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
@@ -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 attachments for a single named rich text attribute.
9
- # This dependent attribute is lazily instantiated and will be auto-saved when it's been changed. Example:
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
- # class Message < ActiveRecord::Base
12
- # has_rich_text :content
13
- # end
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
- # message = Message.create!(content: "<h1>Funny times!</h1>")
16
- # message.content? #=> true
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
- # The dependent RichText model will also automatically process attachments links as sent via the Trix-powered editor.
21
- # These attachments are associated with the RichText model using Active Storage.
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
- # If you wish to preload the dependent RichText model, you can use the named scope:
34
+ # #### Options
24
35
  #
25
- # Message.all.with_rich_text_content # Avoids N+1 queries when you just want the body, not the attachments.
26
- # Message.all.with_rich_text_content_and_embeds # Avoids N+1 queries when you just want the body and attachments.
27
- # Message.all.with_all_rich_text # Loads all rich text associations.
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
- # ==== Options
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
- # * <tt>:strict_loading</tt> - Pass true to force strict loading. When
35
- # omitted, <tt>strict_loading:</tt> will be set to the value of the
36
- # <tt>strict_loading_by_default</tt> class attribute (false by default).
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
- eager_load(rich_text_association_names)
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
@@ -1,29 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
- # = Action Text \Content
6
+ # # Action Text Content
5
7
  #
6
- # The +ActionText::Content+ class wraps an HTML fragment to add support for
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
- # to the database.
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
- # +ActionText::Content+.
14
+ # `ActionText::Content`.
13
15
  #
14
- # class Message < ActiveRecord::Base
15
- # has_rich_text :content
16
- # end
16
+ # class Message < ActiveRecord::Base
17
+ # has_rich_text :content
18
+ # end
17
19
  #
18
- # message = Message.create!(content: "<h1>Funny times!</h1>")
19
- # body = message.content.body # => #<ActionText::Content "<div class=\"trix-conte...">
20
- # body.to_s # => "<h1>Funny times!</h1>"
21
- # body.to_plain_text # => "Funny times!"
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
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
- # html = '<a href="http://example.com/">Example</a>'
50
- # content = ActionText::Content.new(html)
51
- # content.links # => ["http://example.com/"]
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
- # attachable = ActiveStorage::Blob.first
59
- # html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}" caption="Captioned"></action-text-attachment>)
60
- # content = ActionText::Content.new(html)
61
- # content.attachments # => [#<ActionText::Attachment attachable=#<ActiveStorage::Blob...
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
- # attachable = ActiveStorage::Blob.first
81
- # html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}" caption="Captioned"></action-text-attachment>)
82
- # content = ActionText::Content.new(html)
83
- # content.attachables # => [attachable]
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)
@@ -106,10 +109,20 @@ module ActionText
106
109
  self.class.new(content, canonicalize: false)
107
110
  end
108
111
 
109
- # Returns the content as plain text with all HTML tags removed.
112
+ # Returns a plain-text version of the markup contained by the content, with tags
113
+ # removed but HTML entities encoded.
114
+ #
115
+ # content = ActionText::Content.new("<h1>Funny times!</h1>")
116
+ # content.to_plain_text # => "Funny times!"
117
+ #
118
+ # content = ActionText::Content.new("<div onclick='action()'>safe<script>unsafe</script></div>")
119
+ # content.to_plain_text # => "safeunsafe"
110
120
  #
111
- # content = ActionText::Content.new("<h1>Funny times!</h1>")
112
- # content.to_plain_text # => "Funny times!"
121
+ # NOTE: that the returned string is not HTML safe and should not be rendered in
122
+ # browsers.
123
+ #
124
+ # content = ActionText::Content.new("&lt;script&gt;alert()&lt;/script&gt;")
125
+ # content.to_plain_text # => "<script>alert()</script>"
113
126
  def to_plain_text
114
127
  render_attachments(with_full_attributes: false, &:to_plain_text).fragment.to_plain_text
115
128
  end
@@ -130,6 +143,13 @@ module ActionText
130
143
  "action_text/contents/content"
131
144
  end
132
145
 
146
+ # Safely transforms Content into an HTML String.
147
+ #
148
+ # content = ActionText::Content.new(content: "<h1>Funny times!</h1>")
149
+ # content.to_s # => "<h1>Funny times!</h1>"
150
+ #
151
+ # content = ActionText::Content.new("<div onclick='action()'>safe<script>unsafe</script></div>")
152
+ # content.to_s # => "<div>safeunsafe</div>"
133
153
  def to_s
134
154
  to_rendered_html_with_layout
135
155
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  def self.deprecator # :nodoc:
5
7
  @deprecator ||= ActiveSupport::Deprecation.new
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  module Encryption
5
7
  def encrypt
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "rails"
4
6
  require "action_controller/railtie"
5
7
  require "active_record/railtie"