actiontext5 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
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