actiontext 6.0.0.beta1
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.
- checksums.yaml +7 -0
 - data/CHANGELOG.md +5 -0
 - data/MIT-LICENSE +21 -0
 - data/README.md +9 -0
 - data/app/helpers/action_text/content_helper.rb +30 -0
 - data/app/helpers/action_text/tag_helper.rb +75 -0
 - data/app/javascript/actiontext/attachment_upload.js +45 -0
 - data/app/javascript/actiontext/index.js +10 -0
 - data/app/models/action_text/rich_text.rb +29 -0
 - data/app/views/action_text/attachables/_missing_attachable.html.erb +1 -0
 - data/app/views/action_text/attachables/_remote_image.html.erb +8 -0
 - data/app/views/action_text/attachment_galleries/_attachment_gallery.html.erb +3 -0
 - data/app/views/action_text/content/_layout.html.erb +3 -0
 - data/app/views/active_storage/blobs/_blob.html.erb +14 -0
 - data/db/migrate/201805281641_create_action_text_tables.rb +14 -0
 - data/lib/action_text.rb +37 -0
 - data/lib/action_text/attachable.rb +82 -0
 - data/lib/action_text/attachables/content_attachment.rb +38 -0
 - data/lib/action_text/attachables/missing_attachable.rb +13 -0
 - data/lib/action_text/attachables/remote_image.rb +46 -0
 - data/lib/action_text/attachment.rb +103 -0
 - data/lib/action_text/attachment_gallery.rb +65 -0
 - data/lib/action_text/attachments/caching.rb +16 -0
 - data/lib/action_text/attachments/minification.rb +17 -0
 - data/lib/action_text/attachments/trix_conversion.rb +34 -0
 - data/lib/action_text/attribute.rb +48 -0
 - data/lib/action_text/content.rb +132 -0
 - data/lib/action_text/engine.rb +50 -0
 - data/lib/action_text/fragment.rb +57 -0
 - data/lib/action_text/gem_version.rb +17 -0
 - data/lib/action_text/html_conversion.rb +24 -0
 - data/lib/action_text/plain_text_conversion.rb +81 -0
 - data/lib/action_text/serialization.rb +34 -0
 - data/lib/action_text/trix_attachment.rb +92 -0
 - data/lib/action_text/version.rb +10 -0
 - data/lib/tasks/actiontext.rake +20 -0
 - data/lib/templates/actiontext.scss +36 -0
 - data/lib/templates/fixtures.yml +4 -0
 - data/lib/templates/installer.rb +32 -0
 - data/package.json +29 -0
 - metadata +158 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 9eb946145656ee7d27e4ee9ce733a3e0f55e1b63ab16e4cea8741fecaac697f6
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 658a5373916fd11abfdbd569746f1f4ed23fb71cc52294d513d063de47a148ab
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 8418aebfa611943071b773ec51544e039f4f3e8220a594c4a66bfced904754da4f2ee1fc7d0472e7d6e0e207f4aad70f8828509fd0f6a95df5db1557e42f34cb
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 427541191977534c0d36bf0b4cfb0615a5525e0ef645a939b31e066dac689ec0288bc7b6dca919523c8adae6ffee56888bc0d0cb232cd592c51bd8f06daee357
         
     | 
    
        data/CHANGELOG.md
    ADDED
    
    
    
        data/MIT-LICENSE
    ADDED
    
    | 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            MIT License
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Copyright (c) 2019 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,9 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Action Text
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Action Text brings rich text content and editing to Rails. It includes the [Trix editor](https://trix-editor.org) that handles everything from formatting to links to quotes to lists to embedded images and galleries. The rich text content generated by the Trix editor is saved in its own RichText model that's associated with any existing Active Record model in the application. Any embedded images (or other attachments) are automatically stored using Active Storage and associated with the included RichText model.
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            You can read more about Action Text in the [Action Text Overview](https://edgeguides.rubyonrails.org/action_text_overview.html) guide.
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            ## License
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            Action Text is released under the [MIT License](https://opensource.org/licenses/MIT).
         
     | 
| 
         @@ -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 "@rails/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,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ActionText
         
     | 
| 
      
 4 
     | 
    
         
            +
              # The RichText record holds the content produced by the Trix editor in a serialized `body` attribute.
         
     | 
| 
      
 5 
     | 
    
         
            +
              # It also holds all the references to the embedded files, which are stored using Active Storage.
         
     | 
| 
      
 6 
     | 
    
         
            +
              # This record is then associated with the Active Record model the application desires to have
         
     | 
| 
      
 7 
     | 
    
         
            +
              # rich text content using the `has_rich_text` class method.
         
     | 
| 
      
 8 
     | 
    
         
            +
              class RichText < ActiveRecord::Base
         
     | 
| 
      
 9 
     | 
    
         
            +
                self.table_name = "action_text_rich_texts"
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                serialize :body, ActionText::Content
         
     | 
| 
      
 12 
     | 
    
         
            +
                delegate :to_s, :nil?, to: :body
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                belongs_to :record, polymorphic: true, touch: true
         
     | 
| 
      
 15 
     | 
    
         
            +
                has_many_attached :embeds
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                before_save do
         
     | 
| 
      
 18 
     | 
    
         
            +
                  self.embeds = body.attachments.map(&:attachable) if body.present?
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def to_plain_text
         
     | 
| 
      
 22 
     | 
    
         
            +
                  body&.to_plain_text.to_s
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                delegate :blank?, :empty?, :present?, to: :to_plain_text
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            ActiveSupport.run_load_hooks :action_text_rich_text, ActionText::RichText
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <%= "☒" -%>
         
     | 
| 
         @@ -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,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>
         
     | 
| 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class CreateActionTextTables < ActiveRecord::Migration[6.0]
         
     | 
| 
      
 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
         
     | 
    
        data/lib/action_text.rb
    ADDED
    
    | 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "active_support"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "active_support/rails"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            require "nokogiri"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            module ActionText
         
     | 
| 
      
 9 
     | 
    
         
            +
              extend ActiveSupport::Autoload
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              autoload :Attachable
         
     | 
| 
      
 12 
     | 
    
         
            +
              autoload :AttachmentGallery
         
     | 
| 
      
 13 
     | 
    
         
            +
              autoload :Attachment
         
     | 
| 
      
 14 
     | 
    
         
            +
              autoload :Attribute
         
     | 
| 
      
 15 
     | 
    
         
            +
              autoload :Content
         
     | 
| 
      
 16 
     | 
    
         
            +
              autoload :Fragment
         
     | 
| 
      
 17 
     | 
    
         
            +
              autoload :HtmlConversion
         
     | 
| 
      
 18 
     | 
    
         
            +
              autoload :PlainTextConversion
         
     | 
| 
      
 19 
     | 
    
         
            +
              autoload :Serialization
         
     | 
| 
      
 20 
     | 
    
         
            +
              autoload :TrixAttachment
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              module Attachables
         
     | 
| 
      
 23 
     | 
    
         
            +
                extend ActiveSupport::Autoload
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                autoload :ContentAttachment
         
     | 
| 
      
 26 
     | 
    
         
            +
                autoload :MissingAttachable
         
     | 
| 
      
 27 
     | 
    
         
            +
                autoload :RemoteImage
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              module Attachments
         
     | 
| 
      
 31 
     | 
    
         
            +
                extend ActiveSupport::Autoload
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                autoload :Caching
         
     | 
| 
      
 34 
     | 
    
         
            +
                autoload :Minification
         
     | 
| 
      
 35 
     | 
    
         
            +
                autoload :TrixConversion
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
            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 || 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
         
     | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ActionText
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Attachables
         
     | 
| 
      
 5 
     | 
    
         
            +
                class ContentAttachment
         
     | 
| 
      
 6 
     | 
    
         
            +
                  include ActiveModel::Model
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  def self.from_node(node)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    if node["content-type"]
         
     | 
| 
      
 10 
     | 
    
         
            +
                      if matches = node["content-type"].match(/vnd\.rubyonrails\.(.+)\.html/)
         
     | 
| 
      
 11 
     | 
    
         
            +
                        attachment = new(name: matches[1])
         
     | 
| 
      
 12 
     | 
    
         
            +
                        attachment if attachment.valid?
         
     | 
| 
      
 13 
     | 
    
         
            +
                      end
         
     | 
| 
      
 14 
     | 
    
         
            +
                    end
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  attr_accessor :name
         
     | 
| 
      
 18 
     | 
    
         
            +
                  validates_inclusion_of :name, in: %w( horizontal-rule )
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def attachable_plain_text_representation(caption)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    case name
         
     | 
| 
      
 22 
     | 
    
         
            +
                    when "horizontal-rule"
         
     | 
| 
      
 23 
     | 
    
         
            +
                      " ┄ "
         
     | 
| 
      
 24 
     | 
    
         
            +
                    else
         
     | 
| 
      
 25 
     | 
    
         
            +
                      " "
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def to_partial_path
         
     | 
| 
      
 30 
     | 
    
         
            +
                    "action_text/attachables/content_attachment"
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def to_trix_content_attachment_partial_path
         
     | 
| 
      
 34 
     | 
    
         
            +
                    "action_text/attachables/content_attachments/#{name.underscore}"
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     |