mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-23 20:18:00 +01:00
image mod tools
This commit is contained in:
parent
6c3dd03270
commit
a74e7e9170
16 changed files with 420 additions and 5 deletions
|
@ -11,6 +11,7 @@ defmodule Philomena.Images do
|
|||
alias Philomena.Images.Image
|
||||
alias Philomena.Images.Hider
|
||||
alias Philomena.Images.Uploader
|
||||
alias Philomena.ImageFeatures.ImageFeature
|
||||
alias Philomena.SourceChanges.SourceChange
|
||||
alias Philomena.TagChanges.TagChange
|
||||
alias Philomena.Tags
|
||||
|
@ -84,6 +85,91 @@ defmodule Philomena.Images do
|
|||
|> Repo.isolated_transaction(:serializable)
|
||||
end
|
||||
|
||||
def feature_image(featurer, %Image{} = image) do
|
||||
image = Repo.preload(image, :tags)
|
||||
[featured] = Tags.get_or_create_tags("featured image")
|
||||
|
||||
feature =
|
||||
%ImageFeature{user_id: featurer.id, image_id: image.id}
|
||||
|> ImageFeature.changeset(%{})
|
||||
|
||||
image =
|
||||
image
|
||||
|> Image.tag_changeset(%{}, image.tags, [featured | image.tags])
|
||||
|> Image.cache_changeset()
|
||||
|
||||
Multi.new()
|
||||
|> Multi.insert(:feature, feature)
|
||||
|> Multi.update(:image, image)
|
||||
|> Multi.run(:added_tag_count, fn repo, %{image: image} ->
|
||||
tag_ids = image.added_tags |> Enum.map(& &1.id)
|
||||
tags = Tag |> where([t], t.id in ^tag_ids)
|
||||
|
||||
{count, nil} = repo.update_all(tags, inc: [images_count: 1])
|
||||
|
||||
{:ok, count}
|
||||
end)
|
||||
|> Repo.isolated_transaction(:serializable)
|
||||
end
|
||||
|
||||
def lock_comments(%Image{} = image, locked) do
|
||||
image
|
||||
|> Image.lock_comments_changeset(locked)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def lock_description(%Image{} = image, locked) do
|
||||
image
|
||||
|> Image.lock_description_changeset(locked)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def lock_tags(%Image{} = image, locked) do
|
||||
image
|
||||
|> Image.lock_tags_changeset(locked)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def remove_hash(%Image{} = image) do
|
||||
image
|
||||
|> Image.remove_hash_changeset()
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def update_scratchpad(%Image{} = image, attrs) do
|
||||
image
|
||||
|> Image.scratchpad_changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def remove_source_history(%Image{} = image) do
|
||||
image
|
||||
|> Repo.preload(:source_changes)
|
||||
|> Image.remove_source_history_changeset()
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def repair_image(%Image{} = image) do
|
||||
Philomena.Images.Thumbnailer.generate_thumbnails(image.id)
|
||||
end
|
||||
|
||||
def update_file(%Image{} = image, attrs) do
|
||||
image =
|
||||
image
|
||||
|> Image.changeset(attrs)
|
||||
|> Uploader.analyze_upload(attrs)
|
||||
|
||||
Multi.new()
|
||||
|> Multi.update(:image, image)
|
||||
|> Multi.run(:after, fn _repo, %{image: image} ->
|
||||
Uploader.persist_upload(image)
|
||||
Uploader.unpersist_old_upload(image)
|
||||
|
||||
{:ok, nil}
|
||||
end)
|
||||
|> Repo.isolated_transaction(:serializable)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a image.
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ defmodule Philomena.Images.Image do
|
|||
has_many :hides, ImageHide
|
||||
has_many :gallery_interactions, Galleries.Interaction
|
||||
has_many :subscriptions, Subscription
|
||||
has_many :source_changes, SourceChange
|
||||
has_many :source_changes, SourceChange, on_replace: :delete
|
||||
has_many :tag_changes, TagChange
|
||||
has_many :upvoters, through: [:upvotes, :user]
|
||||
has_many :downvoters, through: [:downvotes, :user]
|
||||
|
@ -204,6 +204,32 @@ defmodule Philomena.Images.Image do
|
|||
|> put_change(:duplicate_id, nil)
|
||||
end
|
||||
|
||||
def lock_comments_changeset(image, locked) do
|
||||
change(image, commenting_allowed: not locked)
|
||||
end
|
||||
|
||||
def lock_description_changeset(image, locked) do
|
||||
change(image, description_editing_allowed: not locked)
|
||||
end
|
||||
|
||||
def lock_tags_changeset(image, locked) do
|
||||
change(image, tag_editing_allowed: not locked)
|
||||
end
|
||||
|
||||
def remove_hash_changeset(image) do
|
||||
change(image, image_orig_sha512_hash: nil)
|
||||
end
|
||||
|
||||
def scratchpad_changeset(image, attrs) do
|
||||
cast(image, attrs, [:scratchpad])
|
||||
end
|
||||
|
||||
def remove_source_history_changeset(image) do
|
||||
change(image)
|
||||
|> put_change(:source_url, nil)
|
||||
|> put_assoc(:source_changes, [])
|
||||
end
|
||||
|
||||
def cache_changeset(image) do
|
||||
changeset = change(image)
|
||||
image = apply_changes(changeset)
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
defmodule PhilomenaWeb.Image.CommentLockController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
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
|
||||
|
||||
def create(conn, _params) do
|
||||
{:ok, image} = Images.lock_comments(conn.assigns.image, true)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Successfully locked comments.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||
end
|
||||
|
||||
def delete(conn, _params) do
|
||||
{:ok, image} = Images.lock_comments(conn.assigns.image, false)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Successfully unlocked comments.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
defmodule PhilomenaWeb.Image.DescriptionLockController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
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
|
||||
|
||||
def create(conn, _params) do
|
||||
{:ok, image} = Images.lock_description(conn.assigns.image, true)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Successfully locked description.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||
end
|
||||
|
||||
def delete(conn, _params) do
|
||||
{:ok, image} = Images.lock_description(conn.assigns.image, false)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Successfully unlocked description.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||
end
|
||||
end
|
32
lib/philomena_web/controllers/image/feature_controller.ex
Normal file
32
lib/philomena_web/controllers/image/feature_controller.ex
Normal file
|
@ -0,0 +1,32 @@
|
|||
defmodule PhilomenaWeb.Image.FeatureController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.Images.Image
|
||||
alias Philomena.Images
|
||||
alias Philomena.Tags
|
||||
|
||||
plug PhilomenaWeb.CanaryMapPlug, create: :hide
|
||||
plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true
|
||||
plug :verify_not_deleted
|
||||
|
||||
def create(conn, _params) do
|
||||
{:ok, %{image: image}} = Images.feature_image(conn.assigns.current_user, conn.assigns.image)
|
||||
Tags.reindex_tags(image.added_tags)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Image marked as featured image.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||
end
|
||||
|
||||
defp verify_not_deleted(conn, _opts) do
|
||||
case conn.assigns.image.hidden_from_users do
|
||||
true ->
|
||||
conn
|
||||
|> put_flash(:error, "Cannot feature a hidden image.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, conn.assigns.image))
|
||||
|
||||
_false ->
|
||||
conn
|
||||
end
|
||||
end
|
||||
end
|
40
lib/philomena_web/controllers/image/file_controller.ex
Normal file
40
lib/philomena_web/controllers/image/file_controller.ex
Normal file
|
@ -0,0 +1,40 @@
|
|||
defmodule PhilomenaWeb.Image.FileController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.Images.Image
|
||||
alias Philomena.Images
|
||||
|
||||
plug PhilomenaWeb.CanaryMapPlug, update: :hide
|
||||
plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true
|
||||
plug :verify_not_deleted
|
||||
|
||||
def update(conn, %{"image" => image_params}) do
|
||||
case Images.update_file(conn.assigns.image, image_params) do
|
||||
{:ok, %{image: image}} ->
|
||||
spawn fn ->
|
||||
Images.repair_image(image)
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Successfully updated file.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||
|
||||
_error ->
|
||||
conn
|
||||
|> put_flash(:error, "Failed to update file!")
|
||||
|> redirect(to: Routes.image_path(conn, :show, conn.assigns.image))
|
||||
end
|
||||
end
|
||||
|
||||
defp verify_not_deleted(conn, _opts) do
|
||||
case conn.assigns.image.hidden_from_users do
|
||||
true ->
|
||||
conn
|
||||
|> put_flash(:error, "Cannot replace a hidden image.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, conn.assigns.image))
|
||||
|
||||
_false ->
|
||||
conn
|
||||
end
|
||||
end
|
||||
end
|
17
lib/philomena_web/controllers/image/hash_controller.ex
Normal file
17
lib/philomena_web/controllers/image/hash_controller.ex
Normal file
|
@ -0,0 +1,17 @@
|
|||
defmodule PhilomenaWeb.Image.HashController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.Images.Image
|
||||
alias Philomena.Images
|
||||
|
||||
plug PhilomenaWeb.CanaryMapPlug, delete: :hide
|
||||
plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true
|
||||
|
||||
def delete(conn, _params) do
|
||||
{:ok, image} = Images.remove_hash(conn.assigns.image)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Successfully cleared hash.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||
end
|
||||
end
|
32
lib/philomena_web/controllers/image/repair_controller.ex
Normal file
32
lib/philomena_web/controllers/image/repair_controller.ex
Normal file
|
@ -0,0 +1,32 @@
|
|||
defmodule PhilomenaWeb.Image.RepairController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.Images.Image
|
||||
alias Philomena.Images
|
||||
|
||||
plug PhilomenaWeb.CanaryMapPlug, create: :hide
|
||||
plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true
|
||||
plug :verify_not_deleted
|
||||
|
||||
def create(conn, _params) do
|
||||
spawn fn ->
|
||||
Images.repair_image(conn.assigns.image)
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Repair job started.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, conn.assigns.image))
|
||||
end
|
||||
|
||||
defp verify_not_deleted(conn, _opts) do
|
||||
case conn.assigns.image.hidden_from_users do
|
||||
true ->
|
||||
conn
|
||||
|> put_flash(:error, "Cannot repair a hidden image.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, conn.assigns.image))
|
||||
|
||||
_false ->
|
||||
conn
|
||||
end
|
||||
end
|
||||
end
|
22
lib/philomena_web/controllers/image/scratchpad_controller.ex
Normal file
22
lib/philomena_web/controllers/image/scratchpad_controller.ex
Normal file
|
@ -0,0 +1,22 @@
|
|||
defmodule PhilomenaWeb.Image.ScratchpadController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.Images.Image
|
||||
alias Philomena.Images
|
||||
|
||||
plug PhilomenaWeb.CanaryMapPlug, edit: :hide, update: :hide
|
||||
plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true
|
||||
|
||||
def edit(conn, _params) do
|
||||
changeset = Images.change_image(conn.assigns.image)
|
||||
render(conn, "edit.html", changeset: changeset)
|
||||
end
|
||||
|
||||
def update(conn, %{"image" => image_params}) do
|
||||
{:ok, image} = Images.update_scratchpad(conn.assigns.image, image_params)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Successfully updated moderation notes.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
defmodule PhilomenaWeb.Image.SourceHistoryController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.Images.Image
|
||||
alias Philomena.Images
|
||||
|
||||
plug PhilomenaWeb.CanaryMapPlug, delete: :hide
|
||||
plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true
|
||||
|
||||
def delete(conn, _params) do
|
||||
{:ok, image} = Images.remove_source_history(conn.assigns.image)
|
||||
|
||||
Images.reindex_image(image)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Successfully deleted source history.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||
end
|
||||
end
|
25
lib/philomena_web/controllers/image/tag_lock_controller.ex
Normal file
25
lib/philomena_web/controllers/image/tag_lock_controller.ex
Normal file
|
@ -0,0 +1,25 @@
|
|||
defmodule PhilomenaWeb.Image.TagLockController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
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
|
||||
|
||||
def create(conn, _params) do
|
||||
{:ok, image} = Images.lock_tags(conn.assigns.image, true)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Successfully locked tags.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||
end
|
||||
|
||||
def delete(conn, _params) do
|
||||
{:ok, image} = Images.lock_tags(conn.assigns.image, false)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Successfully unlocked tags.")
|
||||
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||
end
|
||||
end
|
|
@ -120,6 +120,17 @@ defmodule PhilomenaWeb.Router do
|
|||
resources "/delete", Image.Comment.DeleteController, only: [:create], singleton: true
|
||||
end
|
||||
resources "/delete", Image.DeleteController, only: [:create, :delete], singleton: true
|
||||
|
||||
resources "/hash", Image.HashController, only: [:delete], singleton: true
|
||||
resources "/source_history", Image.SourceHistoryController, only: [:delete], singleton: true
|
||||
resources "/repair", Image.RepairController, only: [:create], singleton: true
|
||||
resources "/feature", Image.FeatureController, only: [:create], singleton: true
|
||||
resources "/file", Image.FileController, only: [:update], singleton: true
|
||||
resources "/scratchpad", Image.ScratchpadController, only: [:edit, :update], singleton: true
|
||||
|
||||
resources "/comment_lock", Image.CommentLockController, only: [:create, :delete], singleton: true
|
||||
resources "/description_lock", Image.DescriptionLockController, only: [:create, :delete], singleton: true
|
||||
resources "/tag_lock", Image.TagLockController, only: [:create, :delete], singleton: true
|
||||
end
|
||||
|
||||
resources "/forums", ForumController, only: [] do
|
||||
|
|
|
@ -65,15 +65,18 @@
|
|||
br
|
||||
textarea.input.input--wide.input--separate-top#bbcode_embed_thumbnail_tag rows="2" cols="100" readonly="readonly"
|
||||
= "[img]#{thumb_url(@image, false, :medium)}[/img]\n[url=#{Routes.image_url(@conn, :show, @image)}]View on Derpibooru[/url]#{source_link}"
|
||||
|
||||
= if display_mod_tools? do
|
||||
.block__tab.hidden data-tab="replace"
|
||||
/= form_tag image_file_path(@image), method: :put, multipart: true do
|
||||
= render partial: 'layouts/image_upload', locals: { form: nil, field: :image }
|
||||
= submit_tag 'Save changes', class: 'button', autocomplete: 'off', data: { disable_with: 'Replacing...' }
|
||||
= form_for @changeset, Routes.image_file_path(@conn, :update, @image), [method: "put", multipart: true], fn f ->
|
||||
#js-image-upload-previews
|
||||
p Upload a file from your computer
|
||||
.field = file_input f, :image, class: "input js-scraper"
|
||||
= submit "Save changes", class: "button", data: [disable_with: raw("Replacing…")]
|
||||
|
||||
.block__tab.hidden data-tab="administration"
|
||||
.block.block--danger
|
||||
a.button.button--link> href="#"
|
||||
a.button.button--link> href=Routes.image_scratchpad_path(@conn, :edit, @image)
|
||||
i.far.fa-edit
|
||||
= if present?(@image.scratchpad) do
|
||||
strong> Mod notes:
|
||||
|
@ -90,3 +93,37 @@
|
|||
|
||||
- else
|
||||
= button_to "Restore", Routes.image_delete_path(@conn, :delete, @image), method: "delete", class: "button button--state-success"
|
||||
|
||||
.flex.flex--spaced-out.flex--wrap
|
||||
= if not @image.hidden_from_users do
|
||||
= form_for @changeset, Routes.image_feature_path(@conn, :create, @image), [method: "post"], fn _f ->
|
||||
.field
|
||||
p Automatically tags 'featured image'
|
||||
= submit "Feature", data: [confirm: "Are you really, really sure?"], class: "button button--state-success"
|
||||
|
||||
= form_for @changeset, Routes.image_repair_path(@conn, :create, @image), [method: "post"], fn _f ->
|
||||
.field
|
||||
= submit "Repair", class: "button button--state-success"
|
||||
|
||||
= form_for @changeset, Routes.image_hash_path(@conn, :delete, @image), [method: "delete"], fn _f ->
|
||||
.field
|
||||
p Allows reuploading the image
|
||||
.flex.flex--end-bunched
|
||||
= submit "Clear hash", data: [confirm: "Are you really, really sure?"], class: "button button--state-danger"
|
||||
|
||||
br
|
||||
.flex.flex--spaced-out
|
||||
= if @image.commenting_allowed do
|
||||
= button_to "Lock commenting", Routes.image_comment_lock_path(@conn, :create, @image), method: "post", class: "button"
|
||||
- else
|
||||
= button_to "Unlock commenting", Routes.image_comment_lock_path(@conn, :delete, @image), method: "delete", class: "button"
|
||||
|
||||
= if @image.description_editing_allowed do
|
||||
= button_to "Lock description editing", Routes.image_description_lock_path(@conn, :create, @image), method: "post", class: "button"
|
||||
- else
|
||||
= button_to "Unlock description editing", Routes.image_description_lock_path(@conn, :delete, @image), method: "delete", class: "button"
|
||||
|
||||
= if @image.tag_editing_allowed do
|
||||
= button_to "Lock tag editing", Routes.image_tag_lock_path(@conn, :create, @image), method: "post", class: "button"
|
||||
- else
|
||||
= button_to "Unlock tag editing", Routes.image_tag_lock_path(@conn, :delete, @image), method: "delete", class: "button"
|
||||
|
|
|
@ -41,3 +41,9 @@
|
|||
| History (
|
||||
= @source_change_count
|
||||
| )
|
||||
|
||||
= if can?(@conn, :hide, @image) do
|
||||
= form_for @changeset, Routes.image_source_history_path(@conn, :delete, @image), [method: "delete"], fn _f ->
|
||||
button.button.button--state-danger.button--separate-left type="submit" data-confirm="Are you really, really sure?" title="Wipe sources"
|
||||
i.fas.fa-eraser>
|
||||
' Wipe
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
h1
|
||||
' Editing moderation notes for image
|
||||
= link "##{@image.id}", to: Routes.image_path(@conn, :show, @image)
|
||||
|
||||
= form_for @changeset, Routes.image_scratchpad_path(@conn, :update, @image), fn f ->
|
||||
.field
|
||||
= textarea f, :scratchpad, placeholder: "Scratchpad contents", class: "input input--wide"
|
||||
|
||||
= submit "Update", class: "button", data: [disable_with: raw("Saving…")]
|
3
lib/philomena_web/views/image/scratchpad_view.ex
Normal file
3
lib/philomena_web/views/image/scratchpad_view.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule PhilomenaWeb.Image.ScratchpadView do
|
||||
use PhilomenaWeb, :view
|
||||
end
|
Loading…
Reference in a new issue