actiontext 6.0.2.1

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 +7 -0
  2. data/CHANGELOG.md +45 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.md +9 -0
  5. data/app/helpers/action_text/content_helper.rb +37 -0
  6. data/app/helpers/action_text/tag_helper.rb +79 -0
  7. data/app/javascript/actiontext/attachment_upload.js +45 -0
  8. data/app/javascript/actiontext/index.js +10 -0
  9. data/app/models/action_text/rich_text.rb +29 -0
  10. data/app/views/action_text/attachables/_missing_attachable.html.erb +1 -0
  11. data/app/views/action_text/attachables/_remote_image.html.erb +8 -0
  12. data/app/views/action_text/attachment_galleries/_attachment_gallery.html.erb +3 -0
  13. data/app/views/action_text/content/_layout.html.erb +3 -0
  14. data/app/views/active_storage/blobs/_blob.html.erb +14 -0
  15. data/db/migrate/20180528164100_create_action_text_tables.rb +13 -0
  16. data/lib/action_text.rb +37 -0
  17. data/lib/action_text/attachable.rb +86 -0
  18. data/lib/action_text/attachables/content_attachment.rb +38 -0
  19. data/lib/action_text/attachables/missing_attachable.rb +13 -0
  20. data/lib/action_text/attachables/remote_image.rb +46 -0
  21. data/lib/action_text/attachment.rb +103 -0
  22. data/lib/action_text/attachment_gallery.rb +65 -0
  23. data/lib/action_text/attachments/caching.rb +16 -0
  24. data/lib/action_text/attachments/minification.rb +17 -0
  25. data/lib/action_text/attachments/trix_conversion.rb +34 -0
  26. data/lib/action_text/attribute.rb +45 -0
  27. data/lib/action_text/content.rb +132 -0
  28. data/lib/action_text/engine.rb +54 -0
  29. data/lib/action_text/fragment.rb +57 -0
  30. data/lib/action_text/gem_version.rb +17 -0
  31. data/lib/action_text/html_conversion.rb +24 -0
  32. data/lib/action_text/plain_text_conversion.rb +81 -0
  33. data/lib/action_text/serialization.rb +34 -0
  34. data/lib/action_text/trix_attachment.rb +92 -0
  35. data/lib/action_text/version.rb +10 -0
  36. data/lib/tasks/actiontext.rake +20 -0
  37. data/lib/templates/actiontext.scss +36 -0
  38. data/lib/templates/fixtures.yml +4 -0
  39. data/lib/templates/installer.rb +45 -0
  40. data/package.json +29 -0
  41. metadata +161 -0
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails"
4
+ require "action_controller/railtie"
5
+ require "active_record/railtie"
6
+ require "active_storage/engine"
7
+
8
+ require "action_text"
9
+
10
+ module ActionText
11
+ class Engine < Rails::Engine
12
+ isolate_namespace ActionText
13
+ config.eager_load_namespaces << ActionText
14
+
15
+ initializer "action_text.attribute" do
16
+ ActiveSupport.on_load(:active_record) do
17
+ include ActionText::Attribute
18
+ end
19
+ end
20
+
21
+ initializer "action_text.attachable" do
22
+ ActiveSupport.on_load(:active_storage_blob) do
23
+ include ActionText::Attachable
24
+
25
+ def previewable_attachable?
26
+ representable?
27
+ end
28
+
29
+ def attachable_plain_text_representation(caption = nil)
30
+ "[#{caption || filename}]"
31
+ end
32
+ end
33
+ end
34
+
35
+ initializer "action_text.helper" do
36
+ ActiveSupport.on_load(:action_controller_base) do
37
+ helper ActionText::Engine.helpers
38
+ end
39
+ end
40
+
41
+ initializer "action_text.renderer" do |app|
42
+ app.executor.to_run { ActionText::Content.renderer = ApplicationController.renderer }
43
+ app.executor.to_complete { ActionText::Content.renderer = ApplicationController.renderer }
44
+
45
+ ActiveSupport.on_load(:action_text_content) do
46
+ self.renderer = ApplicationController.renderer
47
+ end
48
+
49
+ ActiveSupport.on_load(:action_controller_base) do
50
+ before_action { ActionText::Content.renderer = ApplicationController.renderer.new(request.env) }
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionText
4
+ class Fragment
5
+ class << self
6
+ def wrap(fragment_or_html)
7
+ case fragment_or_html
8
+ when self
9
+ fragment_or_html
10
+ when Nokogiri::HTML::DocumentFragment
11
+ new(fragment_or_html)
12
+ else
13
+ from_html(fragment_or_html)
14
+ end
15
+ end
16
+
17
+ def from_html(html)
18
+ new(ActionText::HtmlConversion.fragment_for_html(html.to_s.strip))
19
+ end
20
+ end
21
+
22
+ attr_reader :source
23
+
24
+ def initialize(source)
25
+ @source = source
26
+ end
27
+
28
+ def find_all(selector)
29
+ source.css(selector)
30
+ end
31
+
32
+ def update
33
+ yield source = self.source.clone
34
+ self.class.new(source)
35
+ end
36
+
37
+ def replace(selector)
38
+ update do |source|
39
+ source.css(selector).each do |node|
40
+ node.replace(yield(node).to_s)
41
+ end
42
+ end
43
+ end
44
+
45
+ def to_plain_text
46
+ @plain_text ||= PlainTextConversion.node_to_plain_text(source)
47
+ end
48
+
49
+ def to_html
50
+ @html ||= HtmlConversion.node_to_html(source)
51
+ end
52
+
53
+ def to_s
54
+ to_html
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionText
4
+ # Returns the currently-loaded version of Action Text as a <tt>Gem::Version</tt>.
5
+ def self.gem_version
6
+ Gem::Version.new VERSION::STRING
7
+ end
8
+
9
+ module VERSION
10
+ MAJOR = 6
11
+ MINOR = 0
12
+ TINY = 2
13
+ PRE = "1"
14
+
15
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionText
4
+ module HtmlConversion
5
+ extend self
6
+
7
+ def node_to_html(node)
8
+ node.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_HTML)
9
+ end
10
+
11
+ def fragment_for_html(html)
12
+ document.fragment(html)
13
+ end
14
+
15
+ def create_element(tag_name, attributes = {})
16
+ document.create_element(tag_name, attributes)
17
+ end
18
+
19
+ private
20
+ def document
21
+ Nokogiri::HTML::Document.new.tap { |doc| doc.encoding = "UTF-8" }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionText
4
+ module PlainTextConversion
5
+ extend self
6
+
7
+ def node_to_plain_text(node)
8
+ remove_trailing_newlines(plain_text_for_node(node))
9
+ end
10
+
11
+ private
12
+ def plain_text_for_node(node, index = 0)
13
+ if respond_to?(plain_text_method_for_node(node), true)
14
+ send(plain_text_method_for_node(node), node, index)
15
+ else
16
+ plain_text_for_node_children(node)
17
+ end
18
+ end
19
+
20
+ def plain_text_for_node_children(node)
21
+ node.children.each_with_index.map do |child, index|
22
+ plain_text_for_node(child, index)
23
+ end.compact.join("")
24
+ end
25
+
26
+ def plain_text_method_for_node(node)
27
+ :"plain_text_for_#{node.name}_node"
28
+ end
29
+
30
+ def plain_text_for_block(node, index = 0)
31
+ "#{remove_trailing_newlines(plain_text_for_node_children(node))}\n\n"
32
+ end
33
+
34
+ %i[ h1 p ul ol ].each do |element|
35
+ alias_method :"plain_text_for_#{element}_node", :plain_text_for_block
36
+ end
37
+
38
+ def plain_text_for_br_node(node, index)
39
+ "\n"
40
+ end
41
+
42
+ def plain_text_for_text_node(node, index)
43
+ remove_trailing_newlines(node.text)
44
+ end
45
+
46
+ def plain_text_for_div_node(node, index)
47
+ "#{remove_trailing_newlines(plain_text_for_node_children(node))}\n"
48
+ end
49
+
50
+ def plain_text_for_figcaption_node(node, index)
51
+ "[#{remove_trailing_newlines(plain_text_for_node_children(node))}]"
52
+ end
53
+
54
+ def plain_text_for_blockquote_node(node, index)
55
+ text = plain_text_for_block(node)
56
+ text.sub(/\A(\s*)(.+?)(\s*)\Z/m, '\1“\2”\3')
57
+ end
58
+
59
+ def plain_text_for_li_node(node, index)
60
+ bullet = bullet_for_li_node(node, index)
61
+ text = remove_trailing_newlines(plain_text_for_node_children(node))
62
+ "#{bullet} #{text}\n"
63
+ end
64
+
65
+ def remove_trailing_newlines(text)
66
+ text.chomp("")
67
+ end
68
+
69
+ def bullet_for_li_node(node, index)
70
+ if list_node_name_for_li_node(node) == "ol"
71
+ "#{index + 1}."
72
+ else
73
+ "•"
74
+ end
75
+ end
76
+
77
+ def list_node_name_for_li_node(node)
78
+ node.ancestors.lazy.map(&:name).grep(/^[uo]l$/).first
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionText
4
+ module Serialization
5
+ extend ActiveSupport::Concern
6
+
7
+ class_methods do
8
+ def load(content)
9
+ new(content) if content
10
+ end
11
+
12
+ def dump(content)
13
+ case content
14
+ when nil
15
+ nil
16
+ when self
17
+ content.to_html
18
+ else
19
+ new(content).to_html
20
+ end
21
+ end
22
+ end
23
+
24
+ # Marshal compatibility
25
+
26
+ class_methods do
27
+ alias_method :_load, :load
28
+ end
29
+
30
+ def _dump(*)
31
+ self.class.dump(self)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionText
4
+ class TrixAttachment
5
+ TAG_NAME = "figure"
6
+ SELECTOR = "[data-trix-attachment]"
7
+
8
+ COMPOSED_ATTRIBUTES = %w( caption presentation )
9
+ ATTRIBUTES = %w( sgid contentType url href filename filesize width height previewable content ) + COMPOSED_ATTRIBUTES
10
+ ATTRIBUTE_TYPES = {
11
+ "previewable" => ->(value) { value.to_s == "true" },
12
+ "filesize" => ->(value) { Integer(value.to_s) rescue value },
13
+ "width" => ->(value) { Integer(value.to_s) rescue nil },
14
+ "height" => ->(value) { Integer(value.to_s) rescue nil },
15
+ :default => ->(value) { value.to_s }
16
+ }
17
+
18
+ class << self
19
+ def from_attributes(attributes)
20
+ attributes = process_attributes(attributes)
21
+
22
+ trix_attachment_attributes = attributes.except(*COMPOSED_ATTRIBUTES)
23
+ trix_attributes = attributes.slice(*COMPOSED_ATTRIBUTES)
24
+
25
+ node = ActionText::HtmlConversion.create_element(TAG_NAME)
26
+ node["data-trix-attachment"] = JSON.generate(trix_attachment_attributes)
27
+ node["data-trix-attributes"] = JSON.generate(trix_attributes) if trix_attributes.any?
28
+
29
+ new(node)
30
+ end
31
+
32
+ private
33
+ def process_attributes(attributes)
34
+ typecast_attribute_values(transform_attribute_keys(attributes))
35
+ end
36
+
37
+ def transform_attribute_keys(attributes)
38
+ attributes.transform_keys { |key| key.to_s.underscore.camelize(:lower) }
39
+ end
40
+
41
+ def typecast_attribute_values(attributes)
42
+ attributes.map do |key, value|
43
+ typecast = ATTRIBUTE_TYPES[key] || ATTRIBUTE_TYPES[:default]
44
+ [key, typecast.call(value)]
45
+ end.to_h
46
+ end
47
+ end
48
+
49
+ attr_reader :node
50
+
51
+ def initialize(node)
52
+ @node = node
53
+ end
54
+
55
+ def attributes
56
+ @attributes ||= attachment_attributes.merge(composed_attributes).slice(*ATTRIBUTES)
57
+ end
58
+
59
+ def to_html
60
+ ActionText::HtmlConversion.node_to_html(node)
61
+ end
62
+
63
+ def to_s
64
+ to_html
65
+ end
66
+
67
+ private
68
+ def attachment_attributes
69
+ read_json_object_attribute("data-trix-attachment")
70
+ end
71
+
72
+ def composed_attributes
73
+ read_json_object_attribute("data-trix-attributes")
74
+ end
75
+
76
+ def read_json_object_attribute(name)
77
+ read_json_attribute(name) || {}
78
+ end
79
+
80
+ def read_json_attribute(name)
81
+ if value = node[name]
82
+ begin
83
+ JSON.parse(value)
84
+ rescue => e
85
+ Rails.logger.error "[#{self.class.name}] Couldn't parse JSON #{value} from NODE #{node.inspect}"
86
+ Rails.logger.error "[#{self.class.name}] Failed with #{e.class}: #{e.backtrace}"
87
+ nil
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "gem_version"
4
+
5
+ module ActionText
6
+ # Returns the currently-loaded version of Action Text as a <tt>Gem::Version</tt>.
7
+ def self.version
8
+ gem_version
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :action_text do
4
+ # Prevent migration installation task from showing up twice.
5
+ Rake::Task["install:migrations"].clear_comments
6
+
7
+ desc "Copy over the migration, stylesheet, and JavaScript files"
8
+ task install: %w( environment run_installer copy_migrations )
9
+
10
+ task :run_installer do
11
+ installer_template = File.expand_path("../templates/installer.rb", __dir__)
12
+ system "#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{installer_template}"
13
+ end
14
+
15
+ task :copy_migrations do
16
+ Rake::Task["active_storage:install:migrations"].invoke
17
+ Rake::Task["railties:install:migrations"].reenable # Otherwise you can't run 2 migration copy tasks in one invocation
18
+ Rake::Task["action_text:install:migrations"].invoke
19
+ end
20
+ end
@@ -0,0 +1,36 @@
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
+ //= require trix/dist/trix
7
+
8
+ // We need to override trix.css’s image gallery styles to accommodate the
9
+ // <action-text-attachment> element we wrap around attachments. Otherwise,
10
+ // images in galleries will be squished by the max-width: 33%; rule.
11
+ .trix-content {
12
+ .attachment-gallery {
13
+ > action-text-attachment,
14
+ > .attachment {
15
+ flex: 1 0 33%;
16
+ padding: 0 0.5em;
17
+ max-width: 33%;
18
+ }
19
+
20
+ &.attachment-gallery--2,
21
+ &.attachment-gallery--4 {
22
+ > action-text-attachment,
23
+ > .attachment {
24
+ flex-basis: 50%;
25
+ max-width: 50%;
26
+ }
27
+ }
28
+ }
29
+
30
+ action-text-attachment {
31
+ .attachment {
32
+ padding: 0 !important;
33
+ max-width: 100% !important;
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,4 @@
1
+ # one:
2
+ # record: name_of_fixture (ClassOfFixture)
3
+ # name: content
4
+ # body: <p>In a <i>million</i> stars!</p>
@@ -0,0 +1,45 @@
1
+ require "pathname"
2
+ require "json"
3
+
4
+ APPLICATION_PACK_PATH = Pathname.new("app/javascript/packs/application.js")
5
+ JS_PACKAGE_PATH = Pathname.new("#{__dir__}/../../package.json")
6
+
7
+ JS_PACKAGE = JSON.load(JS_PACKAGE_PATH)
8
+ JS_DEPENDENCIES = JS_PACKAGE["peerDependencies"].dup.merge \
9
+ JS_PACKAGE["name"] => "^#{JS_PACKAGE["version"]}"
10
+
11
+ say "Copying actiontext.scss to app/assets/stylesheets"
12
+ copy_file "#{__dir__}/actiontext.scss", "app/assets/stylesheets/actiontext.scss"
13
+
14
+ say "Copying fixtures to test/fixtures/action_text/rich_texts.yml"
15
+ copy_file "#{__dir__}/fixtures.yml", "test/fixtures/action_text/rich_texts.yml"
16
+
17
+ say "Copying blob rendering partial to app/views/active_storage/blobs/_blob.html.erb"
18
+ copy_file "#{__dir__}/../../app/views/active_storage/blobs/_blob.html.erb",
19
+ "app/views/active_storage/blobs/_blob.html.erb"
20
+
21
+ say "Installing JavaScript dependencies"
22
+ run "yarn add #{JS_DEPENDENCIES.map { |name, version| "#{name}@#{version}" }.join(" ")}"
23
+
24
+ if APPLICATION_PACK_PATH.exist?
25
+ JS_DEPENDENCIES.keys.each do |name|
26
+ line = %[require("#{name}")]
27
+ unless APPLICATION_PACK_PATH.read.include? line
28
+ say "Adding #{name} to #{APPLICATION_PACK_PATH}"
29
+ append_to_file APPLICATION_PACK_PATH, "\n#{line}"
30
+ end
31
+ end
32
+ else
33
+ warn <<~WARNING
34
+ WARNING: Action Text can't locate your JavaScript bundle to add its package dependencies.
35
+
36
+ Add these lines to any bundles:
37
+
38
+ require("trix")
39
+ require("@rails/actiontext")
40
+
41
+ Alternatively, install and setup the webpacker gem then rerun `bin/rails action_text:install`
42
+ to have these dependencies added automatically.
43
+
44
+ WARNING
45
+ end