actiontext 6.1.4 → 7.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -77
- data/MIT-LICENSE +1 -1
- data/README.md +4 -0
- data/app/assets/javascripts/actiontext.js +900 -0
- data/app/assets/javascripts/trix.js +5278 -0
- data/app/assets/stylesheets/trix.css +375 -0
- data/app/helpers/action_text/content_helper.rb +17 -3
- data/app/helpers/action_text/tag_helper.rb +19 -9
- data/app/javascript/actiontext/attachment_upload.js +8 -1
- data/app/models/action_text/encrypted_rich_text.rb +9 -0
- data/app/models/action_text/record.rb +1 -1
- data/app/models/action_text/rich_text.rb +4 -0
- data/app/views/action_text/contents/_content.html.erb +1 -0
- data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
- data/db/migrate/20180528164100_create_action_text_tables.rb +14 -2
- data/lib/action_text/attachable.rb +4 -0
- data/lib/action_text/attachment.rb +4 -4
- data/lib/action_text/attachment_gallery.rb +14 -9
- data/lib/action_text/attachments/caching.rb +1 -1
- data/lib/action_text/attachments/minification.rb +1 -1
- data/lib/action_text/attribute.rb +18 -2
- data/lib/action_text/content.rb +7 -3
- data/lib/action_text/encryption.rb +38 -0
- data/lib/action_text/engine.rb +14 -0
- data/lib/action_text/fixture_set.rb +55 -0
- data/lib/action_text/gem_version.rb +4 -4
- data/lib/action_text/plain_text_conversion.rb +31 -2
- data/lib/action_text/rendering.rb +1 -1
- data/lib/action_text/serialization.rb +2 -0
- data/lib/action_text/trix_attachment.rb +3 -3
- data/lib/action_text.rb +1 -0
- data/lib/generators/action_text/install/install_generator.rb +40 -35
- data/lib/generators/action_text/install/templates/actiontext.css +35 -0
- data/package.json +13 -3
- metadata +40 -19
- data/app/views/action_text/content/_layout.html.erb +0 -3
- data/lib/generators/action_text/install/templates/actiontext.scss +0 -36
@@ -7,7 +7,7 @@ module ActionText
|
|
7
7
|
|
8
8
|
class_methods do
|
9
9
|
def fragment_by_minifying_attachments(content)
|
10
|
-
Fragment.wrap(content).replace(ActionText::Attachment
|
10
|
+
Fragment.wrap(content).replace(ActionText::Attachment.tag_name) do |node|
|
11
11
|
node.tap { |n| n.inner_html = "" }
|
12
12
|
end
|
13
13
|
end
|
@@ -24,7 +24,13 @@ module ActionText
|
|
24
24
|
#
|
25
25
|
# Message.all.with_rich_text_content # Avoids N+1 queries when you just want the body, not the attachments.
|
26
26
|
# Message.all.with_rich_text_content_and_embeds # Avoids N+1 queries when you just want the body and attachments.
|
27
|
-
|
27
|
+
# Message.all.with_all_rich_text # Loads all rich text associations.
|
28
|
+
#
|
29
|
+
# === Options
|
30
|
+
#
|
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
|
+
def has_rich_text(name, encrypted: false)
|
28
34
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
29
35
|
def #{name}
|
30
36
|
rich_text_#{name} || build_rich_text_#{name}
|
@@ -39,12 +45,22 @@ module ActionText
|
|
39
45
|
end
|
40
46
|
CODE
|
41
47
|
|
48
|
+
rich_text_class_name = encrypted ? "ActionText::EncryptedRichText" : "ActionText::RichText"
|
42
49
|
has_one :"rich_text_#{name}", -> { where(name: name) },
|
43
|
-
class_name:
|
50
|
+
class_name: rich_text_class_name, as: :record, inverse_of: :record, autosave: true, dependent: :destroy
|
44
51
|
|
45
52
|
scope :"with_rich_text_#{name}", -> { includes("rich_text_#{name}") }
|
46
53
|
scope :"with_rich_text_#{name}_and_embeds", -> { includes("rich_text_#{name}": { embeds_attachments: :blob }) }
|
47
54
|
end
|
55
|
+
|
56
|
+
# Eager load all dependent RichText models in bulk.
|
57
|
+
def with_all_rich_text
|
58
|
+
eager_load(rich_text_association_names)
|
59
|
+
end
|
60
|
+
|
61
|
+
def rich_text_association_names
|
62
|
+
reflect_on_all_associations(:has_one).collect(&:name).select { |n| n.start_with?("rich_text_") }
|
63
|
+
end
|
48
64
|
end
|
49
65
|
end
|
50
66
|
end
|
data/lib/action_text/content.rb
CHANGED
@@ -58,7 +58,7 @@ module ActionText
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def render_attachments(**options, &block)
|
61
|
-
content = fragment.replace(ActionText::Attachment
|
61
|
+
content = fragment.replace(ActionText::Attachment.tag_name) do |node|
|
62
62
|
block.call(attachment_for_node(node, **options))
|
63
63
|
end
|
64
64
|
self.class.new(content, canonicalize: false)
|
@@ -84,7 +84,11 @@ module ActionText
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def to_rendered_html_with_layout
|
87
|
-
render
|
87
|
+
render layout: "action_text/contents/content", partial: to_partial_path, formats: :html, locals: { content: self }
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_partial_path
|
91
|
+
"action_text/contents/content"
|
88
92
|
end
|
89
93
|
|
90
94
|
def to_s
|
@@ -107,7 +111,7 @@ module ActionText
|
|
107
111
|
|
108
112
|
private
|
109
113
|
def attachment_nodes
|
110
|
-
@attachment_nodes ||= fragment.find_all(ActionText::Attachment
|
114
|
+
@attachment_nodes ||= fragment.find_all(ActionText::Attachment.tag_name)
|
111
115
|
end
|
112
116
|
|
113
117
|
def attachment_gallery_nodes
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionText
|
4
|
+
module Encryption
|
5
|
+
def encrypt
|
6
|
+
transaction do
|
7
|
+
super
|
8
|
+
encrypt_rich_texts if has_encrypted_rich_texts?
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def decrypt
|
13
|
+
transaction do
|
14
|
+
super
|
15
|
+
decrypt_rich_texts if has_encrypted_rich_texts?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def encrypt_rich_texts
|
21
|
+
encryptable_rich_texts.each(&:encrypt)
|
22
|
+
end
|
23
|
+
|
24
|
+
def decrypt_rich_texts
|
25
|
+
encryptable_rich_texts.each(&:decrypt)
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_encrypted_rich_texts?
|
29
|
+
encryptable_rich_texts.present?
|
30
|
+
end
|
31
|
+
|
32
|
+
def encryptable_rich_texts
|
33
|
+
@encryptable_rich_texts ||= self.class.rich_text_association_names
|
34
|
+
.filter_map { |attribute_name| send(attribute_name) }
|
35
|
+
.find_all { |record| record.is_a?(ActionText::EncryptedRichText) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/action_text/engine.rb
CHANGED
@@ -11,6 +11,9 @@ module ActionText
|
|
11
11
|
class Engine < Rails::Engine
|
12
12
|
isolate_namespace ActionText
|
13
13
|
config.eager_load_namespaces << ActionText
|
14
|
+
|
15
|
+
config.action_text = ActiveSupport::OrderedOptions.new
|
16
|
+
config.action_text.attachment_tag_name = "action-text-attachment"
|
14
17
|
config.autoload_once_paths = %W(
|
15
18
|
#{root}/app/helpers
|
16
19
|
#{root}/app/models
|
@@ -19,6 +22,13 @@ module ActionText
|
|
19
22
|
initializer "action_text.attribute" do
|
20
23
|
ActiveSupport.on_load(:active_record) do
|
21
24
|
include ActionText::Attribute
|
25
|
+
prepend ActionText::Encryption
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
initializer "action_text.asset" do
|
30
|
+
if Rails.application.config.respond_to?(:assets)
|
31
|
+
Rails.application.config.assets.precompile += %w( actiontext.js trix.js trix.css )
|
22
32
|
end
|
23
33
|
end
|
24
34
|
|
@@ -68,5 +78,9 @@ module ActionText
|
|
68
78
|
include ActionText::SystemTestHelper
|
69
79
|
end
|
70
80
|
end
|
81
|
+
|
82
|
+
initializer "action_text.configure" do |app|
|
83
|
+
ActionText::Attachment.tag_name = app.config.action_text.attachment_tag_name
|
84
|
+
end
|
71
85
|
end
|
72
86
|
end
|
@@ -1,7 +1,62 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionText
|
4
|
+
# Fixtures are a way of organizing data that you want to test against; in
|
5
|
+
# short, sample data.
|
6
|
+
#
|
7
|
+
# To learn more about fixtures, read the
|
8
|
+
# {ActiveRecord::FixtureSet}[rdoc-ref:ActiveRecord::FixtureSet] documentation.
|
9
|
+
#
|
10
|
+
# === YAML
|
11
|
+
#
|
12
|
+
# Like other Active Record-backed models, ActionText::RichText records inherit
|
13
|
+
# from ActiveRecord::Base instances and can therefore be populated by
|
14
|
+
# fixtures.
|
15
|
+
#
|
16
|
+
# Consider an <tt>Article</tt> class:
|
17
|
+
#
|
18
|
+
# class Article < ApplicationRecord
|
19
|
+
# has_rich_text :content
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# To declare fixture data for the related <tt>content</tt>, first declare fixture
|
23
|
+
# data for <tt>Article</tt> instances in <tt>test/fixtures/articles.yml</tt>:
|
24
|
+
#
|
25
|
+
# first:
|
26
|
+
# title: An Article
|
27
|
+
#
|
28
|
+
# Then declare the <tt>ActionText::RichText</tt> fixture data in
|
29
|
+
# <tt>test/fixtures/action_text/rich_texts.yml</tt>, making sure to declare
|
30
|
+
# each entry's <tt>record:</tt> key as a polymorphic relationship:
|
31
|
+
#
|
32
|
+
# first:
|
33
|
+
# record: first (Article)
|
34
|
+
# name: content
|
35
|
+
# body: <div>Hello, world.</div>
|
36
|
+
#
|
37
|
+
# When processed, Active Record will insert database records for each fixture
|
38
|
+
# entry and will ensure the Action Text relationship is intact.
|
4
39
|
class FixtureSet
|
40
|
+
# Fixtures support Action Text attachments as part of their <tt>body</tt>
|
41
|
+
# HTML.
|
42
|
+
#
|
43
|
+
# === Examples
|
44
|
+
#
|
45
|
+
# For example, consider a second <tt>Article</tt> fixture declared in
|
46
|
+
# <tt>test/fixtures/articles.yml</tt>:
|
47
|
+
#
|
48
|
+
# second:
|
49
|
+
# title: Another Article
|
50
|
+
#
|
51
|
+
# You can attach a mention of <tt>articles(:first)</tt> to <tt>second</tt>'s
|
52
|
+
# <tt>content</tt> by embedding a call to <tt>ActionText::FixtureSet.attachment</tt>
|
53
|
+
# in the <tt>body:</tt> value in <tt>test/fixtures/action_text/rich_texts.yml</tt>:
|
54
|
+
#
|
55
|
+
# second:
|
56
|
+
# record: second (Article)
|
57
|
+
# name: content
|
58
|
+
# body: <div>Hello, <%= ActionText::FixtureSet.attachment("articles", :first) %></div>
|
59
|
+
#
|
5
60
|
def self.attachment(fixture_set_name, label, column_type: :integer)
|
6
61
|
signed_global_id = ActiveRecord::FixtureSet.signed_global_id fixture_set_name, label,
|
7
62
|
column_type: column_type, for: ActionText::Attachable::LOCATOR_NAME
|
@@ -33,10 +33,18 @@ module ActionText
|
|
33
33
|
"#{remove_trailing_newlines(plain_text_for_node_children(node))}\n\n"
|
34
34
|
end
|
35
35
|
|
36
|
-
%i[ h1 p
|
36
|
+
%i[ h1 p ].each do |element|
|
37
37
|
alias_method :"plain_text_for_#{element}_node", :plain_text_for_block
|
38
38
|
end
|
39
39
|
|
40
|
+
def plain_text_for_list(node, index)
|
41
|
+
"#{break_if_nested_list(node, plain_text_for_block(node))}"
|
42
|
+
end
|
43
|
+
|
44
|
+
%i[ ul ol ].each do |element|
|
45
|
+
alias_method :"plain_text_for_#{element}_node", :plain_text_for_list
|
46
|
+
end
|
47
|
+
|
40
48
|
def plain_text_for_br_node(node, index)
|
41
49
|
"\n"
|
42
50
|
end
|
@@ -61,7 +69,9 @@ module ActionText
|
|
61
69
|
def plain_text_for_li_node(node, index)
|
62
70
|
bullet = bullet_for_li_node(node, index)
|
63
71
|
text = remove_trailing_newlines(plain_text_for_node_children(node))
|
64
|
-
|
72
|
+
indentation = indentation_for_li_node(node)
|
73
|
+
|
74
|
+
"#{indentation}#{bullet} #{text}\n"
|
65
75
|
end
|
66
76
|
|
67
77
|
def remove_trailing_newlines(text)
|
@@ -79,5 +89,24 @@ module ActionText
|
|
79
89
|
def list_node_name_for_li_node(node)
|
80
90
|
node.ancestors.lazy.map(&:name).grep(/^[uo]l$/).first
|
81
91
|
end
|
92
|
+
|
93
|
+
def indentation_for_li_node(node)
|
94
|
+
depth = list_node_depth_for_node(node)
|
95
|
+
if depth > 1
|
96
|
+
" " * (depth - 1)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def list_node_depth_for_node(node)
|
101
|
+
node.ancestors.map(&:name).grep(/^[uo]l$/).count
|
102
|
+
end
|
103
|
+
|
104
|
+
def break_if_nested_list(node, text)
|
105
|
+
if list_node_depth_for_node(node) > 0
|
106
|
+
"\n#{text}"
|
107
|
+
else
|
108
|
+
text
|
109
|
+
end
|
110
|
+
end
|
82
111
|
end
|
83
112
|
end
|
@@ -9,9 +9,9 @@ module ActionText
|
|
9
9
|
ATTRIBUTES = %w( sgid contentType url href filename filesize width height previewable content ) + COMPOSED_ATTRIBUTES
|
10
10
|
ATTRIBUTE_TYPES = {
|
11
11
|
"previewable" => ->(value) { value.to_s == "true" },
|
12
|
-
"filesize" => ->(value) { Integer(value.to_s)
|
13
|
-
"width" => ->(value) { Integer(value.to_s
|
14
|
-
"height" => ->(value) { Integer(value.to_s
|
12
|
+
"filesize" => ->(value) { Integer(value.to_s, exception: false) || value },
|
13
|
+
"width" => ->(value) { Integer(value.to_s, exception: false) },
|
14
|
+
"height" => ->(value) { Integer(value.to_s, exception: false) },
|
15
15
|
:default => ->(value) { value.to_s }
|
16
16
|
}
|
17
17
|
|
data/lib/action_text.rb
CHANGED
@@ -9,43 +9,57 @@ module ActionText
|
|
9
9
|
source_root File.expand_path("templates", __dir__)
|
10
10
|
|
11
11
|
def install_javascript_dependencies
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
abort_on_failure: true, capture: true
|
12
|
+
if Pathname(destination_root).join("package.json").exist?
|
13
|
+
say "Installing JavaScript dependencies", :green
|
14
|
+
run "yarn add @rails/actiontext trix"
|
15
|
+
end
|
17
16
|
end
|
18
17
|
|
19
|
-
def
|
20
|
-
|
21
|
-
js_dependencies.each_key do |dependency|
|
22
|
-
line = %[require("#{dependency}")]
|
18
|
+
def append_javascript_dependencies
|
19
|
+
destination = Pathname(destination_root)
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
append_to_file app_javascript_pack_path, "\n#{line}"
|
27
|
-
end
|
28
|
-
end
|
21
|
+
if (application_javascript_path = destination.join("app/javascript/application.js")).exist?
|
22
|
+
insert_into_file application_javascript_path.to_s, %(import "trix"\nimport "@rails/actiontext"\n)
|
29
23
|
else
|
30
|
-
say <<~
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
require("trix")
|
36
|
-
require("@rails/actiontext")
|
24
|
+
say <<~INSTRUCTIONS, :green
|
25
|
+
You must import the @rails/actiontext and trix JavaScript modules in your application entrypoint.
|
26
|
+
INSTRUCTIONS
|
27
|
+
end
|
37
28
|
|
38
|
-
|
39
|
-
|
40
|
-
WARNING
|
29
|
+
if (importmap_path = destination.join("config/importmap.rb")).exist?
|
30
|
+
append_to_file importmap_path.to_s, %(pin "trix"\npin "@rails/actiontext", to: "actiontext.js"\n)
|
41
31
|
end
|
42
32
|
end
|
43
33
|
|
44
34
|
def create_actiontext_files
|
45
|
-
|
35
|
+
destination = Pathname(destination_root)
|
36
|
+
|
37
|
+
template "actiontext.css", "app/assets/stylesheets/actiontext.css"
|
38
|
+
|
39
|
+
unless destination.join("app/assets/application.css").exist?
|
40
|
+
if (stylesheets = Dir.glob "#{destination_root}/app/assets/stylesheets/application.*.{scss,css}").length > 0
|
41
|
+
insert_into_file stylesheets.first.to_s, %(@import 'actiontext.css';)
|
42
|
+
else
|
43
|
+
say <<~INSTRUCTIONS, :green
|
44
|
+
To use the Trix editor, you must require 'app/assets/stylesheets/actiontext.css' in your base stylesheet.
|
45
|
+
INSTRUCTIONS
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
gem_root = "#{__dir__}/../../../.."
|
46
50
|
|
47
|
-
copy_file "#{
|
51
|
+
copy_file "#{gem_root}/app/views/active_storage/blobs/_blob.html.erb",
|
48
52
|
"app/views/active_storage/blobs/_blob.html.erb"
|
53
|
+
|
54
|
+
copy_file "#{gem_root}/app/views/layouts/action_text/contents/_content.html.erb",
|
55
|
+
"app/views/layouts/action_text/contents/_content.html.erb"
|
56
|
+
end
|
57
|
+
|
58
|
+
def enable_image_processing_gem
|
59
|
+
if (gemfile_path = Pathname(destination_root).join("Gemfile")).exist?
|
60
|
+
say "Ensure image_processing gem has been enabled so image uploads will work (remember to bundle!)"
|
61
|
+
uncomment_lines gemfile_path, /gem "image_processing"/
|
62
|
+
end
|
49
63
|
end
|
50
64
|
|
51
65
|
def create_migrations
|
@@ -53,15 +67,6 @@ module ActionText
|
|
53
67
|
end
|
54
68
|
|
55
69
|
hook_for :test_framework
|
56
|
-
|
57
|
-
private
|
58
|
-
GEM_ROOT = "#{__dir__}/../../../.."
|
59
|
-
|
60
|
-
def js_dependencies
|
61
|
-
js_package = JSON.load(Pathname.new("#{GEM_ROOT}/package.json"))
|
62
|
-
js_package["peerDependencies"].merge \
|
63
|
-
js_package["name"] => "^#{js_package["version"]}"
|
64
|
-
end
|
65
70
|
end
|
66
71
|
end
|
67
72
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
/*
|
2
|
+
* Provides a drop-in pointer for the default Trix stylesheet that will format the toolbar and
|
3
|
+
* the trix-editor content (whether displayed or under editing). Feel free to incorporate this
|
4
|
+
* inclusion directly in any other asset bundle and remove this file.
|
5
|
+
*
|
6
|
+
<%- if defined?(Webpacker::Engine) -%>
|
7
|
+
*= require trix/dist/trix
|
8
|
+
<%- else -%>
|
9
|
+
*= require trix
|
10
|
+
<% end -%>
|
11
|
+
*/
|
12
|
+
|
13
|
+
/*
|
14
|
+
* We need to override trix.css’s image gallery styles to accommodate the
|
15
|
+
* <action-text-attachment> element we wrap around attachments. Otherwise,
|
16
|
+
* images in galleries will be squished by the max-width: 33%; rule.
|
17
|
+
*/
|
18
|
+
.trix-content .attachment-gallery > action-text-attachment,
|
19
|
+
.trix-content .attachment-gallery > .attachment {
|
20
|
+
flex: 1 0 33%;
|
21
|
+
padding: 0 0.5em;
|
22
|
+
max-width: 33%;
|
23
|
+
}
|
24
|
+
|
25
|
+
.trix-content .attachment-gallery.attachment-gallery--2 > action-text-attachment,
|
26
|
+
.trix-content .attachment-gallery.attachment-gallery--2 > .attachment, .trix-content .attachment-gallery.attachment-gallery--4 > action-text-attachment,
|
27
|
+
.trix-content .attachment-gallery.attachment-gallery--4 > .attachment {
|
28
|
+
flex-basis: 50%;
|
29
|
+
max-width: 50%;
|
30
|
+
}
|
31
|
+
|
32
|
+
.trix-content action-text-attachment .attachment {
|
33
|
+
padding: 0 !important;
|
34
|
+
max-width: 100% !important;
|
35
|
+
}
|
data/package.json
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
{
|
2
2
|
"name": "@rails/actiontext",
|
3
|
-
"version": "
|
3
|
+
"version": "7.0.0-rc1",
|
4
4
|
"description": "Edit and display rich text in Rails applications",
|
5
5
|
"main": "app/javascript/actiontext/index.js",
|
6
|
+
"type": "module",
|
6
7
|
"files": [
|
7
8
|
"app/javascript/actiontext/*.js"
|
8
9
|
],
|
@@ -21,9 +22,18 @@
|
|
21
22
|
],
|
22
23
|
"license": "MIT",
|
23
24
|
"dependencies": {
|
24
|
-
"@rails/activestorage": "
|
25
|
+
"@rails/activestorage": ">= 7.0.0-alpha1"
|
25
26
|
},
|
26
27
|
"peerDependencies": {
|
27
|
-
"trix": "^1.
|
28
|
+
"trix": "^1.3.1"
|
29
|
+
},
|
30
|
+
"devDependencies": {
|
31
|
+
"@rollup/plugin-node-resolve": "^11.0.1",
|
32
|
+
"@rollup/plugin-commonjs": "^19.0.1",
|
33
|
+
"rollup": "^2.35.1",
|
34
|
+
"trix": "^1.3.1"
|
35
|
+
},
|
36
|
+
"scripts": {
|
37
|
+
"build": "rollup --config rollup.config.js"
|
28
38
|
}
|
29
39
|
}
|