draftjs_html 0.4.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: 9194cbbbe5e94b79dd386efd86e23774128659b74fb40c98ddf43bc9b62fd9a9
|
4
|
+
data.tar.gz: 121e6900da99a52b8a5e0cf5b1cae7206492e979939b214d9ef3a112ebdc471d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e574a85e36eb144f9f5b4e56a766cb3c83385fad293c311627d1ae5974408c8bd5ce39dc6a2081e9d7b9e695ce08dea54ef2050768c1f5660ef478d992e10671
|
7
|
+
data.tar.gz: 0dc0ce686a9d476a93c24e392d02ee7180b4fd556636cce72695562936acb2e5c3dec581c3151e9d8fd3ed32b9aa026d644cb524c2579925479fa8d4f8927e1e
|
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.doc))
|
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.1
|
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
|