This commit is contained in:
Shadowfacts 2020-07-29 22:21:25 -04:00
parent 9d3f45997a
commit 8715db806a
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
7 changed files with 90 additions and 15 deletions

View File

@ -2,6 +2,14 @@ defmodule Wiki.Accounts.User do
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset
@type t() :: %__MODULE__{
email: String.t(),
password: String.t() | nil,
hashed_password: String.t(),
confirmed_at: NaiveDateTime.t(),
content_encryption_key_salt: String.t()
}
@derive {Inspect, except: [:password]} @derive {Inspect, except: [:password]}
schema "users" do schema "users" do
field :email, :string field :email, :string

View File

@ -100,7 +100,7 @@ defmodule Wiki.Content do
%Ecto.Changeset{data: %Page{}} %Ecto.Changeset{data: %Page{}}
""" """
def change_page(%Page{} = page, attrs \\ %{}) do def change_page(%Page{} = page, attrs \\ %{}, options \\ []) do
Page.changeset(page, attrs) Page.changeset(page, attrs, options)
end end
end end

View File

@ -2,12 +2,24 @@ defmodule Wiki.Content.Page do
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset
@type t() :: %__MODULE__{
encrypted_content: binary(),
encrypted_content_iv: binary(),
encrypted_content_tag: binary(),
content: String.t() | nil,
content_encryption_key: String.t() | nil,
html: String.t() | nil,
title: String.t(),
user: Wiki.Accounts.User.t()
}
schema "pages" do schema "pages" do
field :encrypted_content, :binary field :encrypted_content, :binary
field :encrypted_content_iv, :binary field :encrypted_content_iv, :binary
field :encrypted_content_tag, :binary field :encrypted_content_tag, :binary
field :content, :string, virtual: true field :content, :string, virtual: true
field :content_encryption_key, :string, virtual: true field :content_encryption_key, :string, virtual: true
field :html, :string, virtual: true
field :title, :string field :title, :string
belongs_to :user, Wiki.Accounts.User belongs_to :user, Wiki.Accounts.User
@ -16,15 +28,21 @@ defmodule Wiki.Content.Page do
end end
@doc false @doc false
def changeset(page, attrs) do def changeset(page, attrs, options \\ []) do
page changeset =
|> cast(attrs, [ page
:title, |> cast(attrs, [
:content, :title,
:content_encryption_key, :content,
:user_id :content_encryption_key,
]) :user_id
|> encrypt_changeset() ])
if Keyword.get(options, :encrypt, true) do
encrypt_changeset(changeset)
else
changeset
end
|> validate_required([ |> validate_required([
:title, :title,
:encrypted_content, :encrypted_content,

View File

@ -0,0 +1,47 @@
defmodule Wiki.Content.Renderer do
alias Wiki.Repo
alias Wiki.Content.Page
alias WikiWeb.Router.Helpers, as: Routes
alias WikiWeb.Endpoint
import Ecto.Query
@spec render(Page.t()) :: String.t()
def render(page) do
page.content
|> replace_page_links(page)
|> render_markdown()
end
@page_link_regex ~r/\[\[.*?\]\]/
@spec replace_page_links(content :: String.t(), root_page :: Page.t()) :: String.t()
defp replace_page_links(content, root_page) do
String.replace(content, @page_link_regex, fn match ->
title = String.slice(match, 2..-3)
lower_title = String.downcase(title)
path =
from(p in Page)
|> where([p], p.user_id == ^root_page.user_id)
|> where([p], fragment("lower(?)", p.title) == ^lower_title)
|> select([p], p.id)
|> Repo.one()
|> case do
nil ->
Routes.page_path(Endpoint, :new, title: title)
linked_page_id ->
Routes.page_path(Endpoint, :show, linked_page_id)
end
# convert link to markdown, because [[title]] -> link transformation happens before markdown parsing
"[#{title}](#{path})"
end)
end
@spec render_markdown(content :: String.t()) :: String.t()
defp render_markdown(content) do
{:ok, html, _errors} = Earmark.as_html(content)
html
end
end

View File

@ -34,8 +34,8 @@ defmodule WikiWeb.PageController do
render(conn, "index.html", pages: pages) render(conn, "index.html", pages: pages)
end end
def new(conn, _params) do def new(conn, params) do
changeset = Content.change_page(%Page{}) changeset = Content.change_page(%Page{}, params, encrypt: false)
render(conn, "new.html", changeset: changeset) render(conn, "new.html", changeset: changeset)
end end
@ -59,7 +59,8 @@ defmodule WikiWeb.PageController do
end end
def show(conn, _params) do def show(conn, _params) do
render(conn, "show.html", page: conn.assigns.page) rendered_content = Content.Renderer.render(conn.assigns.page)
render(conn, "show.html", page: conn.assigns.page, content: rendered_content)
end end
def edit(conn, _params) do def edit(conn, _params) do

View File

@ -9,7 +9,7 @@
<li> <li>
<strong>Content:</strong> <strong>Content:</strong>
<%= @page.content %> <%= raw(@content) %>
</li> </li>
</ul> </ul>

View File

@ -13,5 +13,6 @@ defmodule Wiki.Repo.Migrations.CreatePages do
end end
create index(:pages, [:user_id]) create index(:pages, [:user_id])
create index(:pages, ["(lower(title))"], name: :pages_lowercase_title_index)
end end
end end