From 28cd37966a12747114c0c9395b78c8a2972c4330 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 2 Aug 2020 16:12:38 -0400 Subject: [PATCH] Add backlinks --- lib/wiki/content.ex | 26 ++++++++++++++++ lib/wiki/content/page.ex | 31 ++++++++++++++++++- lib/wiki/content/page_link.ex | 16 ++++++++++ lib/wiki/content/renderer.ex | 16 ++++++---- lib/wiki_web/controllers/page_controller.ex | 2 +- lib/wiki_web/templates/page/show.html.eex | 11 +++++++ .../20200801233918_create_page_links.exs | 17 ++++++++++ 7 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 lib/wiki/content/page_link.ex create mode 100644 priv/repo/migrations/20200801233918_create_page_links.exs diff --git a/lib/wiki/content.ex b/lib/wiki/content.ex index 4c6ea94..c4189bc 100644 --- a/lib/wiki/content.ex +++ b/lib/wiki/content.ex @@ -56,6 +56,19 @@ defmodule Wiki.Content do %Page{} |> Page.changeset(attrs) |> 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 @doc """ @@ -74,6 +87,19 @@ defmodule Wiki.Content do page |> Page.changeset(attrs) |> 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 @doc """ diff --git a/lib/wiki/content/page.ex b/lib/wiki/content/page.ex index df45b2e..06c29bd 100644 --- a/lib/wiki/content/page.ex +++ b/lib/wiki/content/page.ex @@ -1,6 +1,9 @@ defmodule Wiki.Content.Page do use Ecto.Schema import Ecto.Changeset + import Ecto.Query + alias Wiki.Repo + alias Wiki.Content.PageLink @type t() :: %__MODULE__{ encrypted_content: binary(), @@ -32,6 +35,14 @@ defmodule Wiki.Content.Page do belongs_to :user, Wiki.Accounts.User 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() end @@ -65,7 +76,7 @@ defmodule Wiki.Content.Page do {encrypted_content, tag, iv} = do_encrypt(content, key) 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) changeset @@ -122,4 +133,22 @@ defmodule Wiki.Content.Page do false ) 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 diff --git a/lib/wiki/content/page_link.ex b/lib/wiki/content/page_link.ex new file mode 100644 index 0000000..ab29c2c --- /dev/null +++ b/lib/wiki/content/page_link.ex @@ -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 diff --git a/lib/wiki/content/renderer.ex b/lib/wiki/content/renderer.ex index 5c4a689..0726fdc 100644 --- a/lib/wiki/content/renderer.ex +++ b/lib/wiki/content/renderer.ex @@ -5,14 +5,18 @@ defmodule Wiki.Content.Renderer do alias WikiWeb.Endpoint import Ecto.Query - @spec render(String.t(), integer()) :: {String.t(), [integer()]} + @spec render(String.t(), integer()) :: String.t() def render(content, user_id) do - {content, linked_pages} = replace_page_links(content, user_id) + {content, _linked_pages} = replace_page_links(content, user_id) - { - render_markdown(content), - Enum.uniq(linked_pages) - } + content + |> render_markdown() + 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 @page_link_regex ~r/\[\[.*?\]\]/ diff --git a/lib/wiki_web/controllers/page_controller.ex b/lib/wiki_web/controllers/page_controller.ex index 1c020bf..a8d7a40 100644 --- a/lib/wiki_web/controllers/page_controller.ex +++ b/lib/wiki_web/controllers/page_controller.ex @@ -85,7 +85,7 @@ defmodule WikiWeb.PageController do end 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) end diff --git a/lib/wiki_web/templates/page/show.html.eex b/lib/wiki_web/templates/page/show.html.eex index 9b381ad..b1f057e 100644 --- a/lib/wiki_web/templates/page/show.html.eex +++ b/lib/wiki_web/templates/page/show.html.eex @@ -20,3 +20,14 @@ <% end %> <% end %> + +

Backlinks

+ diff --git a/priv/repo/migrations/20200801233918_create_page_links.exs b/priv/repo/migrations/20200801233918_create_page_links.exs new file mode 100644 index 0000000..f8257c5 --- /dev/null +++ b/priv/repo/migrations/20200801233918_create_page_links.exs @@ -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