Skip to content

Commit

Permalink
Tag locking (#102)
Browse files Browse the repository at this point in the history
  • Loading branch information
liamwhite authored Mar 1, 2021
1 parent 7030b02 commit f112f79
Show file tree
Hide file tree
Showing 14 changed files with 162 additions and 20 deletions.
14 changes: 12 additions & 2 deletions lib/philomena/images.ex
Original file line number Diff line number Diff line change
Expand Up @@ -265,15 +265,25 @@ defmodule Philomena.Images do
|> Repo.transaction()
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
old_tags = Tags.get_or_create_tags(attrs["old_tag_input"])
new_tags = Tags.get_or_create_tags(attrs["tag_input"])

Multi.new()
|> Multi.run(:image, fn repo, _chg ->
image = repo.preload(image, [:tags, :locked_tags])

image
|> repo.preload(:tags)
|> Image.tag_changeset(%{}, old_tags, new_tags)
|> Image.tag_changeset(%{}, old_tags, new_tags, image.locked_tags)
|> repo.update()
|> case do
{:ok, image} ->
Expand Down
11 changes: 9 additions & 2 deletions lib/philomena/images/image.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ defmodule Philomena.Images.Image do
has_many :favers, through: [:faves, :user]
has_many :hiders, through: [:hides, :user]
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_many :galleries, through: [:gallery_interactions, :image]

Expand Down Expand Up @@ -179,14 +180,20 @@ defmodule Philomena.Images.Image do
|> validate_format(:source_url, ~r/\Ahttps?:\/\//)
end

def tag_changeset(image, attrs, old_tags, new_tags) do
def tag_changeset(image, attrs, old_tags, new_tags, excluded_tags \\ []) do
image
|> cast(attrs, [])
|> TagDiffer.diff_input(old_tags, new_tags)
|> TagDiffer.diff_input(old_tags, new_tags, excluded_tags)
|> TagValidator.validate_tags()
|> cache_changeset()
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
image
|> change()
Expand Down
20 changes: 13 additions & 7 deletions lib/philomena/images/tag_differ.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ defmodule Philomena.Images.TagDiffer do
alias Philomena.Tags.Tag
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)
new_set = to_set(new_tags)

tags = changeset |> get_field(:tags)
added_tags = added_set(old_set, new_set)
removed_tags = removed_set(old_set, new_set)
added_tags = added_set(old_set, new_set, excluded_ids)
removed_tags = removed_set(old_set, new_set, excluded_ids)

{tags, actually_added, actually_removed} = apply_changes(tags, added_tags, removed_tags)

Expand All @@ -21,7 +23,7 @@ defmodule Philomena.Images.TagDiffer do
|> put_assoc(:tags, tags)
end

defp added_set(old_set, new_set) do
defp added_set(old_set, new_set, excluded_ids) do
# new_tags - old_tags
added_set =
new_set
Expand All @@ -40,12 +42,16 @@ defmodule Philomena.Images.TagDiffer do
|> Enum.filter(fn {_k, v} -> v.namespace == "oc" end)
|> get_oc_tag()

Map.merge(added_and_implied_set, oc_set)
added_and_implied_set
|> Map.merge(oc_set)
|> Map.drop(excluded_ids)
end

defp removed_set(old_set, new_set) do
defp removed_set(old_set, new_set, excluded_ids) do
# 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

defp get_oc_tag([]), do: Map.new()
Expand Down
21 changes: 21 additions & 0 deletions lib/philomena/images/tag_lock.ex
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion lib/philomena_web/controllers/image/tag_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ defmodule PhilomenaWeb.Image.TagController do
plug :load_and_authorize_resource,
model: Image,
id_name: "image_id",
preload: [:user, tags: :aliases]
preload: [:user, :locked_tags, tags: :aliases]

def update(conn, %{"image" => image_params}) do
attributes = conn.assigns.attributes
Expand Down
23 changes: 21 additions & 2 deletions lib/philomena_web/controllers/image/tag_lock_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,27 @@ defmodule PhilomenaWeb.Image.TagLockController do
alias Philomena.Images.Image
alias Philomena.Images

plug PhilomenaWeb.CanaryMapPlug, create: :hide, delete: :hide
plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true
plug PhilomenaWeb.CanaryMapPlug, show: :hide, update: :hide, create: :hide, delete: :hide

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
{:ok, image} = Images.lock_tags(conn.assigns.image, true)
Expand Down
2 changes: 1 addition & 1 deletion lib/philomena_web/controllers/image_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ defmodule PhilomenaWeb.ImageController do
[i, _],
_ 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})
|> Repo.one()
|> case do
Expand Down
4 changes: 3 additions & 1 deletion lib/philomena_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,9 @@ defmodule PhilomenaWeb.Router do
only: [:create, :delete],
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

resources "/forums", ForumController, only: [] do
Expand Down
9 changes: 5 additions & 4 deletions lib/philomena_web/templates/image/_options.html.slime
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@
- else
= 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
.flex.flex--spaced-out
= 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?"]
br
.flex.flex--spaced-out
= link "Lock specific tags", to: Routes.image_tag_lock_path(@conn, :show, @image), class: "button"
= 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 changes: 6 additions & 0 deletions lib/philomena_web/templates/image/_tags.html.slime
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
.js-imageform class=form_class
= 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 ->
= if @changeset.action do
.alert.alert-danger
Expand Down
13 changes: 13 additions & 0 deletions lib/philomena_web/templates/image/tag_lock/show.html.slime
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions lib/philomena_web/views/image/tag_lock_view.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defmodule PhilomenaWeb.Image.TagLockView do
use PhilomenaWeb, :view
end
13 changes: 13 additions & 0 deletions priv/repo/migrations/20210301012137_add_tag_locks.exs
Original file line number Diff line number Diff line change
@@ -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
41 changes: 41 additions & 0 deletions priv/repo/structure.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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: -
--
Expand Down Expand Up @@ -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);


--
-- 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: -
--
Expand Down Expand Up @@ -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);


--
-- 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: -
--
Expand Down Expand Up @@ -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 (20201124224116);
INSERT INTO public."schema_migrations" (version) VALUES (20210121200815);
INSERT INTO public."schema_migrations" (version) VALUES (20210301012137);

0 comments on commit f112f79

Please sign in to comment.