mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-27 13:47:58 +01:00
tag description and implication editing
This commit is contained in:
parent
2eb39b43aa
commit
4f3390f728
10 changed files with 233 additions and 17 deletions
|
@ -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 """
|
||||
|
|
|
@ -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
|
||||
|
|
24
lib/philomena/tags/uploader.ex
Normal file
24
lib/philomena/tags/uploader.ex
Normal 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
|
|
@ -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...
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
@ -31,4 +31,4 @@ defmodule PhilomenaWeb.Admin.Report.ClaimController do
|
|||
|> put_flash(:info, "Successfully released report.")
|
||||
|> redirect(to: Routes.admin_report_path(conn, :show, report))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -81,4 +84,4 @@
|
|||
|
||||
| (
|
||||
= link "more info", to: "#"
|
||||
| )
|
||||
| )
|
||||
|
|
65
lib/philomena_web/templates/tag/edit.html.slime
Normal file
65
lib/philomena_web/templates/tag/edit.html.slime
Normal 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.
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue