philomena/lib/philomena_web/textile_renderer.ex

140 lines
3.4 KiB
Elixir

defmodule PhilomenaWeb.TextileRenderer do
alias Philomena.Textile.Parser
alias Philomena.Images.Image
alias Philomena.SpoilerExecutor
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, spoilers} =
parsed
|> Enum.flat_map(fn tree ->
tree
|> Enum.flat_map(fn
{:text, text} ->
[text]
_ ->
[]
end)
end)
|> find_images(conn)
parsed
|> Enum.map(fn tree ->
tree
|> Enum.map(fn
{:text, text} ->
text
|> replacement_entities()
|> replacement_images(conn, spoilers, 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, spoilers, 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,
spoilers: spoilers,
conn: conn
)
|> safe_to_string()
[image, "t"] ->
Phoenix.View.render(@image_view, "_image_target.html",
image: image,
size: :small,
spoilers: spoilers,
conn: conn
)
|> safe_to_string()
[image, "s"] ->
Phoenix.View.render(@image_view, "_image_target.html",
image: image,
size: :thumb_small,
spoilers: spoilers,
conn: conn
)
|> safe_to_string()
[image] ->
link(">>#{image.id}", to: "/#{image.id}")
|> safe_to_string()
end
end)
end
defp find_images(text_segments, conn) 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)
|> Enum.filter(&(&1 < 2_147_483_647))
end)
|> load_images(conn)
end
defp load_images([], _conn), do: {%{}, %{}}
defp load_images(ids, conn) do
images =
Image
|> where([i], i.id in ^ids)
|> where([i], i.hidden_from_users == false)
|> preload(:tags)
|> Repo.all()
spoilers =
SpoilerExecutor.execute_spoiler(
conn.assigns.compiled_spoiler,
images
)
{Map.new(images, &{&1.id, &1}), spoilers}
end
end