philomena/lib/philomena_web/textile_renderer.ex
2020-05-07 22:19:08 -04:00

127 lines
3 KiB
Elixir

defmodule PhilomenaWeb.TextileRenderer do
alias Textile.Parser
alias Philomena.Images.Image
alias Philomena.Repo
import Phoenix.HTML
import Phoenix.HTML.Link
import Ecto.Query
# Kill bogus compile time dependency on ImageView
@image_view Module.concat(["PhilomenaWeb.ImageView"])
def render_one(post, conn) do
hd(render_collection([post], conn))
end
def render_collection(posts, conn) do
opts = %{image_transform: &Camo.Image.image_url/1}
parsed = Enum.map(posts, &Parser.parse(opts, &1.body))
images =
parsed
|> Enum.flat_map(fn tree ->
tree
|> Enum.flat_map(fn
{:text, text} ->
[text]
_ ->
[]
end)
end)
|> find_images
parsed
|> Enum.map(fn tree ->
tree
|> Enum.map(fn
{:text, text} ->
text
|> replacement_entities()
|> replacement_images(conn, images)
{_k, markup} ->
markup
end)
|> Enum.join()
end)
end
defp replacement_entities(t) do
t
|> String.replace("->", "→")
|> String.replace("--", "—")
|> String.replace("...", "…")
|> String.replace(~r|(\s)-(\s)|, "\\1—\\2")
|> String.replace("(tm)", "&tm;")
|> String.replace("(c)", "©")
|> String.replace("(r)", "®")
|> String.replace("'", "’")
end
defp replacement_images(t, conn, images) do
t
|> String.replace(~r|>>(\d+)([pts])?|, fn match ->
# Stupid, but the method doesn't give us capture group information
match_data = Regex.run(~r|>>(\d+)([pts])?|, match, capture: :all_but_first)
[image_id | rest] = match_data
image = images[String.to_integer(image_id)]
case [image | rest] do
[nil, _] ->
match
[nil] ->
match
[image, "p"] ->
Phoenix.View.render(@image_view, "_image_target.html",
image: image,
size: :medium,
conn: conn
)
|> safe_to_string()
[image, "t"] ->
Phoenix.View.render(@image_view, "_image_target.html",
image: image,
size: :small,
conn: conn
)
|> safe_to_string()
[image, "s"] ->
Phoenix.View.render(@image_view, "_image_target.html",
image: image,
size: :thumb_small,
conn: conn
)
|> safe_to_string()
[image] ->
link(">>#{image.id}", to: "/#{image.id}")
|> safe_to_string()
end
end)
end
defp find_images(text_segments) do
text_segments
|> Enum.flat_map(fn t ->
Regex.scan(~r|>>(\d+)|, t, capture: :all_but_first)
|> Enum.map(fn [first] -> String.to_integer(first) end)
end)
|> load_images()
end
defp load_images([]), do: %{}
defp load_images(ids) do
Image
|> where([i], i.id in ^ids)
|> where([i], i.hidden_from_users == false)
|> preload(:tags)
|> Repo.all()
|> Map.new(&{&1.id, &1})
end
end