Add backlinks

This commit is contained in:
Shadowfacts 2020-08-02 16:12:38 -04:00
parent 4ba50f9fb5
commit 28cd37966a
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
7 changed files with 111 additions and 8 deletions

View File

@ -56,6 +56,19 @@ defmodule Wiki.Content do
%Page{} %Page{}
|> Page.changeset(attrs) |> Page.changeset(attrs)
|> Repo.insert() |> Repo.insert()
|> case do
{:ok, page} ->
content = Map.get(attrs, "content") || Map.get(attrs, :content)
unless is_nil(content) do
Page.update_page_links(%Page{page | content: content})
end
{:ok, page}
{:error, reason} ->
{:error, reason}
end
end end
@doc """ @doc """
@ -74,6 +87,19 @@ defmodule Wiki.Content do
page page
|> Page.changeset(attrs) |> Page.changeset(attrs)
|> Repo.update() |> Repo.update()
|> case do
{:ok, page} ->
content = Map.get(attrs, "content") || Map.get(attrs, :content)
unless is_nil(content) do
Page.update_page_links(%Page{page | content: content})
end
{:ok, page}
{:error, reason} ->
{:error, reason}
end
end end
@doc """ @doc """

View File

@ -1,6 +1,9 @@
defmodule Wiki.Content.Page do defmodule Wiki.Content.Page do
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset
import Ecto.Query
alias Wiki.Repo
alias Wiki.Content.PageLink
@type t() :: %__MODULE__{ @type t() :: %__MODULE__{
encrypted_content: binary(), encrypted_content: binary(),
@ -32,6 +35,14 @@ defmodule Wiki.Content.Page do
belongs_to :user, Wiki.Accounts.User belongs_to :user, Wiki.Accounts.User
has_many :uploads, Wiki.Content.Upload has_many :uploads, Wiki.Content.Upload
# Pages that this page links to
has_many :_pages_linked_to, PageLink, foreign_key: :from_id
has_many :pages_linked_to, through: [:_pages_linked_to, :to]
# Pages that link to this page
has_many :_pages_linked_from, PageLink, foreign_key: :to_id
has_many :pages_linked_from, through: [:_pages_linked_from, :from]
timestamps() timestamps()
end end
@ -65,7 +76,7 @@ defmodule Wiki.Content.Page do
{encrypted_content, tag, iv} = do_encrypt(content, key) {encrypted_content, tag, iv} = do_encrypt(content, key)
user_id = get_field(changeset, :user_id) user_id = get_field(changeset, :user_id)
{html, _linked_pages} = Wiki.Content.Renderer.render(content, user_id) html = Wiki.Content.Renderer.render(content, user_id)
{encrypted_html, html_tag, html_iv} = do_encrypt(html, key) {encrypted_html, html_tag, html_iv} = do_encrypt(html, key)
changeset changeset
@ -122,4 +133,22 @@ defmodule Wiki.Content.Page do
false false
) )
end end
@spec update_page_links(page :: __MODULE__.t()) :: :ok
def update_page_links(%__MODULE__{content: content, id: id} = page) when is_binary(content) do
linked_page_ids = Wiki.Content.Renderer.get_linked_pages(page)
if length(linked_page_ids) == 0 do
Repo.delete_all(from link in PageLink, where: link.from_id == ^id)
else
Repo.delete_all(
from link in PageLink, where: link.from_id == ^id and not (link.to_id in ^linked_page_ids)
)
values = Enum.map(linked_page_ids, fn to_id -> [from_id: id, to_id: to_id] end)
Repo.insert_all(PageLink, values, on_conflict: :nothing)
end
:ok
end
end end

View File

@ -0,0 +1,16 @@
defmodule Wiki.Content.PageLink do
use Ecto.Schema
import Ecto.Changeset
schema "page_links" do
belongs_to :from, Wiki.Content.Page
belongs_to :to, Wiki.Content.Page
end
@doc false
def changeset(page_link, attrs) do
page_link
|> cast(attrs, [:from_id, :to_id])
|> validate_required([:from_id, :to_id])
end
end

View File

@ -5,14 +5,18 @@ defmodule Wiki.Content.Renderer do
alias WikiWeb.Endpoint alias WikiWeb.Endpoint
import Ecto.Query import Ecto.Query
@spec render(String.t(), integer()) :: {String.t(), [integer()]} @spec render(String.t(), integer()) :: String.t()
def render(content, user_id) do def render(content, user_id) do
{content, linked_pages} = replace_page_links(content, user_id) {content, _linked_pages} = replace_page_links(content, user_id)
{ content
render_markdown(content), |> render_markdown()
Enum.uniq(linked_pages) end
}
@spec get_linked_pages(page :: Page.t()) :: [integer()]
def get_linked_pages(%Page{content: content, user_id: user_id}) do
{_content, linked_pages} = replace_page_links(content, user_id)
Enum.uniq(linked_pages)
end end
@page_link_regex ~r/\[\[.*?\]\]/ @page_link_regex ~r/\[\[.*?\]\]/

View File

@ -85,7 +85,7 @@ defmodule WikiWeb.PageController do
end end
def show(conn, _params) do def show(conn, _params) do
page = Repo.preload(conn.assigns.page, :uploads) page = Repo.preload(conn.assigns.page, [:uploads, :pages_linked_from])
render(conn, "show.html", page: page) render(conn, "show.html", page: page)
end end

View File

@ -20,3 +20,14 @@
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
<h2>Backlinks</h2>
<ul>
<%= for page <- @page.pages_linked_from do %>
<li>
<a href="<%= Routes.page_path(@conn, :show, page.id) %>">
<%= page.title %>
</a>
</li>
<% end %>
</ul>

View File

@ -0,0 +1,17 @@
defmodule Wiki.Repo.Migrations.CreatePageLinks do
use Ecto.Migration
def change do
create table(:page_links) do
add :from_id, references(:pages, on_delete: :nothing), primary_key: true
add :to_id, references(:pages, on_delete: :nothing), primary_key: true
end
create index(:page_links, [:from_id])
create index(:page_links, [:to_id])
create unique_index(:page_links, [:from_id, :to_id],
name: :page_link_from_id_to_id_unique_index
)
end
end