Skip to content

Commit c00be59

Browse files
Merge pull request #88 from epochtalk/md-parser
Md parser
2 parents 3a7ef6f + fe7253d commit c00be59

File tree

9 files changed

+101
-68
lines changed

9 files changed

+101
-68
lines changed

lib/epochtalk_server/models/post.ex

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,11 @@ defmodule EpochtalkServer.Models.Post do
176176
create(%{
177177
"thread_id" => post_attrs["thread_id"],
178178
"user_id" => user_id,
179-
"content" => %{title: post_attrs["title"], body: post_attrs["body"]},
179+
"content" => %{
180+
title: post_attrs["title"],
181+
body: post_attrs["body"],
182+
body_html: post_attrs["body_html"]
183+
},
180184
"deleted" => post_attrs["deleted"],
181185
"locked" => post_attrs["locked"]
182186
})
@@ -248,7 +252,7 @@ defmodule EpochtalkServer.Models.Post do
248252
"""
249253
SELECT
250254
p.thread_id, t.board_id, t.slug, b.right_to_left, p.user_id, p.content ->> \'title\' as title,
251-
p.content ->> \'body\' as body, p.metadata, p.deleted, p.locked, p.created_at,
255+
p.content ->> \'body\' as body, p.content ->> \'body_html\' as body_html, p.metadata, p.deleted, p.locked, p.created_at,
252256
p.updated_at, p.imported_at, CASE WHEN EXISTS (
253257
SELECT rp.id
254258
FROM administration.reports_posts rp
@@ -314,6 +318,7 @@ defmodule EpochtalkServer.Models.Post do
314318
user_id: p1.user_id,
315319
title: p1.title,
316320
body: p1.body,
321+
body_html: p1.body_html,
317322
deleted: p1.deleted,
318323
locked: p1.locked,
319324
right_to_left: p1.right_to_left,

lib/epochtalk_server/models/thread.ex

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,11 @@ defmodule EpochtalkServer.Models.Thread do
703703
post_attrs = %{
704704
thread_id: thread_id,
705705
user_id: user.id,
706-
content: %{title: thread_attrs["title"], body: thread_attrs["body"]}
706+
content: %{
707+
title: thread_attrs["title"],
708+
body: thread_attrs["body"],
709+
body_html: thread_attrs["body_html"]
710+
}
707711
}
708712

709713
case Post.create(post_attrs) do

lib/epochtalk_server_web/controllers/post.ex

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ defmodule EpochtalkServerWeb.Controllers.Post do
99
alias EpochtalkServerWeb.Helpers.Validate
1010
alias EpochtalkServerWeb.Helpers.ACL
1111
alias EpochtalkServerWeb.Helpers.Sanitize
12+
alias EpochtalkServerWeb.Helpers.Parse
1213
alias EpochtalkServer.Models.Post
1314
alias EpochtalkServer.Models.Poll
1415
alias EpochtalkServer.Models.Thread
@@ -47,9 +48,9 @@ defmodule EpochtalkServerWeb.Controllers.Post do
4748
post_max_length <-
4849
Application.get_env(:epochtalk_server, :frontend_config)["post_max_length"],
4950
thread_id <- Validate.cast(attrs, "thread_id", :integer, required: true),
50-
title <-
51+
_title <-
5152
Validate.cast(attrs, "title", :string, required: true, max: @max_post_title_length),
52-
body <- Validate.cast(attrs, "body", :string, required: true, max: post_max_length),
53+
_body <- Validate.cast(attrs, "body", :string, required: true, max: post_max_length),
5354
user_priority <- ACL.get_user_priority(conn),
5455
{:bypass_lock, true} <-
5556
{:bypass_lock, can_authed_user_bypass_thread_lock(user, thread_id)},
@@ -63,17 +64,18 @@ defmodule EpochtalkServerWeb.Controllers.Post do
6364
{:board_banned, BoardBan.is_banned_from_board(user, thread_id: thread_id)},
6465
attrs <- AutoModeration.moderate(user, attrs),
6566
attrs <- Mention.username_to_user_id(user, attrs),
66-
attrs <- Sanitize.html_from_title(title, attrs),
67-
attrs <- Sanitize.html_from_body(body, attrs),
67+
attrs <- Sanitize.html_and_entities_from_title(attrs),
68+
attrs <- Sanitize.html_and_entities_from_body(attrs),
69+
attrs <- Parse.markdown_within_body(attrs),
6870
# TODO(akinsey): Implement the following for completion
6971
# Plugins
7072
# 1) Track IP (done)
7173

7274
# Pre Processing
7375

74-
# 1) clean post title (html_sanitize_ex) (done)
75-
# 2) parse/clean post body
76-
# 3) handle uploaded images
76+
# 1) clean post title (done)
77+
# 2) parse/clean post body (wip)
78+
# 3) handle uploaded images (wip)
7779
# 4) handle filtering out newbie images
7880

7981
# Hooks

lib/epochtalk_server_web/controllers/thread.ex

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ defmodule EpochtalkServerWeb.Controllers.Thread do
99
alias EpochtalkServerWeb.Helpers.Validate
1010
alias EpochtalkServerWeb.Helpers.ACL
1111
alias EpochtalkServerWeb.Helpers.Sanitize
12+
alias EpochtalkServerWeb.Helpers.Parse
1213
alias EpochtalkServer.Models.Thread
1314
alias EpochtalkServer.Models.User
1415
alias EpochtalkServer.Models.Board
@@ -68,9 +69,9 @@ defmodule EpochtalkServerWeb.Controllers.Thread do
6869
# pre/parallel hooks
6970
attrs <- AutoModeration.moderate(user, attrs),
7071
attrs <- Mention.username_to_user_id(user, attrs),
71-
attrs <- Sanitize.html_from_title(attrs["title"], attrs),
72-
attrs <- Sanitize.html_from_body(attrs["body"], attrs),
73-
72+
attrs <- Sanitize.html_and_entities_from_title(attrs),
73+
attrs <- Sanitize.html_and_entities_from_body(attrs),
74+
attrs <- Parse.markdown_within_body(attrs),
7475
# thread creation
7576
{:ok, thread_data} <- Thread.create(attrs, user) do
7677
# post hooks
@@ -105,7 +106,7 @@ defmodule EpochtalkServerWeb.Controllers.Thread do
105106

106107
# Pre Processing
107108

108-
# 1) clean post (html_sanitize_ex)
109+
# 1) clean post
109110
# 2) parse post
110111
# 3) handle uploaded images
111112
# 4) handle filtering out newbie images
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
defmodule EpochtalkServerWeb.Helpers.Parse do
2+
@moduledoc """
3+
Helper for parsing `User` input from the front end client
4+
"""
5+
6+
@doc """
7+
Used to parse markdown within `Thread` or `Post` body, assumes
8+
"""
9+
@spec markdown_within_body(attrs :: map) :: map()
10+
def markdown_within_body(attrs) when is_map(attrs),
11+
do: Map.put(attrs, "body_html", Earmark.as_html!(attrs["body_html"] || attrs["body"]))
12+
end

lib/epochtalk_server_web/helpers/sanitize.ex

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,60 @@ defmodule EpochtalkServerWeb.Helpers.Sanitize do
44
"""
55

66
@doc """
7-
Used to sanitize html from `Thread` or `Post` title
7+
Used to sanitize html and entities from `Thread` or `Post` title
88
99
## Example
1010
iex> alias EpochtalkServerWeb.Helpers.Sanitize
11-
iex> attrs = %{"title" => "<strong>Hello World</strong><br /><script></script><a href='google.com'></a>"}
12-
iex> Sanitize.html_from_title(attrs["title"], attrs)
13-
%{"title" => "Hello World"}
11+
iex> attrs = %{"title" => "&nbsp;<strong>Hello World</strong><br /><script></script><a href='google.com'></a>"}
12+
iex> Sanitize.html_and_entities_from_title(attrs)
13+
%{"title" => "&#38;nbsp;&lt;strong&gt;Hello World&lt;/strong&gt;&lt;br /&gt;&lt;script&gt;&lt;/script&gt;&lt;a href='google.com'&gt;&lt;/a&gt;"}
1414
"""
15-
@spec html_from_title(title :: String.t(), attrs :: map) :: map()
16-
def html_from_title(title, attrs) when is_binary(title) and is_map(attrs),
17-
do: Map.put(attrs, "title", HtmlSanitizeEx.strip_tags(title))
15+
@spec html_and_entities_from_title(attrs :: map) :: map()
16+
def html_and_entities_from_title(attrs) when is_map(attrs) do
17+
sanitized_title =
18+
attrs["title"]
19+
|> String.replace(~r/(?:&)/, "&#38;")
20+
|> String.replace(~r/(?:<)/, "&lt;")
21+
|> String.replace(~r/(?:>)/, "&gt;")
22+
23+
Map.put(attrs, "title", sanitized_title)
24+
end
1825

1926
@doc """
20-
Used to sanitize html from `Message` subject
27+
Used to sanitize html and entities from `Message` subject
2128
2229
## Example
2330
iex> alias EpochtalkServerWeb.Helpers.Sanitize
24-
iex> attrs = %{"subject" => "<strong>Hey this is</strong><br /> <script>a</script> <a href='google.com'>message</a>"}
25-
iex> Sanitize.html_from_subject(attrs["subject"], attrs)
26-
%{"subject" => "Hey this is a message"}
31+
iex> attrs = %{"subject" => "&nbsp;<strong>Hello World</strong><br /><script></script><a href='google.com'></a>"}
32+
iex> Sanitize.html_and_entities_from_subject(attrs)
33+
%{"subject" => "&#38;nbsp;&lt;strong&gt;Hello World&lt;/strong&gt;&lt;br /&gt;&lt;script&gt;&lt;/script&gt;&lt;a href='google.com'&gt;&lt;/a&gt;"}
2734
"""
28-
@spec html_from_subject(subject :: String.t(), attrs :: map) :: map()
29-
def html_from_subject(subject, attrs) when is_binary(subject) and is_map(attrs),
30-
do: Map.put(attrs, "subject", HtmlSanitizeEx.strip_tags(subject))
35+
@spec html_and_entities_from_subject(attrs :: map) :: map()
36+
def html_and_entities_from_subject(attrs) when is_map(attrs) do
37+
sanitized_subject =
38+
attrs["subject"]
39+
|> String.replace(~r/(?:&)/, "&#38;")
40+
|> String.replace(~r/(?:<)/, "&lt;")
41+
|> String.replace(~r/(?:>)/, "&gt;")
42+
43+
Map.put(attrs, "subject", sanitized_subject)
44+
end
3145

3246
@doc """
33-
Used to sanitize all html except basic formatting html from `Thread` or `Post` body
47+
Used to sanitize html and entities from `Thread` or `Post` body, store sanitized body in `body_html`
3448
## Example
3549
iex> alias EpochtalkServerWeb.Helpers.Sanitize
36-
iex> attrs = %{"body" => "<i>Hey <b>this</b> is</i><br /> <h1><script>a</script></h1> <a href='google.com'>post</a>"}
37-
iex> Sanitize.html_from_body(attrs["body"], attrs)
38-
%{"body" => "<i>Hey <b>this</b> is</i><br /> <h1>a</h1> <a href=\\"google.com\\">post</a>"}
50+
iex> attrs = %{"body" => "<i>Hey <b>this</b> is</i><br /> <h1><script>a</script></h1> <a href='google.com'>post</a> &nbsp;"}
51+
iex> Sanitize.html_and_entities_from_body(attrs)
52+
%{"body" => "<i>Hey <b>this</b> is</i><br /> <h1><script>a</script></h1> <a href='google.com'>post</a> &nbsp;", "body_html" => "&lt;i>Hey &lt;b>this&lt;/b> is&lt;/i>&lt;br /> &lt;h1>&lt;script>a&lt;/script>&lt;/h1> &lt;a href='google.com'>post&lt;/a> &#38;nbsp;"}
3953
"""
40-
@spec html_from_body(body :: String.t(), attrs :: map) :: map()
41-
def html_from_body(body, attrs) when is_binary(body) and is_map(attrs),
42-
do: Map.put(attrs, "body", HtmlSanitizeEx.basic_html(body))
54+
@spec html_and_entities_from_body(attrs :: map) :: map()
55+
def html_and_entities_from_body(attrs) when is_map(attrs) do
56+
sanitized_body =
57+
attrs["body"]
58+
|> String.replace(~r/(?:&)/, "&#38;")
59+
|> String.replace(~r/(?:<)/, "&lt;")
60+
61+
Map.put(attrs, "body_html", sanitized_body)
62+
end
4363
end

lib/epochtalk_server_web/json/post_json.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,8 @@ defmodule EpochtalkServerWeb.Controllers.PostJSON do
265265

266266
defp format_post_data_for_by_thread(post) do
267267
post
268-
# TODO(akinsey): this is a temp hack to get posts to display
269-
|> Map.put(:body_html, post.body)
268+
# if body_html does not exist, default to post.body
269+
|> Map.put(:body_html, post.body_html || post.body)
270270
|> Map.put(:user, %{
271271
id: post.user_id,
272272
name: post.name,

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ defmodule EpochtalkServer.MixProject do
3737
{:corsica, "~> 1.3.0"},
3838
{:credo, "~> 1.6", only: [:dev, :test], runtime: false},
3939
{:dialyxir, "~> 1.2", only: [:dev], runtime: false},
40+
{:earmark, "~> 1.4"},
4041
{:ecto_sql, "~> 3.6"},
4142
{:ex_doc, "~> 0.29.4"},
4243
{:ex_machina, "~> 2.7.0", only: :test},
@@ -46,7 +47,6 @@ defmodule EpochtalkServer.MixProject do
4647
{:guardian_phoenix, "~> 2.0"},
4748
{:guardian_db, "~> 2.1"},
4849
{:guardian_redis, "~> 0.1"},
49-
{:html_sanitize_ex, "~> 1.4"},
5050
{:iteraptor, git: "https://github.com/epochtalk/elixir-iteraptor.git", tag: "1.13.1"},
5151
{:jason, "~> 1.4.0"},
5252
{:mimic, "~> 1.7.4", only: :test},

0 commit comments

Comments
 (0)