actiontext5 5.2.0

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.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +2 -0
  3. data/.gitignore +13 -0
  4. data/.travis.yml +17 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +140 -0
  7. data/LICENSE +21 -0
  8. data/README.md +1 -0
  9. data/Rakefile +27 -0
  10. data/actiontext.gemspec +29 -0
  11. data/app/helpers/action_text/content_helper.rb +30 -0
  12. data/app/helpers/action_text/tag_helper.rb +75 -0
  13. data/app/javascript/actiontext/attachment_upload.js +45 -0
  14. data/app/javascript/actiontext/index.js +11 -0
  15. data/app/models/action_text/rich_text.rb +25 -0
  16. data/app/views/action_text/attachables/_missing_attachable.html.erb +1 -0
  17. data/app/views/action_text/attachables/_remote_image.html.erb +8 -0
  18. data/app/views/action_text/attachment_galleries/_attachment_gallery.html.erb +3 -0
  19. data/app/views/action_text/content/_layout.html.erb +3 -0
  20. data/app/views/active_storage/blobs/_blob.html.erb +14 -0
  21. data/bin/test +6 -0
  22. data/bin/webpack +29 -0
  23. data/bin/webpack-dev-server +29 -0
  24. data/db/migrate/201805281641_create_action_text_tables.rb +14 -0
  25. data/lib/action_text/attachable.rb +82 -0
  26. data/lib/action_text/attachables/content_attachment.rb +38 -0
  27. data/lib/action_text/attachables/missing_attachable.rb +11 -0
  28. data/lib/action_text/attachables/remote_image.rb +46 -0
  29. data/lib/action_text/attachment.rb +103 -0
  30. data/lib/action_text/attachment_gallery.rb +65 -0
  31. data/lib/action_text/attachments/caching.rb +16 -0
  32. data/lib/action_text/attachments/minification.rb +17 -0
  33. data/lib/action_text/attachments/trix_conversion.rb +34 -0
  34. data/lib/action_text/attribute.rb +48 -0
  35. data/lib/action_text/content.rb +126 -0
  36. data/lib/action_text/engine.rb +45 -0
  37. data/lib/action_text/fragment.rb +57 -0
  38. data/lib/action_text/html_conversion.rb +24 -0
  39. data/lib/action_text/plain_text_conversion.rb +81 -0
  40. data/lib/action_text/serialization.rb +34 -0
  41. data/lib/action_text/trix_attachment.rb +92 -0
  42. data/lib/action_text/version.rb +5 -0
  43. data/lib/action_text.rb +38 -0
  44. data/lib/actiontext5.rb +1 -0
  45. data/lib/tasks/actiontext.rake +20 -0
  46. data/lib/templates/actiontext.scss +36 -0
  47. data/lib/templates/fixtures.yml +4 -0
  48. data/lib/templates/installer.rb +22 -0
  49. data/package.json +21 -0
  50. data/test/dummy/.babelrc +18 -0
  51. data/test/dummy/.postcssrc.yml +3 -0
  52. data/test/dummy/Rakefile +6 -0
  53. data/test/dummy/app/assets/config/manifest.js +3 -0
  54. data/test/dummy/app/assets/images/.keep +0 -0
  55. data/test/dummy/app/assets/stylesheets/application.css +16 -0
  56. data/test/dummy/app/assets/stylesheets/messages.css +4 -0
  57. data/test/dummy/app/assets/stylesheets/scaffold.css +80 -0
  58. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  59. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  60. data/test/dummy/app/controllers/application_controller.rb +2 -0
  61. data/test/dummy/app/controllers/concerns/.keep +0 -0
  62. data/test/dummy/app/controllers/messages_controller.rb +58 -0
  63. data/test/dummy/app/helpers/application_helper.rb +2 -0
  64. data/test/dummy/app/helpers/messages_helper.rb +2 -0
  65. data/test/dummy/app/javascript/packs/application.js +1 -0
  66. data/test/dummy/app/jobs/application_job.rb +2 -0
  67. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  68. data/test/dummy/app/models/application_record.rb +3 -0
  69. data/test/dummy/app/models/concerns/.keep +0 -0
  70. data/test/dummy/app/models/message.rb +4 -0
  71. data/test/dummy/app/models/person.rb +7 -0
  72. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  73. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  74. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  75. data/test/dummy/app/views/messages/_form.html.erb +27 -0
  76. data/test/dummy/app/views/messages/edit.html.erb +6 -0
  77. data/test/dummy/app/views/messages/index.html.erb +29 -0
  78. data/test/dummy/app/views/messages/new.html.erb +5 -0
  79. data/test/dummy/app/views/messages/show.html.erb +13 -0
  80. data/test/dummy/app/views/people/_trix_content_attachment.html.erb +3 -0
  81. data/test/dummy/bin/bundle +3 -0
  82. data/test/dummy/bin/rails +4 -0
  83. data/test/dummy/bin/rake +4 -0
  84. data/test/dummy/bin/setup +36 -0
  85. data/test/dummy/bin/update +31 -0
  86. data/test/dummy/bin/yarn +11 -0
  87. data/test/dummy/config/application.rb +19 -0
  88. data/test/dummy/config/boot.rb +5 -0
  89. data/test/dummy/config/cable.yml +10 -0
  90. data/test/dummy/config/database.yml +25 -0
  91. data/test/dummy/config/environment.rb +5 -0
  92. data/test/dummy/config/environments/development.rb +63 -0
  93. data/test/dummy/config/environments/production.rb +96 -0
  94. data/test/dummy/config/environments/test.rb +46 -0
  95. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  96. data/test/dummy/config/initializers/assets.rb +14 -0
  97. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  98. data/test/dummy/config/initializers/content_security_policy.rb +22 -0
  99. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  100. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  101. data/test/dummy/config/initializers/inflections.rb +16 -0
  102. data/test/dummy/config/initializers/mime_types.rb +4 -0
  103. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  104. data/test/dummy/config/locales/en.yml +33 -0
  105. data/test/dummy/config/puma.rb +34 -0
  106. data/test/dummy/config/routes.rb +4 -0
  107. data/test/dummy/config/spring.rb +6 -0
  108. data/test/dummy/config/storage.yml +35 -0
  109. data/test/dummy/config/webpack/development.js +3 -0
  110. data/test/dummy/config/webpack/environment.js +3 -0
  111. data/test/dummy/config/webpack/production.js +3 -0
  112. data/test/dummy/config/webpack/test.js +3 -0
  113. data/test/dummy/config/webpacker.yml +65 -0
  114. data/test/dummy/config.ru +5 -0
  115. data/test/dummy/db/migrate/20180208205311_create_messages.rb +8 -0
  116. data/test/dummy/db/migrate/20180212164506_create_active_storage_tables.active_storage.rb +26 -0
  117. data/test/dummy/db/migrate/2018052816_create_action_text_tables.rb +14 -0
  118. data/test/dummy/db/migrate/20181003185713_create_people.rb +9 -0
  119. data/test/dummy/db/schema.rb +58 -0
  120. data/test/dummy/lib/assets/.keep +0 -0
  121. data/test/dummy/log/.keep +0 -0
  122. data/test/dummy/package.json +11 -0
  123. data/test/dummy/public/404.html +67 -0
  124. data/test/dummy/public/422.html +67 -0
  125. data/test/dummy/public/500.html +66 -0
  126. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  127. data/test/dummy/public/apple-touch-icon.png +0 -0
  128. data/test/dummy/public/favicon.ico +0 -0
  129. data/test/dummy/storage/.keep +0 -0
  130. data/test/dummy/tmp/.keep +0 -0
  131. data/test/dummy/tmp/storage/.keep +0 -0
  132. data/test/dummy/yarn.lock +6071 -0
  133. data/test/fixtures/files/racecar.jpg +0 -0
  134. data/test/template/form_helper_test.rb +71 -0
  135. data/test/test_helper.rb +30 -0
  136. data/test/unit/attachment_test.rb +60 -0
  137. data/test/unit/content_test.rb +116 -0
  138. data/test/unit/model_test.rb +47 -0
  139. data/test/unit/plain_text_conversion_test.rb +94 -0
  140. data/test/unit/trix_attachment_test.rb +83 -0
  141. data/yarn.lock +11 -0
  142. metadata +372 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8160e6be3d5b37488107aa744e5a8a2b74347ba2a36227c149d9e79dbd0630e5
4
+ data.tar.gz: a0674e2c6bbdd9a4fd9301870768fc560701e3cc1b86f8a68c4db7dd0d73aa40
5
+ SHA512:
6
+ metadata.gz: 9cc4c4939ae0f7d160faa09aefc48a13f6ee55659ca11b4511304b155982876cff63d4f1c0c5e58df2cebc982a598a2ac3c45fef7c09ef8c7196272415447952
7
+ data.tar.gz: 71bb739f6919d271cef629b64f39c3eb2c0fa9dfa743d4408954956221b6f1e9adb790b101a562f8d561ce5904fd154565750e94083f3e16ba05556891976094
data/.gitattributes ADDED
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ .bundle/
2
+ .byebug_history
3
+ log/*.log
4
+ node_modules/
5
+ pkg/
6
+ test/dummy/db/*.sqlite3
7
+ test/dummy/db/*.sqlite3-journal
8
+ test/dummy/log/*.log
9
+ test/dummy/node_modules/
10
+ test/dummy/public/
11
+ test/dummy/yarn-error.log
12
+ test/dummy/storage/
13
+ test/dummy/tmp/
data/.travis.yml ADDED
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+ sudo: false
3
+
4
+ rvm:
5
+ - 2.5.3
6
+ - ruby-head
7
+
8
+ cache:
9
+ bundler: true
10
+
11
+ matrix:
12
+ allow_failures:
13
+ - rvm: ruby-head
14
+ fast_finish: true
15
+
16
+ script:
17
+ - bundle exec rake
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+ git_source(:github) { |repo_path| "https://github.com/#{repo_path}.git" }
3
+
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,140 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ actiontext5 (5.2.0)
5
+ nokogiri
6
+ rails (~> 5.2.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actioncable (5.2.8)
12
+ actionpack (= 5.2.8)
13
+ nio4r (~> 2.0)
14
+ websocket-driver (>= 0.6.1)
15
+ actionmailer (5.2.8)
16
+ actionpack (= 5.2.8)
17
+ actionview (= 5.2.8)
18
+ activejob (= 5.2.8)
19
+ mail (~> 2.5, >= 2.5.4)
20
+ rails-dom-testing (~> 2.0)
21
+ actionpack (5.2.8)
22
+ actionview (= 5.2.8)
23
+ activesupport (= 5.2.8)
24
+ rack (~> 2.0, >= 2.0.8)
25
+ rack-test (>= 0.6.3)
26
+ rails-dom-testing (~> 2.0)
27
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
28
+ actionview (5.2.8)
29
+ activesupport (= 5.2.8)
30
+ builder (~> 3.1)
31
+ erubi (~> 1.4)
32
+ rails-dom-testing (~> 2.0)
33
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
34
+ activejob (5.2.8)
35
+ activesupport (= 5.2.8)
36
+ globalid (>= 0.3.6)
37
+ activemodel (5.2.8)
38
+ activesupport (= 5.2.8)
39
+ activerecord (5.2.8)
40
+ activemodel (= 5.2.8)
41
+ activesupport (= 5.2.8)
42
+ arel (>= 9.0)
43
+ activestorage (5.2.8)
44
+ actionpack (= 5.2.8)
45
+ activerecord (= 5.2.8)
46
+ marcel (~> 1.0.0)
47
+ activesupport (5.2.8)
48
+ concurrent-ruby (~> 1.0, >= 1.0.2)
49
+ i18n (>= 0.7, < 2)
50
+ minitest (~> 5.1)
51
+ tzinfo (~> 1.1)
52
+ arel (9.0.0)
53
+ builder (3.2.4)
54
+ byebug (11.1.3)
55
+ concurrent-ruby (1.1.10)
56
+ crass (1.0.6)
57
+ erubi (1.10.0)
58
+ globalid (1.0.0)
59
+ activesupport (>= 5.0)
60
+ i18n (1.10.0)
61
+ concurrent-ruby (~> 1.0)
62
+ loofah (2.18.0)
63
+ crass (~> 1.0.2)
64
+ nokogiri (>= 1.5.9)
65
+ mail (2.7.1)
66
+ mini_mime (>= 0.1.1)
67
+ marcel (1.0.2)
68
+ method_source (1.0.0)
69
+ mini_magick (4.11.0)
70
+ mini_mime (1.1.2)
71
+ mini_portile2 (2.8.0)
72
+ minitest (5.15.0)
73
+ nio4r (2.5.8)
74
+ nokogiri (1.13.6)
75
+ mini_portile2 (~> 2.8.0)
76
+ racc (~> 1.4)
77
+ racc (1.6.0)
78
+ rack (2.2.3.1)
79
+ rack-proxy (0.7.2)
80
+ rack
81
+ rack-test (1.1.0)
82
+ rack (>= 1.0, < 3)
83
+ rails (5.2.8)
84
+ actioncable (= 5.2.8)
85
+ actionmailer (= 5.2.8)
86
+ actionpack (= 5.2.8)
87
+ actionview (= 5.2.8)
88
+ activejob (= 5.2.8)
89
+ activemodel (= 5.2.8)
90
+ activerecord (= 5.2.8)
91
+ activestorage (= 5.2.8)
92
+ activesupport (= 5.2.8)
93
+ bundler (>= 1.3.0)
94
+ railties (= 5.2.8)
95
+ sprockets-rails (>= 2.0.0)
96
+ rails-dom-testing (2.0.3)
97
+ activesupport (>= 4.2.0)
98
+ nokogiri (>= 1.6)
99
+ rails-html-sanitizer (1.4.2)
100
+ loofah (~> 2.3)
101
+ railties (5.2.8)
102
+ actionpack (= 5.2.8)
103
+ activesupport (= 5.2.8)
104
+ method_source
105
+ rake (>= 0.8.7)
106
+ thor (>= 0.19.0, < 2.0)
107
+ rake (13.0.6)
108
+ sprockets (4.0.3)
109
+ concurrent-ruby (~> 1.0)
110
+ rack (> 1, < 3)
111
+ sprockets-rails (3.4.2)
112
+ actionpack (>= 5.2)
113
+ activesupport (>= 5.2)
114
+ sprockets (>= 3.0.0)
115
+ sqlite3 (1.4.2)
116
+ thor (1.2.1)
117
+ thread_safe (0.3.6)
118
+ tzinfo (1.2.9)
119
+ thread_safe (~> 0.1)
120
+ webpacker (3.2.2)
121
+ activesupport (>= 4.2)
122
+ rack-proxy (>= 0.6.1)
123
+ railties (>= 4.2)
124
+ websocket-driver (0.7.5)
125
+ websocket-extensions (>= 0.1.0)
126
+ websocket-extensions (0.1.5)
127
+
128
+ PLATFORMS
129
+ ruby
130
+
131
+ DEPENDENCIES
132
+ actiontext5!
133
+ bundler (~> 1.15)
134
+ byebug
135
+ mini_magick
136
+ sqlite3
137
+ webpacker (~> 3.2.2)
138
+
139
+ BUNDLED WITH
140
+ 1.17.2
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Basecamp, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ This gem is just a fork from https://github.com/rails/actiontext builded to work with Rails 5.2.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Action Text'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ require 'bundler/gem_tasks'
18
+
19
+ require 'rake/testtask'
20
+
21
+ Rake::TestTask.new(:test) do |t|
22
+ t.libs << 'test'
23
+ t.pattern = 'test/**/*_test.rb'
24
+ t.verbose = false
25
+ end
26
+
27
+ task default: :test
@@ -0,0 +1,29 @@
1
+ $:.push File.expand_path("lib", __dir__)
2
+
3
+ # Maintain your gem's version:
4
+ require "action_text/version"
5
+
6
+ # Describe your gem and declare its dependencies:
7
+ Gem::Specification.new do |s|
8
+ s.name = "actiontext5"
9
+ s.version = ActionText::VERSION
10
+ s.authors = ["Benoit MARTIN-CHAVE"]
11
+ s.email = ["benoit@martin-chave.com"]
12
+ s.summary = "ActionText for Rails 5"
13
+ s.homepage = "https://github.com/BenoitMC/actiontext5"
14
+ s.license = "MIT"
15
+
16
+ s.required_ruby_version = ">= 2.5.0"
17
+
18
+ s.add_dependency "rails", "~> 5.2.0"
19
+ s.add_dependency "nokogiri"
20
+
21
+ s.add_development_dependency "bundler", "~> 1.15"
22
+ s.add_development_dependency "mini_magick"
23
+ s.add_development_dependency "sqlite3"
24
+ s.add_development_dependency "webpacker", "~> 3.2.2"
25
+ s.add_development_dependency "byebug"
26
+
27
+ s.files = `git ls-files`.split("\n")
28
+ s.test_files = `git ls-files -- test/*`.split("\n")
29
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionText
4
+ module ContentHelper
5
+ SANITIZER = Rails::Html::Sanitizer.white_list_sanitizer
6
+ ALLOWED_TAGS = SANITIZER.allowed_tags + [ ActionText::Attachment::TAG_NAME, "figure", "figcaption" ]
7
+ ALLOWED_ATTRIBUTES = SANITIZER.allowed_attributes + ActionText::Attachment::ATTRIBUTES
8
+
9
+ def render_action_text_content(content)
10
+ content = content.render_attachments do |attachment|
11
+ unless attachment.in?(content.gallery_attachments)
12
+ attachment.node.tap do |node|
13
+ node.inner_html = render(attachment, in_gallery: false).chomp
14
+ end
15
+ end
16
+ end
17
+
18
+ content = content.render_attachment_galleries do |attachment_gallery|
19
+ render(layout: attachment_gallery, object: attachment_gallery) do
20
+ attachment_gallery.attachments.map do |attachment|
21
+ attachment.node.inner_html = render(attachment, in_gallery: true).chomp
22
+ attachment.to_html
23
+ end.join("").html_safe
24
+ end.chomp
25
+ end
26
+
27
+ sanitize content.to_html, tags: ALLOWED_TAGS, attributes: ALLOWED_ATTRIBUTES
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionText
4
+ module TagHelper
5
+ cattr_accessor(:id, instance_accessor: false) { 0 }
6
+
7
+ # Returns a `trix-editor` tag that instantiates the Trix JavaScript editor as well as a hidden field
8
+ # that Trix will write to on changes, so the content will be sent on form submissions.
9
+ #
10
+ # ==== Options
11
+ # * <tt>:class</tt> - Defaults to "trix-content" which ensures default styling is applied.
12
+ #
13
+ # ==== Example
14
+ #
15
+ # rich_text_area_tag "content", message.content
16
+ # # <input type="hidden" name="content" id="trix_input_post_1">
17
+ # # <trix-editor id="content" input="trix_input_post_1" class="trix-content" ...></trix-editor>
18
+ def rich_text_area_tag(name, value = nil, options = {})
19
+ options = options.symbolize_keys
20
+
21
+ options[:input] ||= "trix_input_#{ActionText::TagHelper.id += 1}"
22
+ options[:class] ||= "trix-content"
23
+
24
+ options[:data] ||= {}
25
+ options[:data][:direct_upload_url] = main_app.rails_direct_uploads_url
26
+ options[:data][:blob_url_template] = main_app.rails_service_blob_url(":signed_id", ":filename")
27
+
28
+ editor_tag = content_tag("trix-editor", "", options)
29
+ input_tag = hidden_field_tag(name, value, id: options[:input])
30
+
31
+ input_tag + editor_tag
32
+ end
33
+ end
34
+ end
35
+
36
+ module ActionView::Helpers
37
+ class Tags::ActionText < Tags::Base
38
+ delegate :dom_id, to: ActionView::RecordIdentifier
39
+
40
+ def render
41
+ options = @options.stringify_keys
42
+ add_default_name_and_id(options)
43
+ options["input"] ||= dom_id(object, [options["id"], :trix_input].compact.join("_")) if object
44
+ @template_object.rich_text_area_tag(options.delete("name"), editable_value, options)
45
+ end
46
+
47
+ def editable_value
48
+ value&.body.try(:to_trix_html)
49
+ end
50
+ end
51
+
52
+ module FormHelper
53
+ # Returns a `trix-editor` tag that instantiates the Trix JavaScript editor as well as a hidden field
54
+ # that Trix will write to on changes, so the content will be sent on form submissions.
55
+ #
56
+ # ==== Options
57
+ # * <tt>:class</tt> - Defaults to "trix-content" which ensures default styling is applied.
58
+ #
59
+ # ==== Example
60
+ # form_with(model: @message) do |form|
61
+ # form.rich_text_area :content
62
+ # end
63
+ # # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1">
64
+ # # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
65
+ def rich_text_area(object_name, method, options = {})
66
+ Tags::ActionText.new(object_name, method, self, options).render
67
+ end
68
+ end
69
+
70
+ class FormBuilder
71
+ def rich_text_area(method, options = {})
72
+ @template.rich_text_area(@object_name, method, objectify_options(options))
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,45 @@
1
+ import { DirectUpload } from "activestorage"
2
+
3
+ export class AttachmentUpload {
4
+ constructor(attachment, element) {
5
+ this.attachment = attachment
6
+ this.element = element
7
+ this.directUpload = new DirectUpload(attachment.file, this.directUploadUrl, this)
8
+ }
9
+
10
+ start() {
11
+ this.directUpload.create(this.directUploadDidComplete.bind(this))
12
+ }
13
+
14
+ directUploadWillStoreFileWithXHR(xhr) {
15
+ xhr.upload.addEventListener("progress", event => {
16
+ const progress = event.loaded / event.total * 100
17
+ this.attachment.setUploadProgress(progress)
18
+ })
19
+ }
20
+
21
+ directUploadDidComplete(error, attributes) {
22
+ if (error) {
23
+ throw new Error(`Direct upload failed: ${error}`)
24
+ }
25
+
26
+ this.attachment.setAttributes({
27
+ sgid: attributes.attachable_sgid,
28
+ url: this.createBlobUrl(attributes.signed_id, attributes.filename)
29
+ })
30
+ }
31
+
32
+ createBlobUrl(signedId, filename) {
33
+ return this.blobUrlTemplate
34
+ .replace(":signed_id", signedId)
35
+ .replace(":filename", encodeURIComponent(filename))
36
+ }
37
+
38
+ get directUploadUrl() {
39
+ return this.element.dataset.directUploadUrl
40
+ }
41
+
42
+ get blobUrlTemplate() {
43
+ return this.element.dataset.blobUrlTemplate
44
+ }
45
+ }
@@ -0,0 +1,11 @@
1
+ import * as Trix from "trix"
2
+ import { AttachmentUpload } from "./attachment_upload"
3
+
4
+ addEventListener("trix-attachment-add", event => {
5
+ const { attachment, target } = event
6
+
7
+ if (attachment.file) {
8
+ const upload = new AttachmentUpload(attachment, target)
9
+ upload.start()
10
+ }
11
+ })
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The RichText record holds the content produced by the Trix editor in a serialized `body` attribute.
4
+ # It also holds all the references to the embedded files, which are stored using Active Storage.
5
+ # This record is then associated with the Active Record model the application desires to have
6
+ # rich text content using the `has_rich_text` class method.
7
+ class ActionText::RichText < ActiveRecord::Base
8
+ self.table_name = "action_text_rich_texts"
9
+
10
+ serialize :body, ActionText::Content
11
+ delegate :to_s, :nil?, to: :body
12
+
13
+ belongs_to :record, polymorphic: true, touch: true
14
+ has_many_attached :embeds
15
+
16
+ before_save do
17
+ self.embeds = body.attachments.map(&:attachable) if body.present?
18
+ end
19
+
20
+ def to_plain_text
21
+ body&.to_plain_text.to_s
22
+ end
23
+
24
+ delegate :blank?, :empty?, :present?, to: :to_plain_text
25
+ end
@@ -0,0 +1,8 @@
1
+ <figure class="attachment attachment--preview">
2
+ <%= image_tag(remote_image.url, width: remote_image.width, height: remote_image.height) %>
3
+ <% if caption = remote_image.try(:caption) %>
4
+ <figcaption class="attachment__caption">
5
+ <%= caption %>
6
+ </figcaption>
7
+ <% end %>
8
+ </figure>
@@ -0,0 +1,3 @@
1
+ <div class="attachment-gallery attachment-gallery--<%= attachment_gallery.size %>">
2
+ <%= yield %>
3
+ </div>
@@ -0,0 +1,3 @@
1
+ <div class="trix-content">
2
+ <%= render_action_text_content(content) %>
3
+ </div>
@@ -0,0 +1,14 @@
1
+ <figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
2
+ <% if blob.representable? %>
3
+ <%= image_tag blob.representation(resize_to_fit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %>
4
+ <% end %>
5
+
6
+ <figcaption class="attachment__caption">
7
+ <% if caption = blob.try(:caption) %>
8
+ <%= caption %>
9
+ <% else %>
10
+ <span class="attachment__name"><%= blob.filename %></span>
11
+ <span class="attachment__size"><%= number_to_human_size blob.byte_size %></span>
12
+ <% end %>
13
+ </figcaption>
14
+ </figure>
data/bin/test ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ $: << File.expand_path("../test", __dir__)
4
+
5
+ require "bundler/setup"
6
+ require "rails/plugin/test"
data/bin/webpack ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'webpack' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("webpacker", "webpack")
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'webpack-dev-server' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("webpacker", "webpack-dev-server")
@@ -0,0 +1,14 @@
1
+ class CreateActionTextTables < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :action_text_rich_texts do |t|
4
+ t.string :name, null: false
5
+ t.text :body, limit: 16777215
6
+ t.references :record, null: false, polymorphic: true, index: false
7
+
8
+ t.datetime :created_at, null: false
9
+ t.datetime :updated_at, null: false
10
+
11
+ t.index [ :record_type, :record_id, :name ], name: "index_action_text_rich_texts_uniqueness", unique: true
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionText
4
+ module Attachable
5
+ extend ActiveSupport::Concern
6
+
7
+ LOCATOR_NAME = "attachable"
8
+
9
+ class << self
10
+ def from_node(node)
11
+ if attachable = attachable_from_sgid(node["sgid"])
12
+ attachable
13
+ elsif attachable = ActionText::Attachables::ContentAttachment.from_node(node)
14
+ attachable
15
+ elsif attachable = ActionText::Attachables::RemoteImage.from_node(node)
16
+ attachable
17
+ else
18
+ ActionText::Attachables::MissingAttachable
19
+ end
20
+ end
21
+
22
+ def from_attachable_sgid(sgid, options = {})
23
+ method = sgid.is_a?(Array) ? :locate_many_signed : :locate_signed
24
+ record = GlobalID::Locator.public_send(method, sgid, options.merge(for: LOCATOR_NAME))
25
+ record or raise ActiveRecord::RecordNotFound
26
+ end
27
+
28
+ private
29
+ def attachable_from_sgid(sgid)
30
+ from_attachable_sgid(sgid)
31
+ rescue ActiveRecord::RecordNotFound
32
+ nil
33
+ end
34
+ end
35
+
36
+ class_methods do
37
+ def from_attachable_sgid(sgid)
38
+ ActionText::Attachable.from_attachable_sgid(sgid, only: self)
39
+ end
40
+ end
41
+
42
+ def attachable_sgid
43
+ to_sgid(expires_in: nil, for: LOCATOR_NAME).to_s
44
+ end
45
+
46
+ def attachable_content_type
47
+ try(:content_type) || "application/octet-stream"
48
+ end
49
+
50
+ def attachable_filename
51
+ filename.to_s if respond_to?(:filename)
52
+ end
53
+
54
+ def attachable_filesize
55
+ try(:byte_size) || try(:filesize)
56
+ end
57
+
58
+ def attachable_metadata
59
+ try(:metadata) || {}
60
+ end
61
+
62
+ def previewable_attachable?
63
+ false
64
+ end
65
+
66
+ def as_json(*)
67
+ super.merge(attachable_sgid: attachable_sgid)
68
+ end
69
+
70
+ def to_rich_text_attributes(attributes = {})
71
+ attributes.dup.tap do |attrs|
72
+ attrs[:sgid] = attachable_sgid
73
+ attrs[:content_type] = attachable_content_type
74
+ attrs[:previewable] = true if previewable_attachable?
75
+ attrs[:filename] = attachable_filename
76
+ attrs[:filesize] = attachable_filesize
77
+ attrs[:width] = attachable_metadata[:width]
78
+ attrs[:height] = attachable_metadata[:height]
79
+ end.compact
80
+ end
81
+ end
82
+ end