{:earmark, "~> 1.0.1"}
mix do deps.get, compile
command to make sure the library connection is correct. If everything went smoothly, we can proceed to work on templates and views.Earmark.to_html(data)
for the entire project, because we don’t want to search for and rewrite this code if we decide to use other features or Earmark settings. To do this, we will write a function in the web/views/post_view.ex
, which will become a single point if you need to change something. Open this file and add the following function: def markdown(body) do body |> Earmark.to_html |> raw end
body
, which we pass to the to_html function from the Earmark library. Since we expect to get HTML output suitable for posting, we use the raw
function. I know that you thought: “Oh, the raw function seems to be extremely unsafe!” . The answer will be - "Yes, everything is so . " The problem is that we need to get raw html
on the output in order to be able to render it. At the same time, I don’t want to cut out some potentially dangerous tags every time we display a post. So let's open the web/models/post.ex
and add some code to solve the problem: def changeset(struct, params \\ %{}) do struct |> cast(params, [:title, :body]) |> validate_required([:title, :body]) |> strip_unsafe_body(params) end defp strip_unsafe_body(model, %{"body" => nil}) do model end defp strip_unsafe_body(model, %{"body" => body}) do {:safe, clean_body} = Phoenix.HTML.html_escape(body) model |> put_change(:body, clean_body) end defp strip_unsafe_body(model, _) do model end
changeset
function. Added new call to strip_unsafe_body(model, …)
. This call, using pattern matching, will select one of three functions. The first one includes the body
parameter, equal to nil
. The second is the body
parameter, which we need to clear. And the third - for the control catch of the remaining calls.put_change
function to replace the body
text cleared using the html_escape
function from Phoenix.HTML
version. This function accepts text and returns the {:safe, cleaned_up_body}
if successful. defmodule Pxblog.PostTest do use Pxblog.ModelCase alias Pxblog.Post @valid_attrs %{body: "some content", title: "some content"} @invalid_attrs %{} test "changeset with valid attributes" do changeset = Post.changeset(%Post{}, @valid_attrs) assert changeset.valid? end test "changeset with invalid attributes" do changeset = Post.changeset(%Post{}, @invalid_attrs) refute changeset.valid? end test "when the body includes a script tag" do changeset = Post.changeset(%Post{}, %{@valid_attrs | body: "Hello <script type='javascript'>alert('foo');</script>"}) refute String.match? get_change(changeset, :body), ~r{<script>} end test "when the body includes an iframe tag" do changeset = Post.changeset(%Post{}, %{@valid_attrs | body: "Hello <iframe src='http://google.com'></iframe>"}) refute String.match? get_change(changeset, :body), ~r{<iframe>} end test "body includes no stripped tags" do changeset = Post.changeset(%Post{}, @valid_attrs) assert get_change(changeset, :body) == @valid_attrs[:body] end end
@valid_attrs
. We receive data from the controller in the form of a dictionary with string keys (not atoms). If we change nothing, then the tests will fall.get_change/2
from Ecto.Changeset
to retrieve the modified value from the revision. Finally, we write several tests:script
tag.iframe
tag.test/view/post_view_test.exs
and fill it with the following content: defmodule Pxblog.PostViewTest do use Pxblog.ConnCase, async: true test "converts markdown to html" do {:safe, result} = Pxblog.PostView.markdown("**bold me**") assert String.contains? result, "<strong>bold me</strong>" end test "leaves text with no markdown alone" do {:safe, result} = Pxblog.PostView.markdown("leave me alone") assert String.contains? result, "leave me alone" end end
web/templates/post/form.html.eex
and add the id
parameter with the value "body-editor"
. Finally, the line should look like this: <%= textarea f, :body, class: "form-control", id: "body-editor" %>
web/templates/post/form.html.eex
): <link rel="stylesheet" href="//cdn.jsdelivr.net/simplemde/latest/simplemde.min.css"> <script src="//cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script> <script>var simplemde = new SimpleMDE();</script>
web/templates/post/show.html.eex
file and replace the line with the post body output with the markdown(body)
function that we defined in the view. <%= markdown(@post.body) %>
add_markdown_support
branch of our repository.Source: https://habr.com/ru/post/317550/
All Articles