./tools/rdoc-to-md --only=actiontext -a
This commit is contained in:
parent
195d80199f
commit
1ecac5b8d3
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
require "rails-html-sanitizer"
|
||||
|
||||
module ActionText
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
require "active_support/core_ext/object/try"
|
||||
require "action_view/helpers/tags/placeholderable"
|
||||
|
||||
@ -7,20 +9,24 @@ module ActionText
|
||||
module TagHelper
|
||||
cattr_accessor(:id, instance_accessor: false) { 0 }
|
||||
|
||||
# Returns a +trix-editor+ tag that instantiates the Trix JavaScript editor as well as a hidden field
|
||||
# that Trix will write to on changes, so the content will be sent on form submissions.
|
||||
# Returns a `trix-editor` tag that instantiates the Trix JavaScript editor as
|
||||
# well as a hidden field that Trix will write to on changes, so the content will
|
||||
# be sent on form submissions.
|
||||
#
|
||||
# ==== Options
|
||||
# * <tt>:class</tt> - Defaults to "trix-content" so that default styles will be applied.
|
||||
# Setting this to a different value will prevent default styles from being applied.
|
||||
# * <tt>[:data][:direct_upload_url]</tt> - Defaults to +rails_direct_uploads_url+.
|
||||
# * <tt>[:data][:blob_url_template]</tt> - Defaults to <tt>rails_service_blob_url(":signed_id", ":filename")</tt>.
|
||||
# #### Options
|
||||
# * `:class` - Defaults to "trix-content" so that default styles will be
|
||||
# applied. Setting this to a different value will prevent default styles
|
||||
# from being applied.
|
||||
# * `[:data][:direct_upload_url]` - Defaults to `rails_direct_uploads_url`.
|
||||
# * `[:data][:blob_url_template]` - Defaults to
|
||||
# `rails_service_blob_url(":signed_id", ":filename")`.
|
||||
#
|
||||
# ==== Example
|
||||
#
|
||||
# rich_text_area_tag "content", message.content
|
||||
# # <input type="hidden" name="content" id="trix_input_post_1">
|
||||
# # <trix-editor id="content" input="trix_input_post_1" class="trix-content" ...></trix-editor>
|
||||
# #### Example
|
||||
#
|
||||
# rich_text_area_tag "content", message.content
|
||||
# # <input type="hidden" name="content" id="trix_input_post_1">
|
||||
# # <trix-editor id="content" input="trix_input_post_1" class="trix-content" ...></trix-editor>
|
||||
def rich_text_area_tag(name, value = nil, options = {})
|
||||
options = options.symbolize_keys
|
||||
form = options.delete(:form)
|
||||
@ -56,23 +62,27 @@ def render
|
||||
end
|
||||
|
||||
module FormHelper
|
||||
# Returns a +trix-editor+ tag that instantiates the Trix JavaScript editor as well as a hidden field
|
||||
# that Trix will write to on changes, so the content will be sent on form submissions.
|
||||
# Returns a `trix-editor` tag that instantiates the Trix JavaScript editor as
|
||||
# well as a hidden field that Trix will write to on changes, so the content will
|
||||
# be sent on form submissions.
|
||||
#
|
||||
# ==== Options
|
||||
# * <tt>:class</tt> - Defaults to "trix-content" which ensures default styling is applied.
|
||||
# * <tt>:value</tt> - Adds a default value to the HTML input tag.
|
||||
# * <tt>[:data][:direct_upload_url]</tt> - Defaults to +rails_direct_uploads_url+.
|
||||
# * <tt>[:data][:blob_url_template]</tt> - Defaults to <tt>rails_service_blob_url(":signed_id", ":filename")</tt>.
|
||||
# #### Options
|
||||
# * `:class` - Defaults to "trix-content" which ensures default styling is
|
||||
# applied.
|
||||
# * `:value` - Adds a default value to the HTML input tag.
|
||||
# * `[:data][:direct_upload_url]` - Defaults to `rails_direct_uploads_url`.
|
||||
# * `[:data][:blob_url_template]` - Defaults to
|
||||
# `rails_service_blob_url(":signed_id", ":filename")`.
|
||||
#
|
||||
# ==== Example
|
||||
# rich_text_area :message, :content
|
||||
# # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1">
|
||||
# # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
|
||||
#
|
||||
# rich_text_area :message, :content, value: "<h1>Default message</h1>"
|
||||
# # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1" value="<h1>Default message</h1>">
|
||||
# # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
|
||||
# #### Example
|
||||
# rich_text_area :message, :content
|
||||
# # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1">
|
||||
# # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
|
||||
#
|
||||
# rich_text_area :message, :content, value: "<h1>Default message</h1>"
|
||||
# # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1" value="<h1>Default message</h1>">
|
||||
# # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
|
||||
def rich_text_area(object_name, method, options = {})
|
||||
Tags::ActionText.new(object_name, method, self, options).render
|
||||
end
|
||||
@ -81,9 +91,9 @@ def rich_text_area(object_name, method, options = {})
|
||||
class FormBuilder
|
||||
# Wraps ActionView::Helpers::FormHelper#rich_text_area for form builders:
|
||||
#
|
||||
# <%= form_with model: @message do |f| %>
|
||||
# <%= f.rich_text_area :content %>
|
||||
# <% end %>
|
||||
# <%= form_with model: @message do |f| %>
|
||||
# <%= f.rich_text_area :content %>
|
||||
# <% end %>
|
||||
#
|
||||
# Please refer to the documentation of the base helper for details.
|
||||
def rich_text_area(method, options = {})
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
class EncryptedRichText < RichText
|
||||
encrypts :body
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
class Record < ActiveRecord::Base # :nodoc:
|
||||
self.abstract_class = true
|
||||
|
@ -1,37 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
# = Action Text \RichText
|
||||
# # Action Text RichText
|
||||
#
|
||||
# The RichText record holds the content produced by the Trix editor in a serialized +body+ attribute.
|
||||
# It also holds all the references to the embedded files, which are stored using Active Storage.
|
||||
# This record is then associated with the Active Record model the application desires to have
|
||||
# rich text content using the +has_rich_text+ class method.
|
||||
# The RichText record holds the content produced by the Trix editor in a
|
||||
# serialized `body` attribute. It also holds all the references to the embedded
|
||||
# files, which are stored using Active Storage. This record is then associated
|
||||
# with the Active Record model the application desires to have rich text content
|
||||
# using the `has_rich_text` class method.
|
||||
#
|
||||
# class Message < ActiveRecord::Base
|
||||
# has_rich_text :content
|
||||
# end
|
||||
# class Message < ActiveRecord::Base
|
||||
# has_rich_text :content
|
||||
# end
|
||||
#
|
||||
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
||||
# message.content #=> #<ActionText::RichText....
|
||||
# message.content.to_s # => "<h1>Funny times!</h1>"
|
||||
# message.content.to_plain_text # => "Funny times!"
|
||||
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
||||
# message.content #=> #<ActionText::RichText....
|
||||
# message.content.to_s # => "<h1>Funny times!</h1>"
|
||||
# message.content.to_plain_text # => "Funny times!"
|
||||
#
|
||||
# message = Message.create!(content: "<div onclick='action()'>safe<script>unsafe</script></div>")
|
||||
# message.content #=> #<ActionText::RichText....
|
||||
# message.content.to_s # => "<div>safeunsafe</div>"
|
||||
# message.content.to_plain_text # => "safeunsafe"
|
||||
# message = Message.create!(content: "<div onclick='action()'>safe<script>unsafe</script></div>")
|
||||
# message.content #=> #<ActionText::RichText....
|
||||
# message.content.to_s # => "<div>safeunsafe</div>"
|
||||
# message.content.to_plain_text # => "safeunsafe"
|
||||
class RichText < Record
|
||||
##
|
||||
# :method: to_s
|
||||
#
|
||||
# Safely transforms RichText into an HTML String.
|
||||
#
|
||||
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
||||
# message.content.to_s # => "<h1>Funny times!</h1>"
|
||||
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
||||
# message.content.to_s # => "<h1>Funny times!</h1>"
|
||||
#
|
||||
# message = Message.create!(content: "<div onclick='action()'>safe<script>unsafe</script></div>")
|
||||
# message.content.to_s # => "<div>safeunsafe</div>"
|
||||
# message = Message.create!(content: "<div onclick='action()'>safe<script>unsafe</script></div>")
|
||||
# message.content.to_s # => "<div>safeunsafe</div>"
|
||||
|
||||
serialize :body, coder: ActionText::Content
|
||||
delegate :to_s, :nil?, to: :body
|
||||
@ -43,32 +46,33 @@ class RichText < Record
|
||||
self.embeds = body.attachables.grep(ActiveStorage::Blob).uniq if body.present?
|
||||
end
|
||||
|
||||
# Returns a plain-text version of the markup contained by the +body+ attribute,
|
||||
# Returns a plain-text version of the markup contained by the `body` attribute,
|
||||
# with tags removed but HTML entities encoded.
|
||||
#
|
||||
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
||||
# message.content.to_plain_text # => "Funny times!"
|
||||
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
||||
# message.content.to_plain_text # => "Funny times!"
|
||||
#
|
||||
# NOTE: that the returned string is not HTML safe and should not be rendered in browsers.
|
||||
# NOTE: that the returned string is not HTML safe and should not be rendered in
|
||||
# browsers.
|
||||
#
|
||||
# message = Message.create!(content: "<script>alert()</script>")
|
||||
# message.content.to_plain_text # => "<script>alert()</script>"
|
||||
# message = Message.create!(content: "<script>alert()</script>")
|
||||
# message.content.to_plain_text # => "<script>alert()</script>"
|
||||
def to_plain_text
|
||||
body&.to_plain_text.to_s
|
||||
end
|
||||
|
||||
# Returns the +body+ attribute in a format that makes it editable in the Trix
|
||||
# Returns the `body` attribute in a format that makes it editable in the Trix
|
||||
# editor. Previews of attachments are rendered inline.
|
||||
#
|
||||
# content = "<h1>Funny Times!</h1><figure data-trix-attachment='{\"sgid\":\"..."\}'></figure>"
|
||||
# message = Message.create!(content: content)
|
||||
# message.content.to_trix_html # =>
|
||||
# # <div class="trix-content">
|
||||
# # <h1>Funny times!</h1>
|
||||
# # <figure data-trix-attachment='{\"sgid\":\"..."\}'>
|
||||
# # <img src="http://example.org/rails/active_storage/.../funny.jpg">
|
||||
# # </figure>
|
||||
# # </div>
|
||||
# content = "<h1>Funny Times!</h1><figure data-trix-attachment='{\"sgid\":\"..."\}'></figure>"
|
||||
# message = Message.create!(content: content)
|
||||
# message.content.to_trix_html # =>
|
||||
# # <div class="trix-content">
|
||||
# # <h1>Funny times!</h1>
|
||||
# # <figure data-trix-attachment='{\"sgid\":\"..."\}'>
|
||||
# # <img src="http://example.org/rails/active_storage/.../funny.jpg">
|
||||
# # </figure>
|
||||
# # </div>
|
||||
def to_trix_html
|
||||
body&.to_trix_html
|
||||
end
|
||||
|
@ -1,31 +1,33 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
# = Action Text \Attachable
|
||||
# # Action Text Attachable
|
||||
#
|
||||
# Include this module to make a record attachable to an ActionText::Content.
|
||||
#
|
||||
# class Person < ApplicationRecord
|
||||
# include ActionText::Attachable
|
||||
# end
|
||||
# class Person < ApplicationRecord
|
||||
# include ActionText::Attachable
|
||||
# end
|
||||
#
|
||||
# person = Person.create! name: "Javan"
|
||||
# html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
|
||||
# content = ActionText::Content.new(html)
|
||||
# content.attachables # => [person]
|
||||
# person = Person.create! name: "Javan"
|
||||
# html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
|
||||
# content = ActionText::Content.new(html)
|
||||
# content.attachables # => [person]
|
||||
module Attachable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
LOCATOR_NAME = "attachable"
|
||||
|
||||
class << self
|
||||
# Extracts the +ActionText::Attachable+ from the attachment HTML node:
|
||||
# Extracts the `ActionText::Attachable` from the attachment HTML node:
|
||||
#
|
||||
# person = Person.create! name: "Javan"
|
||||
# html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
|
||||
# fragment = ActionText::Fragment.wrap(html)
|
||||
# attachment_node = fragment.find_all(ActionText::Attachment.tag_name).first
|
||||
# ActionText::Attachable.from_node(attachment_node) # => person
|
||||
# person = Person.create! name: "Javan"
|
||||
# html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
|
||||
# fragment = ActionText::Fragment.wrap(html)
|
||||
# attachment_node = fragment.find_all(ActionText::Attachment.tag_name).first
|
||||
# ActionText::Attachable.from_node(attachment_node) # => person
|
||||
def from_node(node)
|
||||
if attachable = attachable_from_sgid(node["sgid"])
|
||||
attachable
|
||||
@ -57,23 +59,23 @@ def from_attachable_sgid(sgid)
|
||||
ActionText::Attachable.from_attachable_sgid(sgid, only: self)
|
||||
end
|
||||
|
||||
# Returns the path to the partial that is used for rendering missing attachables.
|
||||
# Defaults to "action_text/attachables/missing_attachable".
|
||||
# Returns the path to the partial that is used for rendering missing
|
||||
# attachables. Defaults to "action_text/attachables/missing_attachable".
|
||||
#
|
||||
# Override to render a different partial:
|
||||
#
|
||||
# class User < ApplicationRecord
|
||||
# def self.to_missing_attachable_partial_path
|
||||
# "users/missing_attachable"
|
||||
# class User < ApplicationRecord
|
||||
# def self.to_missing_attachable_partial_path
|
||||
# "users/missing_attachable"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
def to_missing_attachable_partial_path
|
||||
ActionText::Attachables::MissingAttachable::DEFAULT_PARTIAL_PATH
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the Signed Global ID for the attachable. The purpose of the ID is
|
||||
# set to 'attachable' so it can't be reused for other purposes.
|
||||
# Returns the Signed Global ID for the attachable. The purpose of the ID is set
|
||||
# to 'attachable' so it can't be reused for other purposes.
|
||||
def attachable_sgid
|
||||
to_sgid(expires_in: nil, for: LOCATOR_NAME).to_s
|
||||
end
|
||||
@ -98,30 +100,30 @@ def previewable_attachable?
|
||||
false
|
||||
end
|
||||
|
||||
# Returns the path to the partial that is used for rendering the attachable
|
||||
# in Trix. Defaults to +to_partial_path+.
|
||||
# Returns the path to the partial that is used for rendering the attachable in
|
||||
# Trix. Defaults to `to_partial_path`.
|
||||
#
|
||||
# Override to render a different partial:
|
||||
#
|
||||
# class User < ApplicationRecord
|
||||
# def to_trix_content_attachment_partial_path
|
||||
# "users/trix_content_attachment"
|
||||
# class User < ApplicationRecord
|
||||
# def to_trix_content_attachment_partial_path
|
||||
# "users/trix_content_attachment"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
def to_trix_content_attachment_partial_path
|
||||
to_partial_path
|
||||
end
|
||||
|
||||
# Returns the path to the partial that is used for rendering the attachable.
|
||||
# Defaults to +to_partial_path+.
|
||||
# Defaults to `to_partial_path`.
|
||||
#
|
||||
# Override to render a different partial:
|
||||
#
|
||||
# class User < ApplicationRecord
|
||||
# def to_attachable_partial_path
|
||||
# "users/attachable"
|
||||
# class User < ApplicationRecord
|
||||
# def to_attachable_partial_path
|
||||
# "users/attachable"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
def to_attachable_partial_path
|
||||
to_partial_path
|
||||
end
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
module Attachables
|
||||
class ContentAttachment # :nodoc:
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
module Attachables
|
||||
class MissingAttachable
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
module Attachables
|
||||
class RemoteImage
|
||||
|
@ -1,19 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
require "active_support/core_ext/object/try"
|
||||
|
||||
module ActionText
|
||||
# = Action Text \Attachment
|
||||
# # Action Text Attachment
|
||||
#
|
||||
# Attachments serialize attachables to HTML or plain text.
|
||||
#
|
||||
# class Person < ApplicationRecord
|
||||
# include ActionText::Attachable
|
||||
# end
|
||||
# class Person < ApplicationRecord
|
||||
# include ActionText::Attachable
|
||||
# end
|
||||
#
|
||||
# attachable = Person.create! name: "Javan"
|
||||
# attachment = ActionText::Attachment.from_attachable(attachable)
|
||||
# attachment.to_html # => "<action-text-attachment sgid=\"BAh7CEk..."
|
||||
# attachable = Person.create! name: "Javan"
|
||||
# attachment = ActionText::Attachment.from_attachable(attachable)
|
||||
# attachment.to_html # => "<action-text-attachment sgid=\"BAh7CEk..."
|
||||
class Attachment
|
||||
include Attachments::TrixConversion, Attachments::Minification, Attachments::Caching
|
||||
|
||||
@ -82,29 +84,29 @@ def with_full_attributes
|
||||
|
||||
# Converts the attachment to plain text.
|
||||
#
|
||||
# attachable = ActiveStorage::Blob.find_by filename: "racecar.jpg"
|
||||
# attachment = ActionText::Attachment.from_attachable(attachable)
|
||||
# attachment.to_plain_text # => "[racecar.jpg]"
|
||||
# attachable = ActiveStorage::Blob.find_by filename: "racecar.jpg"
|
||||
# attachment = ActionText::Attachment.from_attachable(attachable)
|
||||
# attachment.to_plain_text # => "[racecar.jpg]"
|
||||
#
|
||||
# Use the +caption+ when set:
|
||||
# Use the `caption` when set:
|
||||
#
|
||||
# attachment = ActionText::Attachment.from_attachable(attachable, caption: "Vroom vroom")
|
||||
# attachment.to_plain_text # => "[Vroom vroom]"
|
||||
# attachment = ActionText::Attachment.from_attachable(attachable, caption: "Vroom vroom")
|
||||
# attachment.to_plain_text # => "[Vroom vroom]"
|
||||
#
|
||||
# The presentation can be overridden by implementing the
|
||||
# +attachable_plain_text_representation+ method:
|
||||
# `attachable_plain_text_representation` method:
|
||||
#
|
||||
# class Person < ApplicationRecord
|
||||
# include ActionText::Attachable
|
||||
# class Person < ApplicationRecord
|
||||
# include ActionText::Attachable
|
||||
#
|
||||
# def attachable_plain_text_representation
|
||||
# "[#{name}]"
|
||||
# def attachable_plain_text_representation
|
||||
# "[#{name}]"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# attachable = Person.create! name: "Javan"
|
||||
# attachment = ActionText::Attachment.from_attachable(attachable)
|
||||
# attachment.to_plain_text # => "[Javan]"
|
||||
# attachable = Person.create! name: "Javan"
|
||||
# attachment = ActionText::Attachment.from_attachable(attachable)
|
||||
# attachment.to_plain_text # => "[Javan]"
|
||||
def to_plain_text
|
||||
if respond_to?(:attachable_plain_text_representation)
|
||||
attachable_plain_text_representation(caption)
|
||||
@ -115,9 +117,9 @@ def to_plain_text
|
||||
|
||||
# Converts the attachment to HTML.
|
||||
#
|
||||
# attachable = Person.create! name: "Javan"
|
||||
# attachment = ActionText::Attachment.from_attachable(attachable)
|
||||
# attachment.to_html # => "<action-text-attachment sgid=\"BAh7CEk...
|
||||
# attachable = Person.create! name: "Javan"
|
||||
# attachment = ActionText::Attachment.from_attachable(attachable)
|
||||
# attachment.to_html # => "<action-text-attachment sgid=\"BAh7CEk...
|
||||
def to_html
|
||||
HtmlConversion.node_to_html(node)
|
||||
end
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
class AttachmentGallery
|
||||
include ActiveModel::Model
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
module Attachments
|
||||
module Caching
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
module Attachments
|
||||
module Minification
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
require "active_support/core_ext/object/try"
|
||||
|
||||
module ActionText
|
||||
|
@ -1,44 +1,52 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
module Attribute
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
# Provides access to a dependent RichText model that holds the body and attachments for a single named rich text attribute.
|
||||
# This dependent attribute is lazily instantiated and will be auto-saved when it's been changed. Example:
|
||||
# Provides access to a dependent RichText model that holds the body and
|
||||
# attachments for a single named rich text attribute. This dependent attribute
|
||||
# is lazily instantiated and will be auto-saved when it's been changed. Example:
|
||||
#
|
||||
# class Message < ActiveRecord::Base
|
||||
# has_rich_text :content
|
||||
# end
|
||||
# class Message < ActiveRecord::Base
|
||||
# has_rich_text :content
|
||||
# end
|
||||
#
|
||||
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
||||
# message.content? #=> true
|
||||
# message.content.to_s # => "<h1>Funny times!</h1>"
|
||||
# message.content.to_plain_text # => "Funny times!"
|
||||
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
||||
# message.content? #=> true
|
||||
# message.content.to_s # => "<h1>Funny times!</h1>"
|
||||
# message.content.to_plain_text # => "Funny times!"
|
||||
#
|
||||
# The dependent RichText model will also automatically process attachments links as sent via the Trix-powered editor.
|
||||
# These attachments are associated with the RichText model using Active Storage.
|
||||
# The dependent RichText model will also automatically process attachments links
|
||||
# as sent via the Trix-powered editor. These attachments are associated with the
|
||||
# RichText model using Active Storage.
|
||||
#
|
||||
# If you wish to preload the dependent RichText model, you can use the named scope:
|
||||
# If you wish to preload the dependent RichText model, you can use the named
|
||||
# scope:
|
||||
#
|
||||
# Message.all.with_rich_text_content # Avoids N+1 queries when you just want the body, not the attachments.
|
||||
# Message.all.with_rich_text_content_and_embeds # Avoids N+1 queries when you just want the body and attachments.
|
||||
# Message.all.with_all_rich_text # Loads all rich text associations.
|
||||
# Message.all.with_rich_text_content # Avoids N+1 queries when you just want the body, not the attachments.
|
||||
# Message.all.with_rich_text_content_and_embeds # Avoids N+1 queries when you just want the body and attachments.
|
||||
# Message.all.with_all_rich_text # Loads all rich text associations.
|
||||
#
|
||||
# ==== Options
|
||||
# #### Options
|
||||
#
|
||||
# * <tt>:encrypted</tt> - Pass true to encrypt the rich text attribute. The encryption will be non-deterministic. See
|
||||
# +ActiveRecord::Encryption::EncryptableRecord.encrypts+. Default: false.
|
||||
# * `:encrypted` - Pass true to encrypt the rich text attribute. The
|
||||
# encryption will be non-deterministic. See
|
||||
# `ActiveRecord::Encryption::EncryptableRecord.encrypts`. Default: false.
|
||||
#
|
||||
# * <tt>:strict_loading</tt> - Pass true to force strict loading. When
|
||||
# omitted, <tt>strict_loading:</tt> will be set to the value of the
|
||||
# <tt>strict_loading_by_default</tt> class attribute (false by default).
|
||||
# * `:strict_loading` - Pass true to force strict loading. When omitted,
|
||||
# `strict_loading:` will be set to the value of the
|
||||
# `strict_loading_by_default` class attribute (false by default).
|
||||
#
|
||||
# Note: Action Text relies on polymorphic associations, which in turn store class names in the database.
|
||||
# When renaming classes that use <tt>has_rich_text</tt>, make sure to also update the class names in the
|
||||
# <tt>action_text_rich_texts.record_type</tt> polymorphic type column of
|
||||
# the corresponding rows.
|
||||
#
|
||||
# Note: Action Text relies on polymorphic associations, which in turn store
|
||||
# class names in the database. When renaming classes that use `has_rich_text`,
|
||||
# make sure to also update the class names in the
|
||||
# `action_text_rich_texts.record_type` polymorphic type column of the
|
||||
# corresponding rows.
|
||||
def has_rich_text(name, encrypted: false, strict_loading: strict_loading_by_default)
|
||||
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
||||
def #{name}
|
||||
|
@ -1,24 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
# = Action Text \Content
|
||||
# # Action Text Content
|
||||
#
|
||||
# The +ActionText::Content+ class wraps an HTML fragment to add support for
|
||||
# The `ActionText::Content` class wraps an HTML fragment to add support for
|
||||
# parsing, rendering and serialization. It can be used to extract links and
|
||||
# attachments, convert the fragment to plain text, or serialize the fragment
|
||||
# to the database.
|
||||
# attachments, convert the fragment to plain text, or serialize the fragment to
|
||||
# the database.
|
||||
#
|
||||
# The ActionText::RichText record serializes the `body` attribute as
|
||||
# +ActionText::Content+.
|
||||
# `ActionText::Content`.
|
||||
#
|
||||
# class Message < ActiveRecord::Base
|
||||
# has_rich_text :content
|
||||
# end
|
||||
# class Message < ActiveRecord::Base
|
||||
# has_rich_text :content
|
||||
# end
|
||||
#
|
||||
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
||||
# body = message.content.body # => #<ActionText::Content "<div class=\"trix-conte...">
|
||||
# body.to_s # => "<h1>Funny times!</h1>"
|
||||
# body.to_plain_text # => "Funny times!"
|
||||
# message = Message.create!(content: "<h1>Funny times!</h1>")
|
||||
# body = message.content.body # => #<ActionText::Content "<div class=\"trix-conte...">
|
||||
# body.to_s # => "<h1>Funny times!</h1>"
|
||||
# body.to_plain_text # => "Funny times!"
|
||||
class Content
|
||||
include Rendering, Serialization
|
||||
|
||||
@ -47,19 +49,19 @@ def initialize(content = nil, options = {})
|
||||
|
||||
# Extracts links from the HTML fragment:
|
||||
#
|
||||
# html = '<a href="http://example.com/">Example</a>'
|
||||
# content = ActionText::Content.new(html)
|
||||
# content.links # => ["http://example.com/"]
|
||||
# html = '<a href="http://example.com/">Example</a>'
|
||||
# content = ActionText::Content.new(html)
|
||||
# content.links # => ["http://example.com/"]
|
||||
def links
|
||||
@links ||= fragment.find_all("a[href]").map { |a| a["href"] }.uniq
|
||||
end
|
||||
|
||||
# Extracts +ActionText::Attachment+s from the HTML fragment:
|
||||
#
|
||||
# attachable = ActiveStorage::Blob.first
|
||||
# html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}" caption="Captioned"></action-text-attachment>)
|
||||
# content = ActionText::Content.new(html)
|
||||
# content.attachments # => [#<ActionText::Attachment attachable=#<ActiveStorage::Blob...
|
||||
# attachable = ActiveStorage::Blob.first
|
||||
# html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}" caption="Captioned"></action-text-attachment>)
|
||||
# content = ActionText::Content.new(html)
|
||||
# content.attachments # => [#<ActionText::Attachment attachable=#<ActiveStorage::Blob...
|
||||
def attachments
|
||||
@attachments ||= attachment_nodes.map do |node|
|
||||
attachment_for_node(node)
|
||||
@ -78,10 +80,10 @@ def gallery_attachments
|
||||
|
||||
# Extracts +ActionText::Attachable+s from the HTML fragment:
|
||||
#
|
||||
# attachable = ActiveStorage::Blob.first
|
||||
# html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}" caption="Captioned"></action-text-attachment>)
|
||||
# content = ActionText::Content.new(html)
|
||||
# content.attachables # => [attachable]
|
||||
# attachable = ActiveStorage::Blob.first
|
||||
# html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}" caption="Captioned"></action-text-attachment>)
|
||||
# content = ActionText::Content.new(html)
|
||||
# content.attachables # => [attachable]
|
||||
def attachables
|
||||
@attachables ||= attachment_nodes.map do |node|
|
||||
ActionText::Attachable.from_node(node)
|
||||
@ -107,19 +109,20 @@ def render_attachment_galleries(&block)
|
||||
self.class.new(content, canonicalize: false)
|
||||
end
|
||||
|
||||
# Returns a plain-text version of the markup contained by the content,
|
||||
# with tags removed but HTML entities encoded.
|
||||
# Returns a plain-text version of the markup contained by the content, with tags
|
||||
# removed but HTML entities encoded.
|
||||
#
|
||||
# content = ActionText::Content.new("<h1>Funny times!</h1>")
|
||||
# content.to_plain_text # => "Funny times!"
|
||||
# content = ActionText::Content.new("<h1>Funny times!</h1>")
|
||||
# content.to_plain_text # => "Funny times!"
|
||||
#
|
||||
# content = ActionText::Content.new("<div onclick='action()'>safe<script>unsafe</script></div>")
|
||||
# content.to_plain_text # => "safeunsafe"
|
||||
# content = ActionText::Content.new("<div onclick='action()'>safe<script>unsafe</script></div>")
|
||||
# content.to_plain_text # => "safeunsafe"
|
||||
#
|
||||
# NOTE: that the returned string is not HTML safe and should not be rendered in browsers.
|
||||
# NOTE: that the returned string is not HTML safe and should not be rendered in
|
||||
# browsers.
|
||||
#
|
||||
# content = ActionText::Content.new("<script>alert()</script>")
|
||||
# content.to_plain_text # => "<script>alert()</script>"
|
||||
# content = ActionText::Content.new("<script>alert()</script>")
|
||||
# content.to_plain_text # => "<script>alert()</script>"
|
||||
def to_plain_text
|
||||
render_attachments(with_full_attributes: false, &:to_plain_text).fragment.to_plain_text
|
||||
end
|
||||
@ -142,11 +145,11 @@ def to_partial_path
|
||||
|
||||
# Safely transforms Content into an HTML String.
|
||||
#
|
||||
# content = ActionText::Content.new(content: "<h1>Funny times!</h1>")
|
||||
# content.to_s # => "<h1>Funny times!</h1>"
|
||||
# content = ActionText::Content.new(content: "<h1>Funny times!</h1>")
|
||||
# content.to_s # => "<h1>Funny times!</h1>"
|
||||
#
|
||||
# content = ActionText::Content.new("<div onclick='action()'>safe<script>unsafe</script></div>")
|
||||
# content.to_s # => "<div>safeunsafe</div>"
|
||||
# content = ActionText::Content.new("<div onclick='action()'>safe<script>unsafe</script></div>")
|
||||
# content.to_s # => "<div>safeunsafe</div>"
|
||||
def to_s
|
||||
to_rendered_html_with_layout
|
||||
end
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
def self.deprecator # :nodoc:
|
||||
@deprecator ||= ActiveSupport::Deprecation.new
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
module Encryption
|
||||
def encrypt
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
require "rails"
|
||||
require "action_controller/railtie"
|
||||
require "active_record/railtie"
|
||||
|
@ -1,62 +1,62 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
# = Action Text \FixtureSet
|
||||
# # Action Text FixtureSet
|
||||
#
|
||||
# Fixtures are a way of organizing data that you want to test against; in
|
||||
# short, sample data.
|
||||
# Fixtures are a way of organizing data that you want to test against; in short,
|
||||
# sample data.
|
||||
#
|
||||
# To learn more about fixtures, read the ActiveRecord::FixtureSet documentation.
|
||||
#
|
||||
# === YAML
|
||||
# ### YAML
|
||||
#
|
||||
# Like other Active Record-backed models, ActionText::RichText records inherit
|
||||
# from ActiveRecord::Base instances and can therefore be populated by
|
||||
# fixtures.
|
||||
# from ActiveRecord::Base instances and can therefore be populated by fixtures.
|
||||
#
|
||||
# Consider an <tt>Article</tt> class:
|
||||
# Consider an `Article` class:
|
||||
#
|
||||
# class Article < ApplicationRecord
|
||||
# has_rich_text :content
|
||||
# end
|
||||
# class Article < ApplicationRecord
|
||||
# has_rich_text :content
|
||||
# end
|
||||
#
|
||||
# To declare fixture data for the related <tt>content</tt>, first declare fixture
|
||||
# data for <tt>Article</tt> instances in <tt>test/fixtures/articles.yml</tt>:
|
||||
# To declare fixture data for the related `content`, first declare fixture data
|
||||
# for `Article` instances in `test/fixtures/articles.yml`:
|
||||
#
|
||||
# first:
|
||||
# title: An Article
|
||||
# first:
|
||||
# title: An Article
|
||||
#
|
||||
# Then declare the ActionText::RichText fixture data in
|
||||
# <tt>test/fixtures/action_text/rich_texts.yml</tt>, making sure to declare
|
||||
# each entry's <tt>record:</tt> key as a polymorphic relationship:
|
||||
# `test/fixtures/action_text/rich_texts.yml`, making sure to declare each
|
||||
# entry's `record:` key as a polymorphic relationship:
|
||||
#
|
||||
# first:
|
||||
# record: first (Article)
|
||||
# name: content
|
||||
# body: <div>Hello, world.</div>
|
||||
# first:
|
||||
# record: first (Article)
|
||||
# name: content
|
||||
# body: <div>Hello, world.</div>
|
||||
#
|
||||
# When processed, Active Record will insert database records for each fixture
|
||||
# entry and will ensure the Action Text relationship is intact.
|
||||
class FixtureSet
|
||||
# Fixtures support Action Text attachments as part of their <tt>body</tt>
|
||||
# HTML.
|
||||
# Fixtures support Action Text attachments as part of their `body` HTML.
|
||||
#
|
||||
# === Examples
|
||||
# ### Examples
|
||||
#
|
||||
# For example, consider a second <tt>Article</tt> fixture declared in
|
||||
# <tt>test/fixtures/articles.yml</tt>:
|
||||
# For example, consider a second `Article` fixture declared in
|
||||
# `test/fixtures/articles.yml`:
|
||||
#
|
||||
# second:
|
||||
# title: Another Article
|
||||
# second:
|
||||
# title: Another Article
|
||||
#
|
||||
# You can attach a mention of <tt>articles(:first)</tt> to <tt>second</tt>'s
|
||||
# <tt>content</tt> by embedding a call to <tt>ActionText::FixtureSet.attachment</tt>
|
||||
# in the <tt>body:</tt> value in <tt>test/fixtures/action_text/rich_texts.yml</tt>:
|
||||
# You can attach a mention of `articles(:first)` to `second`'s `content` by
|
||||
# embedding a call to `ActionText::FixtureSet.attachment` in the `body:` value
|
||||
# in `test/fixtures/action_text/rich_texts.yml`:
|
||||
#
|
||||
# second:
|
||||
# record: second (Article)
|
||||
# name: content
|
||||
# body: <div>Hello, <%= ActionText::FixtureSet.attachment("articles", :first) %></div>
|
||||
# second:
|
||||
# record: second (Article)
|
||||
# name: content
|
||||
# body: <div>Hello, <%= ActionText::FixtureSet.attachment("articles", :first) %></div>
|
||||
#
|
||||
def self.attachment(fixture_set_name, label, column_type: :integer)
|
||||
signed_global_id = ActiveRecord::FixtureSet.signed_global_id fixture_set_name, label,
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
class Fragment
|
||||
class << self
|
||||
|
@ -1,7 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
# Returns the currently loaded version of Action Text as a +Gem::Version+.
|
||||
# Returns the currently loaded version of Action Text as a `Gem::Version`.
|
||||
def self.gem_version
|
||||
Gem::Version.new VERSION::STRING
|
||||
end
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
module HtmlConversion
|
||||
extend self
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
module PlainTextConversion
|
||||
extend self
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
require "active_support/concern"
|
||||
require "active_support/core_ext/module/attribute_accessors_per_thread"
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
module Serialization
|
||||
extend ActiveSupport::Concern
|
||||
|
@ -1,34 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
module SystemTestHelper
|
||||
# Locates a Trix editor and fills it in with the given HTML.
|
||||
#
|
||||
# The editor can be found by:
|
||||
# * its +id+
|
||||
# * its +placeholder+
|
||||
# * the text from its +label+ element
|
||||
# * its +aria-label+
|
||||
# * the +name+ of its input
|
||||
# * its `id`
|
||||
# * its `placeholder`
|
||||
# * the text from its `label` element
|
||||
# * its `aria-label`
|
||||
# * the `name` of its input
|
||||
#
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# # <trix-editor id="message_content" ...></trix-editor>
|
||||
# fill_in_rich_text_area "message_content", with: "Hello <em>world!</em>"
|
||||
# # <trix-editor id="message_content" ...></trix-editor>
|
||||
# fill_in_rich_text_area "message_content", with: "Hello <em>world!</em>"
|
||||
#
|
||||
# # <trix-editor placeholder="Your message here" ...></trix-editor>
|
||||
# fill_in_rich_text_area "Your message here", with: "Hello <em>world!</em>"
|
||||
# # <trix-editor placeholder="Your message here" ...></trix-editor>
|
||||
# fill_in_rich_text_area "Your message here", with: "Hello <em>world!</em>"
|
||||
#
|
||||
# # <label for="message_content">Message content</label>
|
||||
# # <trix-editor id="message_content" ...></trix-editor>
|
||||
# fill_in_rich_text_area "Message content", with: "Hello <em>world!</em>"
|
||||
# # <label for="message_content">Message content</label>
|
||||
# # <trix-editor id="message_content" ...></trix-editor>
|
||||
# fill_in_rich_text_area "Message content", with: "Hello <em>world!</em>"
|
||||
#
|
||||
# # <trix-editor aria-label="Message content" ...></trix-editor>
|
||||
# fill_in_rich_text_area "Message content", with: "Hello <em>world!</em>"
|
||||
# # <trix-editor aria-label="Message content" ...></trix-editor>
|
||||
# fill_in_rich_text_area "Message content", with: "Hello <em>world!</em>"
|
||||
#
|
||||
# # <input id="trix_input_1" name="message[content]" type="hidden">
|
||||
# # <trix-editor input="trix_input_1"></trix-editor>
|
||||
# fill_in_rich_text_area "message[content]", with: "Hello <em>world!</em>"
|
||||
# # <input id="trix_input_1" name="message[content]" type="hidden">
|
||||
# # <trix-editor input="trix_input_1"></trix-editor>
|
||||
# fill_in_rich_text_area "message[content]", with: "Hello <em>world!</em>"
|
||||
def fill_in_rich_text_area(locator = nil, with:)
|
||||
find(:rich_text_area, locator).execute_script("this.editor.loadHTML(arguments[0])", with.to_s)
|
||||
end
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module ActionText
|
||||
class TrixAttachment
|
||||
TAG_NAME = "figure"
|
||||
|
@ -1,9 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
require_relative "gem_version"
|
||||
|
||||
module ActionText
|
||||
# Returns the currently loaded version of Action Text as a +Gem::Version+.
|
||||
# Returns the currently loaded version of Action Text as a `Gem::Version`.
|
||||
def self.version
|
||||
gem_version
|
||||
end
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
require "pathname"
|
||||
require "json"
|
||||
|
||||
@ -73,8 +75,8 @@ def using_js_runtime?
|
||||
end
|
||||
|
||||
def using_bun?
|
||||
# Cannot assume yarn.lock has been generated yet so we look for
|
||||
# a file known to be generated by the jsbundling-rails gem
|
||||
# Cannot assume yarn.lock has been generated yet so we look for a file known to
|
||||
# be generated by the jsbundling-rails gem
|
||||
@using_bun ||= using_js_runtime? && Pathname(destination_root).join("bun.config.js").exist?
|
||||
end
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# :markup: markdown
|
||||
|
||||
module TestUnit
|
||||
module Generators
|
||||
class InstallGenerator < ::Rails::Generators::Base
|
||||
|
Loading…
Reference in New Issue
Block a user