mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-23 20:18:00 +01:00
Tag locking (#102)
This commit is contained in:
parent
7030b02183
commit
f112f7928b
14 changed files with 162 additions and 20 deletions
|
@ -265,15 +265,25 @@ defmodule Philomena.Images do
|
||||||
|> Repo.transaction()
|
|> Repo.transaction()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_locked_tags(%Image{} = image, attrs) do
|
||||||
|
new_tags = Tags.get_or_create_tags(attrs["tag_input"])
|
||||||
|
|
||||||
|
image
|
||||||
|
|> Repo.preload(:locked_tags)
|
||||||
|
|> Image.locked_tags_changeset(attrs, new_tags)
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
def update_tags(%Image{} = image, attribution, attrs) do
|
def update_tags(%Image{} = image, attribution, attrs) do
|
||||||
old_tags = Tags.get_or_create_tags(attrs["old_tag_input"])
|
old_tags = Tags.get_or_create_tags(attrs["old_tag_input"])
|
||||||
new_tags = Tags.get_or_create_tags(attrs["tag_input"])
|
new_tags = Tags.get_or_create_tags(attrs["tag_input"])
|
||||||
|
|
||||||
Multi.new()
|
Multi.new()
|
||||||
|> Multi.run(:image, fn repo, _chg ->
|
|> Multi.run(:image, fn repo, _chg ->
|
||||||
|
image = repo.preload(image, [:tags, :locked_tags])
|
||||||
|
|
||||||
image
|
image
|
||||||
|> repo.preload(:tags)
|
|> Image.tag_changeset(%{}, old_tags, new_tags, image.locked_tags)
|
||||||
|> Image.tag_changeset(%{}, old_tags, new_tags)
|
|
||||||
|> repo.update()
|
|> repo.update()
|
||||||
|> case do
|
|> case do
|
||||||
{:ok, image} ->
|
{:ok, image} ->
|
||||||
|
|
|
@ -38,6 +38,7 @@ defmodule Philomena.Images.Image do
|
||||||
has_many :favers, through: [:faves, :user]
|
has_many :favers, through: [:faves, :user]
|
||||||
has_many :hiders, through: [:hides, :user]
|
has_many :hiders, through: [:hides, :user]
|
||||||
many_to_many :tags, Tag, join_through: "image_taggings", on_replace: :delete
|
many_to_many :tags, Tag, join_through: "image_taggings", on_replace: :delete
|
||||||
|
many_to_many :locked_tags, Tag, join_through: "image_tag_locks", on_replace: :delete
|
||||||
has_one :intensity, ImageIntensity
|
has_one :intensity, ImageIntensity
|
||||||
has_many :galleries, through: [:gallery_interactions, :image]
|
has_many :galleries, through: [:gallery_interactions, :image]
|
||||||
|
|
||||||
|
@ -179,14 +180,20 @@ defmodule Philomena.Images.Image do
|
||||||
|> validate_format(:source_url, ~r/\Ahttps?:\/\//)
|
|> validate_format(:source_url, ~r/\Ahttps?:\/\//)
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag_changeset(image, attrs, old_tags, new_tags) do
|
def tag_changeset(image, attrs, old_tags, new_tags, excluded_tags \\ []) do
|
||||||
image
|
image
|
||||||
|> cast(attrs, [])
|
|> cast(attrs, [])
|
||||||
|> TagDiffer.diff_input(old_tags, new_tags)
|
|> TagDiffer.diff_input(old_tags, new_tags, excluded_tags)
|
||||||
|> TagValidator.validate_tags()
|
|> TagValidator.validate_tags()
|
||||||
|> cache_changeset()
|
|> cache_changeset()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def locked_tags_changeset(image, attrs, locked_tags) do
|
||||||
|
image
|
||||||
|
|> cast(attrs, [])
|
||||||
|
|> put_assoc(:locked_tags, locked_tags)
|
||||||
|
end
|
||||||
|
|
||||||
def dnp_changeset(image, user) do
|
def dnp_changeset(image, user) do
|
||||||
image
|
image
|
||||||
|> change()
|
|> change()
|
||||||
|
|
|
@ -5,13 +5,15 @@ defmodule Philomena.Images.TagDiffer do
|
||||||
alias Philomena.Tags.Tag
|
alias Philomena.Tags.Tag
|
||||||
alias Philomena.Repo
|
alias Philomena.Repo
|
||||||
|
|
||||||
def diff_input(changeset, old_tags, new_tags) do
|
def diff_input(changeset, old_tags, new_tags, excluded_tags) do
|
||||||
|
excluded_ids = Enum.map(excluded_tags, & &1.id)
|
||||||
|
|
||||||
old_set = to_set(old_tags)
|
old_set = to_set(old_tags)
|
||||||
new_set = to_set(new_tags)
|
new_set = to_set(new_tags)
|
||||||
|
|
||||||
tags = changeset |> get_field(:tags)
|
tags = changeset |> get_field(:tags)
|
||||||
added_tags = added_set(old_set, new_set)
|
added_tags = added_set(old_set, new_set, excluded_ids)
|
||||||
removed_tags = removed_set(old_set, new_set)
|
removed_tags = removed_set(old_set, new_set, excluded_ids)
|
||||||
|
|
||||||
{tags, actually_added, actually_removed} = apply_changes(tags, added_tags, removed_tags)
|
{tags, actually_added, actually_removed} = apply_changes(tags, added_tags, removed_tags)
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ defmodule Philomena.Images.TagDiffer do
|
||||||
|> put_assoc(:tags, tags)
|
|> put_assoc(:tags, tags)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp added_set(old_set, new_set) do
|
defp added_set(old_set, new_set, excluded_ids) do
|
||||||
# new_tags - old_tags
|
# new_tags - old_tags
|
||||||
added_set =
|
added_set =
|
||||||
new_set
|
new_set
|
||||||
|
@ -40,12 +42,16 @@ defmodule Philomena.Images.TagDiffer do
|
||||||
|> Enum.filter(fn {_k, v} -> v.namespace == "oc" end)
|
|> Enum.filter(fn {_k, v} -> v.namespace == "oc" end)
|
||||||
|> get_oc_tag()
|
|> get_oc_tag()
|
||||||
|
|
||||||
Map.merge(added_and_implied_set, oc_set)
|
added_and_implied_set
|
||||||
|
|> Map.merge(oc_set)
|
||||||
|
|> Map.drop(excluded_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp removed_set(old_set, new_set) do
|
defp removed_set(old_set, new_set, excluded_ids) do
|
||||||
# old_tags - new_tags
|
# old_tags - new_tags
|
||||||
old_set |> Map.drop(Map.keys(new_set))
|
old_set
|
||||||
|
|> Map.drop(Map.keys(new_set))
|
||||||
|
|> Map.drop(excluded_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_oc_tag([]), do: Map.new()
|
defp get_oc_tag([]), do: Map.new()
|
||||||
|
|
21
lib/philomena/images/tag_lock.ex
Normal file
21
lib/philomena/images/tag_lock.ex
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
defmodule Philomena.Images.TagLock do
|
||||||
|
use Ecto.Schema
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
alias Philomena.Images.Image
|
||||||
|
alias Philomena.Tags.Tag
|
||||||
|
|
||||||
|
@primary_key false
|
||||||
|
|
||||||
|
schema "image_tag_locks" do
|
||||||
|
belongs_to :image, Image, primary_key: true
|
||||||
|
belongs_to :tag, Tag, primary_key: true
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def changeset(tag_lock, attrs) do
|
||||||
|
tag_lock
|
||||||
|
|> cast(attrs, [])
|
||||||
|
|> validate_required([])
|
||||||
|
end
|
||||||
|
end
|
|
@ -23,7 +23,7 @@ defmodule PhilomenaWeb.Image.TagController do
|
||||||
plug :load_and_authorize_resource,
|
plug :load_and_authorize_resource,
|
||||||
model: Image,
|
model: Image,
|
||||||
id_name: "image_id",
|
id_name: "image_id",
|
||||||
preload: [:user, tags: :aliases]
|
preload: [:user, :locked_tags, tags: :aliases]
|
||||||
|
|
||||||
def update(conn, %{"image" => image_params}) do
|
def update(conn, %{"image" => image_params}) do
|
||||||
attributes = conn.assigns.attributes
|
attributes = conn.assigns.attributes
|
||||||
|
|
|
@ -4,8 +4,27 @@ defmodule PhilomenaWeb.Image.TagLockController do
|
||||||
alias Philomena.Images.Image
|
alias Philomena.Images.Image
|
||||||
alias Philomena.Images
|
alias Philomena.Images
|
||||||
|
|
||||||
plug PhilomenaWeb.CanaryMapPlug, create: :hide, delete: :hide
|
plug PhilomenaWeb.CanaryMapPlug, show: :hide, update: :hide, create: :hide, delete: :hide
|
||||||
plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true
|
|
||||||
|
plug :load_and_authorize_resource,
|
||||||
|
model: Image,
|
||||||
|
id_name: "image_id",
|
||||||
|
persisted: true,
|
||||||
|
preload: [:locked_tags]
|
||||||
|
|
||||||
|
def show(conn, _params) do
|
||||||
|
changeset = Images.change_image(conn.assigns.image)
|
||||||
|
|
||||||
|
render(conn, "show.html", title: "Locking image tags", changeset: changeset)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(conn, %{"image" => image_attrs}) do
|
||||||
|
{:ok, image} = Images.update_locked_tags(conn.assigns.image, image_attrs)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_flash(:info, "Successfully updated list of locked tags.")
|
||||||
|
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||||
|
end
|
||||||
|
|
||||||
def create(conn, _params) do
|
def create(conn, _params) do
|
||||||
{:ok, image} = Images.lock_tags(conn.assigns.image, true)
|
{:ok, image} = Images.lock_tags(conn.assigns.image, true)
|
||||||
|
|
|
@ -181,7 +181,7 @@ defmodule PhilomenaWeb.ImageController do
|
||||||
[i, _],
|
[i, _],
|
||||||
_ in fragment("SELECT COUNT(*) FROM source_changes s WHERE s.image_id = ?", i.id)
|
_ in fragment("SELECT COUNT(*) FROM source_changes s WHERE s.image_id = ?", i.id)
|
||||||
)
|
)
|
||||||
|> preload([:deleter, user: [awards: :badge], tags: :aliases])
|
|> preload([:deleter, :locked_tags, user: [awards: :badge], tags: :aliases])
|
||||||
|> select([i, t, s], {i, t.count, s.count})
|
|> select([i, t, s], {i, t.count, s.count})
|
||||||
|> Repo.one()
|
|> Repo.one()
|
||||||
|> case do
|
|> case do
|
||||||
|
|
|
@ -212,7 +212,9 @@ defmodule PhilomenaWeb.Router do
|
||||||
only: [:create, :delete],
|
only: [:create, :delete],
|
||||||
singleton: true
|
singleton: true
|
||||||
|
|
||||||
resources "/tag_lock", Image.TagLockController, only: [:create, :delete], singleton: true
|
resources "/tag_lock", Image.TagLockController,
|
||||||
|
only: [:show, :update, :create, :delete],
|
||||||
|
singleton: true
|
||||||
end
|
end
|
||||||
|
|
||||||
resources "/forums", ForumController, only: [] do
|
resources "/forums", ForumController, only: [] do
|
||||||
|
|
|
@ -142,7 +142,8 @@
|
||||||
- else
|
- else
|
||||||
= button_to "Unlock tag editing", Routes.image_tag_lock_path(@conn, :delete, @image), method: "delete", class: "button"
|
= button_to "Unlock tag editing", Routes.image_tag_lock_path(@conn, :delete, @image), method: "delete", class: "button"
|
||||||
|
|
||||||
= if @image.hidden_from_users and can?(@conn, :destroy, @image) do
|
br
|
||||||
br
|
.flex.flex--spaced-out
|
||||||
.flex.flex--spaced-out
|
= link "Lock specific tags", to: Routes.image_tag_lock_path(@conn, :show, @image), class: "button"
|
||||||
= button_to "Destroy image", Routes.image_destroy_path(@conn, :create, @image), method: "post", class: "button button--state-danger", data: [confirm: "This action is IRREVERSIBLE. Are you sure?"]
|
= if @image.hidden_from_users and can?(@conn, :destroy, @image) do
|
||||||
|
= button_to "Destroy image", Routes.image_destroy_path(@conn, :create, @image), method: "post", class: "button button--state-danger", data: [confirm: "This action is IRREVERSIBLE. Are you sure?"]
|
||||||
|
|
|
@ -6,6 +6,12 @@
|
||||||
.js-imageform class=form_class
|
.js-imageform class=form_class
|
||||||
= if can?(@conn, :edit_metadata, @image) and !@conn.assigns.current_ban do
|
= if can?(@conn, :edit_metadata, @image) and !@conn.assigns.current_ban do
|
||||||
|
|
||||||
|
= if Enum.any?(@image.locked_tags) do
|
||||||
|
.block.block--fixed.block--warning
|
||||||
|
i.fa.fa-lock>
|
||||||
|
' The following tags have been restricted on this image:
|
||||||
|
code= Enum.map_join(@image.locked_tags, ", ", & &1.name)
|
||||||
|
|
||||||
= form_for @changeset, Routes.image_tag_path(@conn, :update, @image), [id: "tags-form", method: "put", data: [remote: true]], fn f ->
|
= form_for @changeset, Routes.image_tag_path(@conn, :update, @image), [id: "tags-form", method: "put", data: [remote: true]], fn f ->
|
||||||
= if @changeset.action do
|
= if @changeset.action do
|
||||||
.alert.alert-danger
|
.alert.alert-danger
|
||||||
|
|
13
lib/philomena_web/templates/image/tag_lock/show.html.slime
Normal file
13
lib/philomena_web/templates/image/tag_lock/show.html.slime
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
- tag_input = Enum.map_join(@image.locked_tags, ", ", & &1.name)
|
||||||
|
|
||||||
|
h1
|
||||||
|
| Editing locked tags on image #
|
||||||
|
= @image.id
|
||||||
|
|
||||||
|
= form_for @changeset, Routes.image_tag_lock_path(@conn, :update, @image), fn f ->
|
||||||
|
.field
|
||||||
|
= render PhilomenaWeb.TagView, "_tag_editor.html", f: f, name: :tag_input, type: :edit, extra: [value: tag_input]
|
||||||
|
= error_tag f, :tag_input
|
||||||
|
|
||||||
|
.actions
|
||||||
|
= submit "Update", class: "button", autocomplete: "off", data: [disable_with: "Please wait..."]
|
3
lib/philomena_web/views/image/tag_lock_view.ex
Normal file
3
lib/philomena_web/views/image/tag_lock_view.ex
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
defmodule PhilomenaWeb.Image.TagLockView do
|
||||||
|
use PhilomenaWeb, :view
|
||||||
|
end
|
13
priv/repo/migrations/20210301012137_add_tag_locks.exs
Normal file
13
priv/repo/migrations/20210301012137_add_tag_locks.exs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
defmodule Philomena.Repo.Migrations.AddTagLocks do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table("image_tag_locks", primary_key: false) do
|
||||||
|
add :image_id, references(:images, on_delete: :delete_all), null: false
|
||||||
|
add :tag_id, references(:tags, on_delete: :delete_all), null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
create index("image_tag_locks", [:image_id, :tag_id], unique: true)
|
||||||
|
create index("image_tag_locks", [:tag_id])
|
||||||
|
end
|
||||||
|
end
|
|
@ -858,6 +858,16 @@ CREATE TABLE public.image_subscriptions (
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: image_tag_locks; Type: TABLE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public.image_tag_locks (
|
||||||
|
image_id bigint NOT NULL,
|
||||||
|
tag_id bigint NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: image_taggings; Type: TABLE; Schema: public; Owner: -
|
-- Name: image_taggings; Type: TABLE; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
@ -2796,6 +2806,20 @@ CREATE INDEX image_intensities_index ON public.image_intensities USING btree (nw
|
||||||
CREATE UNIQUE INDEX image_sources_image_id_source_index ON public.image_sources USING btree (image_id, source);
|
CREATE UNIQUE INDEX image_sources_image_id_source_index ON public.image_sources USING btree (image_id, source);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: image_tag_locks_image_id_tag_id_index; Type: INDEX; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX image_tag_locks_image_id_tag_id_index ON public.image_tag_locks USING btree (image_id, tag_id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: image_tag_locks_tag_id_index; Type: INDEX; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE INDEX image_tag_locks_tag_id_index ON public.image_tag_locks USING btree (tag_id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: index_adverts_on_restrictions; Type: INDEX; Schema: public; Owner: -
|
-- Name: index_adverts_on_restrictions; Type: INDEX; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
@ -4772,6 +4796,22 @@ ALTER TABLE ONLY public.image_sources
|
||||||
ADD CONSTRAINT image_sources_image_id_fkey FOREIGN KEY (image_id) REFERENCES public.images(id);
|
ADD CONSTRAINT image_sources_image_id_fkey FOREIGN KEY (image_id) REFERENCES public.images(id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: image_tag_locks image_tag_locks_image_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.image_tag_locks
|
||||||
|
ADD CONSTRAINT image_tag_locks_image_id_fkey FOREIGN KEY (image_id) REFERENCES public.images(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: image_tag_locks image_tag_locks_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.image_tag_locks
|
||||||
|
ADD CONSTRAINT image_tag_locks_tag_id_fkey FOREIGN KEY (tag_id) REFERENCES public.tags(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: user_tokens user_tokens_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
-- Name: user_tokens user_tokens_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
@ -4802,3 +4842,4 @@ INSERT INTO public."schema_migrations" (version) VALUES (20200817213256);
|
||||||
INSERT INTO public."schema_migrations" (version) VALUES (20200905214139);
|
INSERT INTO public."schema_migrations" (version) VALUES (20200905214139);
|
||||||
INSERT INTO public."schema_migrations" (version) VALUES (20201124224116);
|
INSERT INTO public."schema_migrations" (version) VALUES (20201124224116);
|
||||||
INSERT INTO public."schema_migrations" (version) VALUES (20210121200815);
|
INSERT INTO public."schema_migrations" (version) VALUES (20210121200815);
|
||||||
|
INSERT INTO public."schema_migrations" (version) VALUES (20210301012137);
|
||||||
|
|
Loading…
Reference in a new issue