mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-23 20:18:00 +01:00
Filter updates
This commit is contained in:
parent
61cafb9054
commit
9294e54771
17 changed files with 212 additions and 16 deletions
|
@ -215,6 +215,18 @@ defmodule Philomena.Users do
|
|||
|> Repo.update()
|
||||
end
|
||||
|
||||
def force_filter(%User{} = user, user_params) do
|
||||
user
|
||||
|> User.force_filter_changeset(user_params)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def unforce_filter(%User{} = user) do
|
||||
user
|
||||
|> User.unforce_filter_changeset()
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking user changes.
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ defmodule Philomena.Users.User do
|
|||
many_to_many :roles, Role, join_through: "users_roles", on_replace: :delete
|
||||
|
||||
belongs_to :current_filter, Filter
|
||||
belongs_to :forced_filter, Filter
|
||||
belongs_to :deleted_by_user, User
|
||||
|
||||
# Authentication
|
||||
|
@ -308,6 +309,16 @@ defmodule Philomena.Users.User do
|
|||
put_api_key(user)
|
||||
end
|
||||
|
||||
def force_filter_changeset(user, params) do
|
||||
user
|
||||
|> cast(params, [:forced_filter_id])
|
||||
|> foreign_key_constraint(:forced_filter_id)
|
||||
end
|
||||
|
||||
def unforce_filter_changeset(user) do
|
||||
change(user, forced_filter_id: nil)
|
||||
end
|
||||
|
||||
def create_totp_secret_changeset(user) do
|
||||
secret = :crypto.strong_rand_bytes(15) |> Base.encode32()
|
||||
data = Philomena.Users.Encryptor.encrypt_model(secret)
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
defmodule PhilomenaWeb.Admin.User.ForceFilterController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.Users.User
|
||||
alias Philomena.Users
|
||||
|
||||
plug :verify_authorized
|
||||
plug :load_resource, model: User, id_name: "user_id", id_field: "slug", persisted: true
|
||||
|
||||
def new(conn, _params) do
|
||||
changeset = Users.change_user(conn.assigns.user)
|
||||
|
||||
render(conn, "new.html", changeset: changeset, title: "Forcing filter for user")
|
||||
end
|
||||
|
||||
def create(conn, %{"user" => user_params}) do
|
||||
{:ok, user} = Users.force_filter(conn.assigns.user, user_params)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Filter was forced.")
|
||||
|> redirect(to: Routes.profile_path(conn, :show, user))
|
||||
end
|
||||
|
||||
def delete(conn, _params) do
|
||||
{:ok, user} = Users.unforce_filter(conn.assigns.current_user)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Forced filter was removed.")
|
||||
|> redirect(to: Routes.profile_path(conn, :show, user))
|
||||
end
|
||||
|
||||
defp verify_authorized(conn, _opts) do
|
||||
case Canada.Can.can?(conn.assigns.current_user, :index, User) do
|
||||
true -> conn
|
||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -16,8 +16,9 @@ defmodule PhilomenaWeb.Image.CommentController do
|
|||
edit: :create_comment,
|
||||
update: :create_comment
|
||||
|
||||
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: [:tags]
|
||||
plug :verify_authorized when action in [:show]
|
||||
plug PhilomenaWeb.FilterForcedUsersPlug when action in [:create, :edit, :update]
|
||||
|
||||
# Undo the previous private parameter screwery
|
||||
plug PhilomenaWeb.LoadCommentPlug, [param: "id", show_hidden: true] when action in [:show]
|
||||
|
|
|
@ -8,7 +8,8 @@ defmodule PhilomenaWeb.Image.FaveController do
|
|||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
plug PhilomenaWeb.CanaryMapPlug, create: :vote, delete: :vote
|
||||
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: [:tags]
|
||||
plug PhilomenaWeb.FilterForcedUsersPlug
|
||||
|
||||
def create(conn, _params) do
|
||||
user = conn.assigns.current_user
|
||||
|
|
|
@ -8,7 +8,8 @@ defmodule PhilomenaWeb.Image.VoteController do
|
|||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
plug PhilomenaWeb.CanaryMapPlug, create: :vote, delete: :vote
|
||||
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: [:tags]
|
||||
plug PhilomenaWeb.FilterForcedUsersPlug
|
||||
|
||||
def create(conn, params) do
|
||||
user = conn.assigns.current_user
|
||||
|
|
|
@ -223,8 +223,9 @@ defmodule PhilomenaWeb.ProfileController do
|
|||
defp set_admin_metadata(conn, _opts) do
|
||||
case Canada.Can.can?(conn.assigns.current_user, :index, User) do
|
||||
true ->
|
||||
user = Repo.preload(conn.assigns.user, :current_filter)
|
||||
user = Repo.preload(conn.assigns.user, [:current_filter, :forced_filter])
|
||||
filter = user.current_filter
|
||||
forced = user.forced_filter
|
||||
|
||||
last_ip =
|
||||
UserIp
|
||||
|
@ -242,6 +243,7 @@ defmodule PhilomenaWeb.ProfileController do
|
|||
|
||||
conn
|
||||
|> assign(:filter, filter)
|
||||
|> assign(:forced, forced)
|
||||
|> assign(:last_ip, last_ip)
|
||||
|> assign(:last_fp, last_fp)
|
||||
|
||||
|
|
|
@ -12,22 +12,25 @@ defmodule PhilomenaWeb.CurrentFilterPlug do
|
|||
conn = conn |> fetch_session()
|
||||
user = conn |> Plug.current_user()
|
||||
|
||||
filter =
|
||||
{filter, forced_filter} =
|
||||
if user do
|
||||
user
|
||||
|> Repo.preload(:current_filter)
|
||||
|> maybe_set_default_filter()
|
||||
|> Map.get(:current_filter)
|
||||
user =
|
||||
user
|
||||
|> Repo.preload([:current_filter, :forced_filter])
|
||||
|> maybe_set_default_filter()
|
||||
|
||||
{user.current_filter, user.forced_filter}
|
||||
else
|
||||
filter_id = conn |> get_session(:filter_id)
|
||||
|
||||
filter = if filter_id, do: Repo.get(Filter, filter_id)
|
||||
|
||||
filter || Filters.default_filter()
|
||||
{filter || Filters.default_filter(), nil}
|
||||
end
|
||||
|
||||
conn
|
||||
|> assign(:current_filter, filter)
|
||||
|> assign(:forced_filter, forced_filter)
|
||||
end
|
||||
|
||||
defp maybe_set_default_filter(%{current_filter: nil} = user) do
|
||||
|
|
59
lib/philomena_web/plugs/filter_forced_users_plug.ex
Normal file
59
lib/philomena_web/plugs/filter_forced_users_plug.ex
Normal file
|
@ -0,0 +1,59 @@
|
|||
defmodule PhilomenaWeb.FilterForcedUsersPlug do
|
||||
@moduledoc """
|
||||
Halts the request pipeline if the current image belongs to the conn's
|
||||
"forced filter".
|
||||
"""
|
||||
|
||||
import Phoenix.Controller
|
||||
import Plug.Conn
|
||||
alias Philomena.Search.String, as: SearchString
|
||||
alias Philomena.Search.Evaluator
|
||||
alias Philomena.Images.Query
|
||||
alias PhilomenaWeb.ImageView
|
||||
|
||||
def init(_opts) do
|
||||
[]
|
||||
end
|
||||
|
||||
def call(conn, _opts) do
|
||||
maybe_fetch_forced(conn, conn.assigns.forced_filter)
|
||||
end
|
||||
|
||||
defp maybe_fetch_forced(conn, nil), do: conn
|
||||
defp maybe_fetch_forced(conn, forced) do
|
||||
maybe_halt(conn, matches_filter?(conn.assigns.current_user, conn.assigns.image, forced))
|
||||
end
|
||||
|
||||
defp maybe_halt(conn, false), do: conn
|
||||
defp maybe_halt(conn, true) do
|
||||
conn
|
||||
|> put_flash(:error, "You have been blocked from performing this action on this image.")
|
||||
|> redirect(external: conn.assigns.referrer)
|
||||
|> halt()
|
||||
end
|
||||
|
||||
defp matches_filter?(user, image, filter) do
|
||||
matches_tag_filter?(image, filter.hidden_tag_ids) or
|
||||
matches_complex_filter?(user, image, filter.hidden_complex_str)
|
||||
end
|
||||
|
||||
defp matches_tag_filter?(image, tag_ids) do
|
||||
image.tags
|
||||
|> MapSet.new(& &1.id)
|
||||
|> MapSet.intersection(MapSet.new(tag_ids))
|
||||
|> Enum.any?()
|
||||
end
|
||||
|
||||
defp matches_complex_filter?(user, image, search_string) do
|
||||
image
|
||||
|> ImageView.image_filter_data()
|
||||
|> Evaluator.hits?(compile_filter(user, search_string))
|
||||
end
|
||||
|
||||
defp compile_filter(user, search_string) do
|
||||
case Query.compile(user, SearchString.normalize(search_string)) do
|
||||
{:ok, query} -> query
|
||||
_error -> %{match_all: %{}}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -11,11 +11,19 @@ defmodule PhilomenaWeb.ImageFilterPlug do
|
|||
# Assign current filter
|
||||
def call(conn, _opts) do
|
||||
user = conn |> Plug.current_user()
|
||||
filter = conn.assigns[:current_filter]
|
||||
filter = defaults(conn.assigns[:current_filter])
|
||||
forced = defaults(conn.assigns[:forced_filter])
|
||||
|
||||
tag_exclusion = %{terms: %{tag_ids: filter.hidden_tag_ids}}
|
||||
query_exclusion = invalid_filter_guard(user, filter.hidden_complex_str)
|
||||
query_spoiler = invalid_filter_guard(user, filter.spoilered_complex_str)
|
||||
query_exclusion = %{
|
||||
bool: %{
|
||||
should: [
|
||||
invalid_filter_guard(user, filter.hidden_complex_str),
|
||||
invalid_filter_guard(user, forced.hidden_complex_str)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
query = %{
|
||||
bool: %{
|
||||
|
@ -29,6 +37,18 @@ defmodule PhilomenaWeb.ImageFilterPlug do
|
|||
|> assign(:compiled_filter, query)
|
||||
end
|
||||
|
||||
defp defaults(nil) do
|
||||
%{
|
||||
hidden_tag_ids: [],
|
||||
hidden_complex_str: nil,
|
||||
spoilered_complex_str: nil
|
||||
}
|
||||
end
|
||||
|
||||
defp defaults(filter) do
|
||||
filter
|
||||
end
|
||||
|
||||
defp invalid_filter_guard(user, search_string) do
|
||||
case Query.compile(user, normalize(search_string)) do
|
||||
{:ok, query} -> query
|
||||
|
|
|
@ -366,6 +366,7 @@ defmodule PhilomenaWeb.Router do
|
|||
resources "/downvotes", User.DownvoteController, only: [:delete], singleton: true
|
||||
resources "/votes", User.VoteController, only: [:delete], singleton: true
|
||||
resources "/wipe", User.WipeController, only: [:create], singleton: true
|
||||
resources "/force_filter", User.ForceFilterController, only: [:new, :create, :delete], singleton: true
|
||||
end
|
||||
|
||||
resources "/batch/tags", Batch.TagController, only: [:update], singleton: true
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
h1
|
||||
' Force-assigning a filter for user
|
||||
= @user.name
|
||||
|
||||
= form_for @changeset, Routes.admin_user_force_filter_path(@conn, :create, @user), [method: "post"], fn f ->
|
||||
.field
|
||||
=> text_input f, :forced_filter_id, placeholder: "Filter ID", class: "input", required: true
|
||||
.field
|
||||
= submit "Force", class: "button button--state-primary"
|
|
@ -12,6 +12,12 @@
|
|||
em
|
||||
' (none)
|
||||
|
||||
= if @forced do
|
||||
br
|
||||
i.fa.fa-fw.fa-filter>
|
||||
strong.comment_deleted> Forced Filter:
|
||||
= link @forced.name, to: Routes.filter_path(@conn, :show, @filter)
|
||||
|
||||
br
|
||||
i.far.fa-fw.fa-clock>
|
||||
' Last seen
|
||||
|
@ -78,6 +84,17 @@ a.label.label--primary.label--block href="#" data-click-toggle=".js-admin__optio
|
|||
i.fas.fa-fw.fa-edit
|
||||
span.admin__button Edit User
|
||||
|
||||
li
|
||||
= link to: Routes.admin_user_force_filter_path(@conn, :new, @user) do
|
||||
i.fas.faw-fw.fa-filter
|
||||
span.admin__button Force Filter
|
||||
|
||||
= if @forced do
|
||||
li
|
||||
= link to: Routes.admin_user_force_filter_path(@conn, :delete, @user), data: [confirm: "Are you really, really sure?", method: "delete"] do
|
||||
i.fas.faw-fw.fa-filter
|
||||
span.admin__button Remove Force Filter
|
||||
|
||||
= if @user.deleted_at do
|
||||
li
|
||||
= link to: Routes.admin_user_activation_path(@conn, :create, @user), data: [confirm: "Are you really, really sure?", method: "post"] do
|
||||
|
|
3
lib/philomena_web/views/admin/user/force_filter_view.ex
Normal file
3
lib/philomena_web/views/admin/user/force_filter_view.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule PhilomenaWeb.Admin.User.ForceFilterView do
|
||||
use PhilomenaWeb, :view
|
||||
end
|
|
@ -216,7 +216,7 @@ defmodule PhilomenaWeb.ImageView do
|
|||
defp thumb_format(_, :rendered, _download), do: "png"
|
||||
defp thumb_format(format, _name, _download), do: format
|
||||
|
||||
defp image_filter_data(image) do
|
||||
def image_filter_data(image) do
|
||||
%{
|
||||
id: image.id,
|
||||
"namespaced_tags.name": String.split(image.tag_list_plus_alias_cache || "", ", "),
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
defmodule Philomena.Repo.Migrations.AddForcedFilter do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:users) do
|
||||
add :forced_filter_id, references(:filters), on_delete: :restrict
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,7 +3,7 @@
|
|||
--
|
||||
|
||||
-- Dumped from database version 12.2 (Debian 12.2-2.pgdg100+1)
|
||||
-- Dumped by pg_dump version 12.2 (Debian 12.2-2.pgdg90+1)
|
||||
-- Dumped by pg_dump version 12.3 (Debian 12.3-1.pgdg90+1)
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
|
@ -1976,7 +1976,8 @@ CREATE TABLE public.users (
|
|||
consumed_timestep integer,
|
||||
otp_required_for_login boolean,
|
||||
otp_backup_codes character varying[],
|
||||
last_renamed_at timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL
|
||||
last_renamed_at timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL,
|
||||
forced_filter_id bigint
|
||||
);
|
||||
|
||||
|
||||
|
@ -4709,9 +4710,17 @@ ALTER TABLE ONLY public.image_sources
|
|||
ADD CONSTRAINT image_sources_image_id_fkey FOREIGN KEY (image_id) REFERENCES public.images(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: users users_forced_filter_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.users
|
||||
ADD CONSTRAINT users_forced_filter_id_fkey FOREIGN KEY (forced_filter_id) REFERENCES public.filters(id);
|
||||
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
INSERT INTO public."schema_migrations" (version) VALUES (20200503002523);
|
||||
INSERT INTO public."schema_migrations" (version) VALUES (20200503002523), (20200607000511);
|
||||
|
||||
|
|
Loading…
Reference in a new issue