draftjs_html 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +37 -5
- data/lib/draftjs_html/node.rb +32 -0
- data/lib/draftjs_html/to_html.rb +26 -19
- data/lib/draftjs_html/version.rb +1 -1
- metadata +2 -1
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 947546094b93886a5533fe19720c44671066c08ebe079dd9a628cead629e880e
         | 
| 4 | 
            +
              data.tar.gz: 280b8abcae0942d236f2352ef8f6aac6fb4a34d51323ed9cb15b8667c8e24d41
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: c0b41f31e0c06a0c9dd6ee08ad7c4cbfd534875130503439ff21777a3145551fd15ac01496e96ec144a96c69ab4e137b4213eefe5f14c1b0b920bf6759e1d1a9
         | 
| 7 | 
            +
              data.tar.gz: 991cabc0dd8a3ccb1785a34f9c629be7b5bc0ee0ec83498ba0e494a234eb3175c5089d3b44a533caa271dd115a499e710cf2bddc5d6442d181ebf2b980438f6c
         | 
    
        data/README.md
    CHANGED
    
    | @@ -59,8 +59,8 @@ raw_draftjs = { | |
| 59 59 |  | 
| 60 60 | 
             
            DraftjsHtml.to_html(raw_draftjs, options: {
         | 
| 61 61 | 
             
              entity_style_mappings:  {
         | 
| 62 | 
            -
                abc: ->(entity, content) {
         | 
| 63 | 
            -
                   | 
| 62 | 
            +
                abc: ->(entity, content, *) {
         | 
| 63 | 
            +
                  DraftjsHtml::Node.new('a', { href: "https://example.com/?id=#{entity.data['user_id']}" }, content)
         | 
| 64 64 | 
             
                },
         | 
| 65 65 | 
             
              },
         | 
| 66 66 | 
             
            }) # => <p>Hello <a href="https://example.com/?id=123">@Arya</a></p>
         | 
| @@ -81,8 +81,9 @@ Defaults to `UTF-8`. | |
| 81 81 |  | 
| 82 82 | 
             
            Allows the author to specify special mapping functions for entities.
         | 
| 83 83 | 
             
            By default, we render `LINK` and `IMAGE` entities using the standard `<a>` and `<img>` tags, respectively.
         | 
| 84 | 
            -
            The author may supply a  | 
| 85 | 
            -
             | 
| 84 | 
            +
            The author may supply a `call`-able object that returns a `DraftjsHtml::Node`-able (or similar).
         | 
| 85 | 
            +
            If returned a String, it's assumed this content is plaintext (or otherwise unsafe) and its content will be coerced to plaintext.
         | 
| 86 | 
            +
            See the section on HTML Injection protection for more details.
         | 
| 86 87 |  | 
| 87 88 | 
             
            #### `:block_type_mapping`
         | 
| 88 89 |  | 
| @@ -128,13 +129,44 @@ However, if you "return" `nil` (or `false-y`) from the proc, it will fallback to | |
| 128 129 | 
             
            DraftjsHtml.to_html(raw_draftjs, options: {
         | 
| 129 130 | 
             
              inline_style_renderer: ->(style_names, content) {
         | 
| 130 131 | 
             
                next if style_names != ['CUSTOM']
         | 
| 131 | 
            -
                 | 
| 132 | 
            +
                Nokogiri::XML::Node.new('pre', document).tap do |node|
         | 
| 133 | 
            +
                  node.content = content
         | 
| 134 | 
            +
                end
         | 
| 132 135 | 
             
              },
         | 
| 133 136 | 
             
            })
         | 
| 134 137 |  | 
| 135 138 | 
             
            # This would use the default inline style rendering UNLESS the *only* applied style for this range was "CUSTOM"
         | 
| 136 139 | 
             
            ```
         | 
| 137 140 |  | 
| 141 | 
            +
            #### HTML Injection protection
         | 
| 142 | 
            +
             | 
| 143 | 
            +
            Working with user-generated content can be a dangerous thing.
         | 
| 144 | 
            +
            While it allows for a lot of flexibility, it also creates some potential attack vectors that you need to be aware of.
         | 
| 145 | 
            +
            We try to take a "safe by default" stance with this library, and not generate HTML that could be dangerous when we know better.
         | 
| 146 | 
            +
             | 
| 147 | 
            +
            To facilitate this, we require a little work from you, dear programmer.
         | 
| 148 | 
            +
            Namely, when specifying special algorithms for generating entities or inline styles, you need to help us keep you safe.
         | 
| 149 | 
            +
            You can do this by returning a `Nokogiri::XML::Node` or `DraftjsHtml::Node` from any functions you provide that generate HTML.
         | 
| 150 | 
            +
            This is similar to Ruby on Rails' `#to_html` method, but rather than a monkeypatch, we chose to provide a "marker class" (classes) that we know are safe.
         | 
| 151 | 
            +
            These classes will handle escaping, encoding, and otherwise "safe generation" for you.
         | 
| 152 | 
            +
            If you, on the other hand, return a bare `String` from one of the custom render functions, we assume it's _unsafe_ and encode it.
         | 
| 153 | 
            +
             | 
| 154 | 
            +
            That is, a function like this:
         | 
| 155 | 
            +
            ```ruby
         | 
| 156 | 
            +
            ->(entity, content, document) do
         | 
| 157 | 
            +
              "<p>hi!</p>"
         | 
| 158 | 
            +
            end
         | 
| 159 | 
            +
            # will become an HTML-entity escaped string (e.g. "<p>hi!</p>")
         | 
| 160 | 
            +
            ```
         | 
| 161 | 
            +
             | 
| 162 | 
            +
            Where, a function like this:
         | 
| 163 | 
            +
            ```ruby
         | 
| 164 | 
            +
            ->(entity, content, document) do
         | 
| 165 | 
            +
              DraftjsHtml::Node.new('p', {}, 'hi!')
         | 
| 166 | 
            +
            end
         | 
| 167 | 
            +
            # will nest HTML nodes as you probably want (e.g. "<p>hi!</p>")
         | 
| 168 | 
            +
            ```
         | 
| 169 | 
            +
             | 
| 138 170 | 
             
            ## Development
         | 
| 139 171 |  | 
| 140 172 | 
             
            After checking out the repo, run `bin/setup` to install dependencies. Then, run
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            module DraftjsHtml
         | 
| 2 | 
            +
              NokogiriNode = Struct.new(:node) do
         | 
| 3 | 
            +
                def to_nokogiri(_document)
         | 
| 4 | 
            +
                  node
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              StringNode = Struct.new(:raw) do
         | 
| 9 | 
            +
                def to_nokogiri(document)
         | 
| 10 | 
            +
                  raw
         | 
| 11 | 
            +
                  Nokogiri::XML::Text.new(raw, document)
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              Node = Struct.new(:element_name, :attributes, :content) do
         | 
| 16 | 
            +
                def self.of(thing)
         | 
| 17 | 
            +
                  case thing
         | 
| 18 | 
            +
                  when Nokogiri::XML::Node then NokogiriNode.new(thing)
         | 
| 19 | 
            +
                  when self.class then thing
         | 
| 20 | 
            +
                  when String then StringNode.new(thing)
         | 
| 21 | 
            +
                  else thing
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def to_nokogiri(document)
         | 
| 26 | 
            +
                  Nokogiri::XML::Node.new(element_name, document).tap do |node|
         | 
| 27 | 
            +
                    node.content = content
         | 
| 28 | 
            +
                    (attributes || {}).each { |k, v| node[k] = v }
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
    
        data/lib/draftjs_html/to_html.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            require_relative 'node'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module DraftjsHtml
         | 
| 2 4 | 
             
              class ToHtml
         | 
| 3 5 | 
             
                BLOCK_TYPE_TO_HTML = {
         | 
| @@ -27,28 +29,25 @@ module DraftjsHtml | |
| 27 29 | 
             
                  'UNDERLINE' => 'u',
         | 
| 28 30 | 
             
                }.freeze
         | 
| 29 31 |  | 
| 30 | 
            -
                DEFAULT_ENTITY_STYLE_FN = ->(_entity, chars) { chars }
         | 
| 32 | 
            +
                DEFAULT_ENTITY_STYLE_FN = ->(_entity, chars, _doc) { chars }
         | 
| 31 33 | 
             
                ENTITY_ATTRIBUTE_NAME_MAP = {
         | 
| 32 34 | 
             
                  'className' => 'class',
         | 
| 33 35 | 
             
                  'url' => 'href',
         | 
| 34 36 | 
             
                }.freeze
         | 
| 35 37 | 
             
                ENTITY_CONVERSION_MAP = {
         | 
| 36 | 
            -
                  'LINK' => ->(entity, content) {
         | 
| 37 | 
            -
                     | 
| 38 | 
            -
             | 
| 39 | 
            -
                    entity.data.slice('url', 'rel', 'target', 'title', 'className').each do |attr, value|
         | 
| 40 | 
            -
                      node[ENTITY_ATTRIBUTE_NAME_MAP.fetch(attr, attr)] = value
         | 
| 38 | 
            +
                  'LINK' => ->(entity, content, *) {
         | 
| 39 | 
            +
                    attributes = entity.data.slice('url', 'rel', 'target', 'title', 'className').each_with_object({}) do |(attr, value), h|
         | 
| 40 | 
            +
                      h[ENTITY_ATTRIBUTE_NAME_MAP.fetch(attr, attr)] = value
         | 
| 41 41 | 
             
                    end
         | 
| 42 42 |  | 
| 43 | 
            -
                     | 
| 43 | 
            +
                    DraftjsHtml::Node.new('a', attributes, content)
         | 
| 44 44 | 
             
                  },
         | 
| 45 | 
            -
                  'IMAGE' => ->(entity,  | 
| 46 | 
            -
                     | 
| 47 | 
            -
             | 
| 48 | 
            -
                      node[ENTITY_ATTRIBUTE_NAME_MAP.fetch(attr, attr)] = value
         | 
| 45 | 
            +
                  'IMAGE' => ->(entity, *) {
         | 
| 46 | 
            +
                    attributes = entity.data.slice('src', 'alt', 'className', 'width', 'height').each_with_object({}) do |(attr, value), h|
         | 
| 47 | 
            +
                      h[ENTITY_ATTRIBUTE_NAME_MAP.fetch(attr, attr)] = value
         | 
| 49 48 | 
             
                    end
         | 
| 50 49 |  | 
| 51 | 
            -
                     | 
| 50 | 
            +
                    DraftjsHtml::Node.new('img', attributes)
         | 
| 52 51 | 
             
                  }
         | 
| 53 52 | 
             
                }.freeze
         | 
| 54 53 |  | 
| @@ -94,18 +93,22 @@ module DraftjsHtml | |
| 94 93 | 
             
                  end
         | 
| 95 94 | 
             
                end
         | 
| 96 95 |  | 
| 97 | 
            -
                def apply_styles_to(html, style_names,  | 
| 98 | 
            -
                  return html | 
| 96 | 
            +
                def apply_styles_to(html, style_names, child)
         | 
| 97 | 
            +
                  return append_child(html, child) if style_names.empty?
         | 
| 99 98 |  | 
| 100 | 
            -
                  custom_render_content = @options[:inline_style_renderer].call(style_names,  | 
| 101 | 
            -
                  return html | 
| 99 | 
            +
                  custom_render_content = @options[:inline_style_renderer].call(style_names, child, @document.parent)
         | 
| 100 | 
            +
                  return append_child(html, custom_render_content) if custom_render_content
         | 
| 102 101 |  | 
| 103 102 | 
             
                  style, *rest = style_names
         | 
| 104 | 
            -
                  html.public_send(style_element_for(style)) do
         | 
| 105 | 
            -
                    apply_styles_to( | 
| 103 | 
            +
                  html.public_send(style_element_for(style)) do |builder|
         | 
| 104 | 
            +
                    apply_styles_to(builder, rest, child)
         | 
| 106 105 | 
             
                  end
         | 
| 107 106 | 
             
                end
         | 
| 108 107 |  | 
| 108 | 
            +
                def append_child(nokogiri, child)
         | 
| 109 | 
            +
                  nokogiri.parent.add_child(DraftjsHtml::Node.of(child).to_nokogiri(@document.parent))
         | 
| 110 | 
            +
                end
         | 
| 111 | 
            +
             | 
| 109 112 | 
             
                def block_element_for(block)
         | 
| 110 113 | 
             
                  return 'br' if block.blank?
         | 
| 111 114 |  | 
| @@ -119,7 +122,11 @@ module DraftjsHtml | |
| 119 122 | 
             
                def try_apply_entity_to(draftjs, char_range)
         | 
| 120 123 | 
             
                  entity = draftjs.find_entity(char_range.entity_key)
         | 
| 121 124 | 
             
                  content = char_range.text
         | 
| 122 | 
            -
                   | 
| 125 | 
            +
                  if entity
         | 
| 126 | 
            +
                    style_fn = (@options[:entity_style_mappings][entity.type] || DEFAULT_ENTITY_STYLE_FN)
         | 
| 127 | 
            +
                    content = style_fn.call(entity, content, @document.parent)
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
             | 
| 123 130 | 
             
                  content
         | 
| 124 131 | 
             
                end
         | 
| 125 132 |  | 
    
        data/lib/draftjs_html/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: draftjs_html
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.5.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - TJ Taylor
         | 
| @@ -65,6 +65,7 @@ files: | |
| 65 65 | 
             
            - lib/draftjs_html/draftjs/entity.rb
         | 
| 66 66 | 
             
            - lib/draftjs_html/draftjs/entity_map.rb
         | 
| 67 67 | 
             
            - lib/draftjs_html/draftjs/to_raw.rb
         | 
| 68 | 
            +
            - lib/draftjs_html/node.rb
         | 
| 68 69 | 
             
            - lib/draftjs_html/to_html.rb
         | 
| 69 70 | 
             
            - lib/draftjs_html/version.rb
         | 
| 70 71 | 
             
            homepage: https://github.com/dugancathal/draftjs_html
         |