add tag alias validations

This commit is contained in:
byte[] 2020-09-06 14:19:21 -04:00
parent 6670b050a1
commit 0d359ee81e
4 changed files with 129 additions and 58 deletions

View file

@ -180,11 +180,26 @@ defmodule Philomena.Tags do
end end
def alias_tag(%Tag{} = tag, attrs) do def alias_tag(%Tag{} = tag, attrs) do
target_tag = Repo.get_by!(Tag, name: attrs["target_tag"]) target_tag = Repo.get_by(Tag, name: String.downcase(attrs["target_tag"]))
if tag.id == target_tag.id do
tag tag
else |> Repo.preload(:aliased_tag)
|> Tag.alias_changeset(target_tag)
|> Repo.update()
|> case do
{:ok, tag} ->
spawn(fn ->
perform_alias(tag, target_tag)
end)
{:ok, tag}
error ->
error
end
end
defp perform_alias(tag, target_tag) do
filters_hidden = filters_hidden =
where(Filter, [f], fragment("? @> ARRAY[?]::integer[]", f.hidden_tag_ids, ^tag.id)) where(Filter, [f], fragment("? @> ARRAY[?]::integer[]", f.hidden_tag_ids, ^tag.id))
@ -236,7 +251,6 @@ defmodule Philomena.Tags do
reindex_tag_images(target_tag) reindex_tag_images(target_tag)
reindex_tags([tag, target_tag]) reindex_tags([tag, target_tag])
end end
end
def reindex_tag_images(%Tag{} = tag) do def reindex_tag_images(%Tag{} = tag) do
# First recount the tag # First recount the tag

View file

@ -1,12 +1,14 @@
defmodule Philomena.Tags.Tag do defmodule Philomena.Tags.Tag do
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset
import Ecto.Query
alias Philomena.Channels.Channel alias Philomena.Channels.Channel
alias Philomena.DnpEntries.DnpEntry alias Philomena.DnpEntries.DnpEntry
alias Philomena.UserLinks.UserLink alias Philomena.UserLinks.UserLink
alias Philomena.Tags.Tag alias Philomena.Tags.Tag
alias Philomena.Slug alias Philomena.Slug
alias Philomena.Repo
@namespaces [ @namespaces [
"artist", "artist",
@ -111,6 +113,15 @@ defmodule Philomena.Tags.Tag do
|> put_change(:image, nil) |> put_change(:image, nil)
end end
def alias_changeset(tag, target_tag) do
change(tag)
|> put_assoc(:aliased_tag, target_tag)
|> validate_required([:aliased_tag])
|> validate_not_aliased_to_self()
|> validate_alias_not_transitive()
|> validate_incoming_aliases()
end
def unalias_changeset(tag) do def unalias_changeset(tag) do
change(tag, aliased_tag_id: nil) change(tag, aliased_tag_id: nil)
end end
@ -245,4 +256,46 @@ defmodule Philomena.Tags.Tag do
category -> change(changeset, category: category) category -> change(changeset, category: category)
end end
end end
defp validate_not_aliased_to_self(changeset) do
aliased_tag = get_field(changeset, :aliased_tag)
id = get_field(changeset, :id)
case aliased_tag do
%{id: ^id} ->
add_error(changeset, :aliased_tag, "is the same tag as the source")
_tag ->
changeset
end
end
defp validate_alias_not_transitive(changeset) do
case get_field(changeset, :aliased_tag) do
%{aliased_tag_id: tag} when not is_nil(tag) ->
add_error(
changeset,
:aliased_tag,
"is itself aliased and would create a transitive alias"
)
_tag ->
changeset
end
end
defp validate_incoming_aliases(changeset) do
id = get_field(changeset, :id)
count =
Tag
|> where(aliased_tag_id: ^id)
|> Repo.aggregate(:count, :id)
if count > 0 do
add_error(changeset, :tag, "has incoming aliases and cannot be aliased")
else
changeset
end
end
end end

View file

@ -19,13 +19,15 @@ defmodule PhilomenaWeb.Tag.AliasController do
end end
def update(conn, %{"tag" => tag_params}) do def update(conn, %{"tag" => tag_params}) do
spawn(fn -> case Tags.alias_tag(conn.assigns.tag, tag_params) do
Tags.alias_tag(conn.assigns.tag, tag_params) {:ok, tag} ->
end)
conn conn
|> put_flash(:info, "Tag alias queued.") |> put_flash(:info, "Tag alias queued.")
|> redirect(to: Routes.tag_alias_path(conn, :edit, conn.assigns.tag)) |> redirect(to: Routes.tag_alias_path(conn, :edit, tag))
{:error, changeset} ->
render(conn, "edit.html", changeset: changeset)
end
end end
def delete(conn, _params) do def delete(conn, _params) do

View file

@ -10,6 +10,8 @@ h1
.field .field
=> label f, "Alias target:" => label f, "Alias target:"
= text_input f, :target_tag, value: alias_target(@tag), class: "input" = text_input f, :target_tag, value: alias_target(@tag), class: "input"
= error_tag f, :tag
= error_tag f, :aliased_tag
.field .field
=> submit "Alias tag", class: "button" => submit "Alias tag", class: "button"