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
|