tag description and implication editing

This commit is contained in:
byte[] 2019-12-14 16:18:49 -05:00
parent 2eb39b43aa
commit 4f3390f728
10 changed files with 233 additions and 17 deletions

View file

@ -4,9 +4,13 @@ defmodule Philomena.Tags do
"""
import Ecto.Query, warn: false
alias Ecto.Multi
alias Philomena.Repo
alias Philomena.Tags.Tag
alias Philomena.Tags.Uploader
alias Philomena.Images
alias Philomena.Images.Image
@spec get_or_create_tags(String.t()) :: List.t()
def get_or_create_tags(tag_list) do
@ -96,11 +100,44 @@ defmodule Philomena.Tags do
"""
def update_tag(%Tag{} = tag, attrs) do
tag_input = Tag.parse_tag_list(attrs["implied_tag_list"])
implied_tags =
Tag
|> where([t], t.name in ^tag_input)
|> Repo.all()
tag
|> Tag.changeset(attrs)
|> Tag.changeset(attrs, implied_tags)
|> Repo.update()
end
def update_tag_image(%Tag{} = tag, attrs) do
changeset = Uploader.analyze_upload(tag, attrs)
Multi.new
|> Multi.update(:tag, changeset)
|> Multi.run(:update_file, fn _repo, %{tag: tag} ->
Uploader.persist_upload(tag)
Uploader.unpersist_old_upload(tag)
{:ok, nil}
end)
|> Repo.isolated_transaction(:serializable)
end
def remove_tag_image(%Tag{} = tag) do
changeset = Tag.remove_image_changeset(tag)
Multi.new
|> Multi.update(:tag, changeset)
|> Multi.run(:remove_file, fn _repo, %{tag: tag} ->
Uploader.unpersist_old_upload(tag)
{:ok, nil}
end)
|> Repo.isolated_transaction(:serializable)
end
@doc """
Deletes a Tag.
@ -114,7 +151,19 @@ defmodule Philomena.Tags do
"""
def delete_tag(%Tag{} = tag) do
Repo.delete(tag)
image_ids =
Image
|> join(:inner, [i], _ in assoc(i, :tags))
|> where([_i, t], t.id == ^tag.id)
|> select([i, _t], i.id)
|> Repo.all()
{:ok, _tag} = Repo.delete(tag)
Image
|> where([i], i.id in ^image_ids)
|> preload(^Images.indexing_preloads())
|> Image.reindex()
end
@doc """

View file

@ -50,7 +50,7 @@ defmodule Philomena.Tags.Tag do
schema "tags" do
belongs_to :aliased_tag, Tag, source: :aliased_tag_id
has_many :aliases, Tag, foreign_key: :aliased_tag_id
many_to_many :implied_tags, Tag, join_through: "tags_implied_tags", join_keys: [tag_id: :id, implied_tag_id: :id]
many_to_many :implied_tags, Tag, join_through: "tags_implied_tags", join_keys: [tag_id: :id, implied_tag_id: :id], on_replace: :delete
many_to_many :implied_by_tags, Tag, join_through: "tags_implied_tags", join_keys: [implied_tag_id: :id, tag_id: :id]
has_many :public_links, UserLink, where: [public: true, aasm_state: "verified"]
has_many :dnp_entries, DnpEntry, where: [aasm_state: "listed"]
@ -68,17 +68,42 @@ defmodule Philomena.Tags.Tag do
field :image_mime_type, :string
field :mod_notes, :string
field :uploaded_image, :string, virtual: true
field :removed_image, :string, virtual: true
field :implied_tag_list, :string, virtual: true
timestamps(inserted_at: :created_at)
end
@doc false
def changeset(tag, attrs) do
tag
|> cast(attrs, [])
|> cast(attrs, [:category, :description, :short_description, :mod_notes])
|> put_change(:implied_tag_list, Enum.map_join(tag.implied_tags, ",", & &1.name))
|> validate_required([])
end
@doc false
def changeset(tag, attrs, implied_tags) do
tag
|> cast(attrs, [:category, :description, :short_description, :mod_notes])
|> put_assoc(:implied_tags, implied_tags)
|> validate_required([])
end
def image_changeset(tag, attrs) do
tag
|> cast(attrs, [:image, :image_format, :image_mime_type, :uploaded_image])
|> validate_required([:image, :image_format, :image_mime_type])
|> validate_inclusion(:image_mime_type, ~W(image/gif image/jpeg image/png))
end
def remove_image_changeset(tag) do
change(tag)
|> put_change(:removed_image, tag.image)
|> put_change(:image, nil)
end
def creation_changeset(tag, attrs) do
tag
|> cast(attrs, [:name])
@ -112,6 +137,20 @@ defmodule Philomena.Tags.Tag do
})
end
def categories do
[
"error",
"rating",
"origin",
"character",
"oc",
"species",
"content-fanmade",
"content-official",
"spoiler"
]
end
def clean_tag_name(name) do
# Downcase, replace extra runs of spaces, replace unicode quotes
# with ascii quotes, trim space from end

View file

@ -0,0 +1,24 @@
defmodule Philomena.Tags.Uploader do
@moduledoc """
Upload and processing callback logic for Tag images.
"""
alias Philomena.Tags.Tag
alias Philomena.Uploader
def analyze_upload(tag, params) do
Uploader.analyze_upload(tag, "image", params["image"], &Tag.image_changeset/2)
end
def persist_upload(tag) do
Uploader.persist_upload(tag, tag_file_root(), "image")
end
def unpersist_old_upload(tag) do
Uploader.unpersist_old_upload(tag, tag_file_root(), "image")
end
defp tag_file_root do
Application.get_env(:philomena, :tag_file_root)
end
end

View file

@ -80,6 +80,9 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do
# Hide topics
def can?(%User{role: "moderator"}, :hide, %Topic{}), do: true
# Edit tags
def can?(%User{role: "moderator"}, :edit, %Tag{}), do: true
#
# Assistants can...
#

View file

@ -14,7 +14,7 @@ defmodule PhilomenaWeb.Admin.Report.ClaimController do
conn
|> put_flash(:info, "Successfully marked report as in progress")
|> redirect(to: Routes.admin_report_path(conn, :show, report))
|> redirect(to: Routes.admin_report_path(conn, :index))
{:error, _changeset} ->
conn

View file

@ -5,10 +5,9 @@ defmodule PhilomenaWeb.TagController do
alias Philomena.{Tags, Tags.Tag}
alias Philomena.Textile.Renderer
alias Philomena.Interactions
alias Philomena.Repo
import Ecto.Query
plug PhilomenaWeb.RecodeParameterPlug, [name: "id"] when action in [:show]
plug :load_and_authorize_resource, model: Tag, id_field: "slug", only: [:show, :edit, :update, :delete], preload: [:aliases, :implied_tags, :implied_by_tags, :dnp_entries, public_links: :user]
def index(conn, params) do
query_string = params["tq"] || "*"
@ -32,14 +31,9 @@ defmodule PhilomenaWeb.TagController do
end
end
def show(conn, %{"id" => slug}) do
def show(conn, _params) do
user = conn.assigns.current_user
tag =
Tag
|> where(slug: ^slug)
|> preload([:aliases, :implied_tags, :implied_by_tags, :dnp_entries, public_links: :user])
|> Repo.one()
tag = conn.assigns.tag
{images, _tags} =
ImageLoader.query(conn, %{term: %{"namespaced_tags.name" => tag.name}})
@ -73,6 +67,35 @@ defmodule PhilomenaWeb.TagController do
)
end
def edit(conn, _params) do
changeset = Tags.change_tag(conn.assigns.tag)
render(conn, "edit.html", changeset: changeset)
end
def update(conn, %{"tag" => tag_params}) do
case Tags.update_tag(conn.assigns.tag, tag_params) do
{:ok, tag} ->
Tags.reindex_tag(tag)
conn
|> put_flash(:info, "Tag successfully updated.")
|> redirect(to: Routes.tag_path(conn, :show, tag))
{:error, changeset} ->
render(conn, "edit.html", changeset: changeset)
end
end
def delete(conn, _params) do
spawn fn ->
Tags.delete_tag(conn.assigns.tag)
end
conn
|> put_flash(:info, "Tag scheduled for deletion.")
|> redirect(to: "/")
end
def escape_name(%{name: name}) do
name =
name

View file

@ -208,6 +208,8 @@ defmodule PhilomenaWeb.Router do
resources "/reject", DuplicateReport.RejectController, only: [:create], singleton: true
resources "/claim", DuplicateReport.ClaimController, only: [:create, :delete], singleton: true
end
resources "/tags", TagController, only: [:edit, :update, :delete]
end
scope "/", PhilomenaWeb do

View file

@ -8,6 +8,9 @@
.flex__grow
= render PhilomenaWeb.TagView, "_tag.html", tag: @tag, conn: @conn
= link "Tag changes", to: Routes.tag_tag_change_path(@conn, :index, @tag), class: "detail-link"
= if manages_tags?(@conn) do
= link "Edit details", to: Routes.tag_path(@conn, :edit, @tag), class: "detail-link"
br
= if @tag.short_description not in [nil, ""] do

View file

@ -0,0 +1,65 @@
h1 Editing Tag
= form_for @changeset, Routes.tag_path(@conn, :update, @tag), [class: "form"], fn f ->
= if @changeset.action do
.alert.alert-danger
p Oops, something went wrong! Please check the errors below.
h2
= @tag.name
.field
' Category:
= select f, :category, tag_categories(), class: "input"
h4 Description
.field
=> label f, :short_description, "Short description:"
= text_input f, :short_description, class: "input input--wide"
.field
=> label f, :description, "Long description:"
= textarea f, :description, class: "input input--wide"
.field
=> label f, :mod_notes, "Mod notes:"
= textarea f, :mod_notes, class: "input input--wide"
h4 Implied Tags
.fieldlabel Tags in this list will be added when this tag is added to an image.
.field
= render PhilomenaWeb.TagView, "_tag_editor.html", f: f, name: :implied_tag_list, type: :edit, conn: @conn
/- if can? :manage, Tag
h4 Tag Merging (Aliasing)
.fieldlabel Merge with target tag for searches, user links, filters, etc.; soft keeps the tag around for redirection purposes as an "alias"
.field
= select_tag :merge_mode, options_for_select({ Soft: :alias, Hard: :hard_merge }, :alias), class: "input"
= text_input f, :target_tag_name, class: "input", placeholder: "Target tag name", autocapitalize: "none", value: @tag.aliased_tag_name
br
= submit "Save Tag", class: "button button--state-primary"
/ not ready yet
br
br
input.toggle-box#tag-management checked="false" type="checkbox"
label for="tag-management" Tag Processing
.toggle-box-container
.toggle-box-container__content
= link_to "Rebuild index", admin_tag_reindex_path(@tag), class: "button", data: { confirm: t("are_you_sure") }, method: :post
p Use this if the tag displays the wrong number of images or returns the wrong search results.
= link_to "Recreate slug", admin_tag_slug_path(@tag), class: "button", method: :post
p
| Use this for old tags with invalid slugs (
code
' /mlp/ →
= Tag.generate_slug "/mlp/"
| )
= link_to "Destroy tag", admin_tag_path(@tag), class: "button button--state-danger", data: { confirm: t("are_you_sure") }, method: :delete
p
strong Irreversible. Use with extreme caution!
ul
li Intended use is removing garbage tags.
li Will remove tag changes on the tag, but not on images or profiles.
li Will fail if the tag is the target of an alias, is implied by other tags, or is a rating tag.

View file

@ -6,6 +6,14 @@ defmodule PhilomenaWeb.TagView do
alias Philomena.Repo
import Ecto.Query
def tag_categories do
[[key: "-", value: ""] | Tag.categories]
end
def manages_tags?(conn) do
can?(conn, :edit, %Tag{})
end
def tag_image(%{image: image}) do
tag_url_root() <> "/" <> image
end