mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-27 13:47:58 +01:00
approval queue stuff
This commit is contained in:
parent
eeb2f851e0
commit
26deaaf588
44 changed files with 449 additions and 82 deletions
|
@ -470,6 +470,7 @@ span.stat {
|
||||||
@import "text";
|
@import "text";
|
||||||
|
|
||||||
@import "~views/adverts";
|
@import "~views/adverts";
|
||||||
|
@import "~views/approval";
|
||||||
@import "~views/badges";
|
@import "~views/badges";
|
||||||
@import "~views/channels";
|
@import "~views/channels";
|
||||||
@import "~views/comments";
|
@import "~views/comments";
|
||||||
|
|
49
assets/css/views/_approval.scss
Normal file
49
assets/css/views/_approval.scss
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
.approval-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
gap: var(--padding-small);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.approval-items--main {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr 1fr 1fr;
|
||||||
|
gap: var(--padding-small);
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.approval-items--main span {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.approval-items--footer {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
gap: var(--padding-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.approval-items--footer * {
|
||||||
|
height: 2rem;
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $min_px_width_for_desktop_layout) {
|
||||||
|
.approval-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: auto;
|
||||||
|
grid-template-areas:
|
||||||
|
"main"
|
||||||
|
"footer";
|
||||||
|
}
|
||||||
|
|
||||||
|
.approval-items--main {
|
||||||
|
grid-area: main;
|
||||||
|
}
|
||||||
|
|
||||||
|
.approval-items--footer {
|
||||||
|
grid-area: footer;
|
||||||
|
}
|
||||||
|
}
|
26
assets/js/pmwarning.js
Normal file
26
assets/js/pmwarning.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
* PmWarning
|
||||||
|
*
|
||||||
|
* Warn users that their PM will be reviewed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { $ } from './utils/dom';
|
||||||
|
|
||||||
|
function warnAboutPMs() {
|
||||||
|
const textarea = $('.js-toolbar-input');
|
||||||
|
const warning = $('.js-hidden-warning');
|
||||||
|
const imageEmbedRegex = /!+\[/g;
|
||||||
|
|
||||||
|
if (!warning || !textarea) return;
|
||||||
|
|
||||||
|
textarea.addEventListener('input', () => {
|
||||||
|
const value = textarea.value;
|
||||||
|
|
||||||
|
if (value.match(imageEmbedRegex))
|
||||||
|
warning.classList.remove('hidden');
|
||||||
|
else if (!warning.classList.contains('hidden'))
|
||||||
|
warning.classList.add('hidden');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { warnAboutPMs };
|
|
@ -34,6 +34,7 @@ import { setupSearch } from './search';
|
||||||
import { setupToolbar } from './markdowntoolbar';
|
import { setupToolbar } from './markdowntoolbar';
|
||||||
import { hideStaffTools } from './staffhider';
|
import { hideStaffTools } from './staffhider';
|
||||||
import { pollOptionCreator } from './poll';
|
import { pollOptionCreator } from './poll';
|
||||||
|
import { warnAboutPMs } from './pmwarning';
|
||||||
|
|
||||||
whenReady(() => {
|
whenReady(() => {
|
||||||
|
|
||||||
|
@ -66,5 +67,6 @@ whenReady(() => {
|
||||||
setupToolbar();
|
setupToolbar();
|
||||||
hideStaffTools();
|
hideStaffTools();
|
||||||
pollOptionCreator();
|
pollOptionCreator();
|
||||||
|
warnAboutPMs();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Philomena.Comments do
|
||||||
|
|
||||||
alias Philomena.Elasticsearch
|
alias Philomena.Elasticsearch
|
||||||
alias Philomena.Reports.Report
|
alias Philomena.Reports.Report
|
||||||
|
alias Philomena.UserStatistics
|
||||||
alias Philomena.Comments.Comment
|
alias Philomena.Comments.Comment
|
||||||
alias Philomena.Comments.ElasticsearchIndex, as: CommentIndex
|
alias Philomena.Comments.ElasticsearchIndex, as: CommentIndex
|
||||||
alias Philomena.IndexWorker
|
alias Philomena.IndexWorker
|
||||||
|
@ -212,6 +213,8 @@ defmodule Philomena.Comments do
|
||||||
|> Repo.transaction()
|
|> Repo.transaction()
|
||||||
|> case do
|
|> case do
|
||||||
{:ok, %{comment: comment, reports: {_count, reports}}} ->
|
{:ok, %{comment: comment, reports: {_count, reports}}} ->
|
||||||
|
notify_comment(comment)
|
||||||
|
UserStatistics.inc_stat(comment.user, :comments_posted)
|
||||||
Reports.reindex_reports(reports)
|
Reports.reindex_reports(reports)
|
||||||
reindex_comment(comment)
|
reindex_comment(comment)
|
||||||
|
|
||||||
|
@ -222,6 +225,8 @@ defmodule Philomena.Comments do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def report_non_approved(%Comment{approved: true}), do: false
|
||||||
|
|
||||||
def report_non_approved(comment) do
|
def report_non_approved(comment) do
|
||||||
Reports.create_system_report(
|
Reports.create_system_report(
|
||||||
comment.id,
|
comment.id,
|
||||||
|
|
|
@ -6,7 +6,7 @@ defmodule Philomena.Conversations do
|
||||||
import Ecto.Query, warn: false
|
import Ecto.Query, warn: false
|
||||||
alias Ecto.Multi
|
alias Ecto.Multi
|
||||||
alias Philomena.Repo
|
alias Philomena.Repo
|
||||||
|
alias Philomena.Reports
|
||||||
alias Philomena.Conversations.Conversation
|
alias Philomena.Conversations.Conversation
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -187,6 +187,12 @@ defmodule Philomena.Conversations do
|
||||||
Ecto.build_assoc(conversation, :messages)
|
Ecto.build_assoc(conversation, :messages)
|
||||||
|> Message.creation_changeset(attrs, user)
|
|> Message.creation_changeset(attrs, user)
|
||||||
|
|
||||||
|
show_as_read =
|
||||||
|
case message do
|
||||||
|
%{changes: %{approved: true}} -> false
|
||||||
|
_ -> true
|
||||||
|
end
|
||||||
|
|
||||||
conversation_query =
|
conversation_query =
|
||||||
Conversation
|
Conversation
|
||||||
|> where(id: ^conversation.id)
|
|> where(id: ^conversation.id)
|
||||||
|
@ -196,11 +202,43 @@ defmodule Philomena.Conversations do
|
||||||
Multi.new()
|
Multi.new()
|
||||||
|> Multi.insert(:message, message)
|
|> Multi.insert(:message, message)
|
||||||
|> Multi.update_all(:conversation, conversation_query,
|
|> Multi.update_all(:conversation, conversation_query,
|
||||||
set: [from_read: false, to_read: false, last_message_at: now]
|
set: [from_read: show_as_read, to_read: show_as_read, last_message_at: now]
|
||||||
)
|
)
|
||||||
|> Repo.transaction()
|
|> Repo.transaction()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def approve_conversation_message(message) do
|
||||||
|
message_query =
|
||||||
|
message
|
||||||
|
|> Message.approve_changeset()
|
||||||
|
|
||||||
|
conversation_query =
|
||||||
|
Conversation
|
||||||
|
|> where(id: ^message.conversation_id)
|
||||||
|
|
||||||
|
now = DateTime.utc_now()
|
||||||
|
|
||||||
|
Multi.new()
|
||||||
|
|> Multi.update(:message, message_query)
|
||||||
|
|> Multi.update_all(:conversation, conversation_query, set: [to_read: false])
|
||||||
|
|> Repo.transaction()
|
||||||
|
end
|
||||||
|
|
||||||
|
def report_non_approved(id) do
|
||||||
|
Reports.create_system_report(
|
||||||
|
id,
|
||||||
|
"Conversation",
|
||||||
|
"Approval",
|
||||||
|
"PM contains externally-embedded images and has been flagged for review."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_as_read(conversation) do
|
||||||
|
conversation
|
||||||
|
|> Conversation.to_read_changeset()
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Updates a message.
|
Updates a message.
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,11 @@ defmodule Philomena.Conversations.Conversation do
|
||||||
|> cast(attrs, [:from_read, :to_read])
|
|> cast(attrs, [:from_read, :to_read])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_read_changeset(conversation) do
|
||||||
|
change(conversation)
|
||||||
|
|> put_change(:to_read, true)
|
||||||
|
end
|
||||||
|
|
||||||
def hidden_changeset(conversation, attrs) do
|
def hidden_changeset(conversation, attrs) do
|
||||||
conversation
|
conversation
|
||||||
|> cast(attrs, [:from_hidden, :to_hidden])
|
|> cast(attrs, [:from_hidden, :to_hidden])
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Philomena.Conversations.Message do
|
||||||
belongs_to :from, User
|
belongs_to :from, User
|
||||||
|
|
||||||
field :body, :string
|
field :body, :string
|
||||||
|
field :approved, :boolean, default: false
|
||||||
|
|
||||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||||
end
|
end
|
||||||
|
@ -31,4 +32,8 @@ defmodule Philomena.Conversations.Message do
|
||||||
|> validate_length(:body, max: 300_000, count: :bytes)
|
|> validate_length(:body, max: 300_000, count: :bytes)
|
||||||
|> Approval.maybe_put_approval(user)
|
|> Approval.maybe_put_approval(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def approve_changeset(message) do
|
||||||
|
change(message, approved: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -115,7 +115,7 @@ defmodule Philomena.Images do
|
||||||
repair_image(image)
|
repair_image(image)
|
||||||
reindex_image(image)
|
reindex_image(image)
|
||||||
Tags.reindex_tags(image.added_tags)
|
Tags.reindex_tags(image.added_tags)
|
||||||
UserStatistics.inc_stat(attribution[:user], :uploads)
|
maybe_approve_image(image, attribution[:user])
|
||||||
|
|
||||||
result
|
result
|
||||||
|
|
||||||
|
@ -135,6 +135,55 @@ defmodule Philomena.Images do
|
||||||
multi
|
multi
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def approve_image(image) do
|
||||||
|
image
|
||||||
|
|> Repo.preload(:user)
|
||||||
|
|> Image.approve_changeset()
|
||||||
|
|> Repo.update()
|
||||||
|
|> case do
|
||||||
|
{:ok, image} ->
|
||||||
|
reindex_image(image)
|
||||||
|
increment_user_stats(image.user)
|
||||||
|
maybe_suggest_user_verification(image.user)
|
||||||
|
|
||||||
|
{:ok, image}
|
||||||
|
|
||||||
|
error ->
|
||||||
|
error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_approve_image(image, nil), do: false
|
||||||
|
|
||||||
|
defp maybe_approve_image(image, %User{verified: false, role: role}) when role == "user",
|
||||||
|
do: false
|
||||||
|
|
||||||
|
defp maybe_approve_image(image, _user), do: approve_image(image)
|
||||||
|
|
||||||
|
defp increment_user_stats(nil), do: false
|
||||||
|
|
||||||
|
defp increment_user_stats(%User{} = user) do
|
||||||
|
UserStatistics.inc_stat(user, :uploads)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_suggest_user_verification(%User{id: id, uploads_count: 5, verified: false}) do
|
||||||
|
Reports.create_system_report(
|
||||||
|
id,
|
||||||
|
"User",
|
||||||
|
"Verification",
|
||||||
|
"User has uploaded enough approved images to be considered for verification."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_suggest_user_verification(_user), do: false
|
||||||
|
|
||||||
|
def count_pending_approvals() do
|
||||||
|
Image
|
||||||
|
|> where(hidden_from_users: false)
|
||||||
|
|> where(approved: false)
|
||||||
|
|> Repo.aggregate(:count)
|
||||||
|
end
|
||||||
|
|
||||||
def feature_image(featurer, %Image{} = image) do
|
def feature_image(featurer, %Image{} = image) do
|
||||||
%ImageFeature{user_id: featurer.id, image_id: image.id}
|
%ImageFeature{user_id: featurer.id, image_id: image.id}
|
||||||
|> ImageFeature.changeset(%{})
|
|> ImageFeature.changeset(%{})
|
||||||
|
|
|
@ -326,6 +326,12 @@ defmodule Philomena.Images.Image do
|
||||||
cast(image, attrs, [:anonymous])
|
cast(image, attrs, [:anonymous])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def approve_changeset(image) do
|
||||||
|
change(image)
|
||||||
|
|> put_change(:approved, true)
|
||||||
|
|> put_change(:first_seen_at, DateTime.truncate(DateTime.utc_now(), :second))
|
||||||
|
end
|
||||||
|
|
||||||
def cache_changeset(image) do
|
def cache_changeset(image) do
|
||||||
changeset = change(image)
|
changeset = change(image)
|
||||||
image = apply_changes(changeset)
|
image = apply_changes(changeset)
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Philomena.Posts do
|
||||||
alias Philomena.Elasticsearch
|
alias Philomena.Elasticsearch
|
||||||
alias Philomena.Topics.Topic
|
alias Philomena.Topics.Topic
|
||||||
alias Philomena.Topics
|
alias Philomena.Topics
|
||||||
|
alias Philomena.UserStatistics
|
||||||
alias Philomena.Posts.Post
|
alias Philomena.Posts.Post
|
||||||
alias Philomena.Posts.ElasticsearchIndex, as: PostIndex
|
alias Philomena.Posts.ElasticsearchIndex, as: PostIndex
|
||||||
alias Philomena.IndexWorker
|
alias Philomena.IndexWorker
|
||||||
|
@ -117,6 +118,8 @@ defmodule Philomena.Posts do
|
||||||
Exq.enqueue(Exq, "notifications", NotificationWorker, ["Posts", post.id])
|
Exq.enqueue(Exq, "notifications", NotificationWorker, ["Posts", post.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def report_non_approved(%Post{approved: true}), do: false
|
||||||
|
|
||||||
def report_non_approved(post) do
|
def report_non_approved(post) do
|
||||||
Reports.create_system_report(
|
Reports.create_system_report(
|
||||||
post.id,
|
post.id,
|
||||||
|
@ -261,6 +264,8 @@ defmodule Philomena.Posts do
|
||||||
|> Repo.transaction()
|
|> Repo.transaction()
|
||||||
|> case do
|
|> case do
|
||||||
{:ok, %{post: post, reports: {_count, reports}}} ->
|
{:ok, %{post: post, reports: {_count, reports}}} ->
|
||||||
|
notify_post(post)
|
||||||
|
UserStatistics.inc_stat(post.user, :forum_posts)
|
||||||
Reports.reindex_reports(reports)
|
Reports.reindex_reports(reports)
|
||||||
reindex_post(post)
|
reindex_post(post)
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,8 @@ defmodule Philomena.Reports.ElasticsearchIndex do
|
||||||
reportable_type: %{type: "keyword"},
|
reportable_type: %{type: "keyword"},
|
||||||
reportable_id: %{type: "keyword"},
|
reportable_id: %{type: "keyword"},
|
||||||
open: %{type: "boolean"},
|
open: %{type: "boolean"},
|
||||||
reason: %{type: "text", analyzer: "snowball"}
|
reason: %{type: "text", analyzer: "snowball"},
|
||||||
|
system: %{type: "boolean"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +54,8 @@ defmodule Philomena.Reports.ElasticsearchIndex do
|
||||||
reportable_id: report.reportable_id,
|
reportable_id: report.reportable_id,
|
||||||
fingerprint: report.fingerprint,
|
fingerprint: report.fingerprint,
|
||||||
open: report.open,
|
open: report.open,
|
||||||
reason: report.reason
|
reason: report.reason,
|
||||||
|
system: report.system
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,7 @@ defmodule Philomena.Topics do
|
||||||
|> case do
|
|> case do
|
||||||
{:ok, %{topic: topic}} = result ->
|
{:ok, %{topic: topic}} = result ->
|
||||||
Posts.reindex_post(hd(topic.posts))
|
Posts.reindex_post(hd(topic.posts))
|
||||||
|
Posts.report_non_approved(hd(topic.posts))
|
||||||
|
|
||||||
result
|
result
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ defmodule Philomena.Topics.Topic do
|
||||||
field :slug, :string
|
field :slug, :string
|
||||||
field :anonymous, :boolean, default: false
|
field :anonymous, :boolean, default: false
|
||||||
field :hidden_from_users, :boolean, default: false
|
field :hidden_from_users, :boolean, default: false
|
||||||
field :approved, :boolean
|
|
||||||
|
|
||||||
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
timestamps(inserted_at: :created_at, type: :utc_datetime)
|
||||||
end
|
end
|
||||||
|
|
|
@ -273,9 +273,6 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do
|
||||||
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :hide, %Topic{}),
|
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :hide, %Topic{}),
|
||||||
do: true
|
do: true
|
||||||
|
|
||||||
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :approve, %Topic{}),
|
|
||||||
do: true
|
|
||||||
|
|
||||||
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :show, %Post{}),
|
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :show, %Post{}),
|
||||||
do: true
|
do: true
|
||||||
|
|
||||||
|
|
28
lib/philomena_web/controllers/admin/approval_controller.ex
Normal file
28
lib/philomena_web/controllers/admin/approval_controller.ex
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
defmodule PhilomenaWeb.Admin.ApprovalController do
|
||||||
|
use PhilomenaWeb, :controller
|
||||||
|
|
||||||
|
alias Philomena.Images.Image
|
||||||
|
alias Philomena.Repo
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
plug :verify_authorized
|
||||||
|
|
||||||
|
def index(conn, _params) do
|
||||||
|
images =
|
||||||
|
Image
|
||||||
|
|> where(hidden_from_users: false)
|
||||||
|
|> where(approved: false)
|
||||||
|
|> order_by(desc: :id)
|
||||||
|
|> preload([:user, tags: [:aliases, :aliased_tag]])
|
||||||
|
|> Repo.paginate(conn.assigns.scrivener)
|
||||||
|
|
||||||
|
render(conn, "index.html", title: "Admin - Approval Queue", images: images)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp verify_authorized(conn, _opts) do
|
||||||
|
case Canada.Can.can?(conn.assigns.current_user, :approve, %Image{}) do
|
||||||
|
true -> conn
|
||||||
|
false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -42,7 +42,10 @@ defmodule PhilomenaWeb.Admin.ReportController do
|
||||||
%{
|
%{
|
||||||
bool: %{
|
bool: %{
|
||||||
must: %{term: %{open: true}},
|
must: %{term: %{open: true}},
|
||||||
must_not: %{term: %{admin_id: user.id}}
|
must_not: [
|
||||||
|
%{term: %{admin_id: user.id}},
|
||||||
|
%{term: %{system: true}}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -59,11 +62,20 @@ defmodule PhilomenaWeb.Admin.ReportController do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|> Polymorphic.load_polymorphic(reportable: [reportable_id: :reportable_type])
|
|> Polymorphic.load_polymorphic(reportable: [reportable_id: :reportable_type])
|
||||||
|
|
||||||
|
system_reports =
|
||||||
|
Report
|
||||||
|
|> where(open: true, system: true)
|
||||||
|
|> preload([:admin, user: :linked_tags])
|
||||||
|
|> order_by(desc: :created_at)
|
||||||
|
|> Repo.all()
|
||||||
|
|> Polymorphic.load_polymorphic(reportable: [reportable_id: :reportable_type])
|
||||||
|
|
||||||
render(conn, "index.html",
|
render(conn, "index.html",
|
||||||
title: "Admin - Reports",
|
title: "Admin - Reports",
|
||||||
layout_class: "layout--wide",
|
layout_class: "layout--wide",
|
||||||
reports: reports,
|
reports: reports,
|
||||||
my_reports: my_reports
|
my_reports: my_reports,
|
||||||
|
system_reports: system_reports
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
defmodule PhilomenaWeb.Conversation.ApproveController do
|
defmodule PhilomenaWeb.Conversation.Message.ApproveController do
|
||||||
use PhilomenaWeb, :controller
|
use PhilomenaWeb, :controller
|
||||||
|
|
||||||
alias Philomena.Conversations.Conversation
|
alias Philomena.Conversations.Message
|
||||||
alias Philomena.Conversations
|
alias Philomena.Conversations
|
||||||
|
|
||||||
plug PhilomenaWeb.CanaryMapPlug, create: :approve
|
plug PhilomenaWeb.CanaryMapPlug, create: :approve
|
||||||
|
|
||||||
plug :load_and_authorize_resource,
|
plug :load_and_authorize_resource,
|
||||||
model: Conversation,
|
model: Message,
|
||||||
id_field: "slug",
|
id_name: "message_id",
|
||||||
id_name: "conversation_id",
|
persisted: true,
|
||||||
persisted: true
|
preload: [:conversation]
|
||||||
|
|
||||||
def create(conn, _params) do
|
def create(conn, _params) do
|
||||||
message = conn.assigns.message
|
message = conn.assigns.message
|
|
@ -20,7 +20,11 @@ defmodule PhilomenaWeb.Conversation.MessageController do
|
||||||
user = conn.assigns.current_user
|
user = conn.assigns.current_user
|
||||||
|
|
||||||
case Conversations.create_message(conversation, user, message_params) do
|
case Conversations.create_message(conversation, user, message_params) do
|
||||||
{:ok, _result} ->
|
{:ok, %{message: message}} ->
|
||||||
|
if not message.approved do
|
||||||
|
Conversations.report_non_approved(message.conversation_id)
|
||||||
|
end
|
||||||
|
|
||||||
count =
|
count =
|
||||||
Message
|
Message
|
||||||
|> where(conversation_id: ^conversation.id)
|
|> where(conversation_id: ^conversation.id)
|
||||||
|
|
|
@ -107,6 +107,11 @@ defmodule PhilomenaWeb.ConversationController do
|
||||||
|
|
||||||
case Conversations.create_conversation(user, conversation_params) do
|
case Conversations.create_conversation(user, conversation_params) do
|
||||||
{:ok, conversation} ->
|
{:ok, conversation} ->
|
||||||
|
if not hd(conversation.messages).approved do
|
||||||
|
Conversations.report_non_approved(conversation.id)
|
||||||
|
Conversations.set_as_read(conversation)
|
||||||
|
end
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, "Conversation successfully created.")
|
|> put_flash(:info, "Conversation successfully created.")
|
||||||
|> redirect(to: Routes.conversation_path(conn, :show, conversation))
|
|> redirect(to: Routes.conversation_path(conn, :show, conversation))
|
||||||
|
|
|
@ -15,7 +15,7 @@ defmodule PhilomenaWeb.Image.ApproveController do
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, "Image has been approved.")
|
|> put_flash(:info, "Image has been approved.")
|
||||||
|> moderation_log(details: &log_details/3, data: image)
|
|> moderation_log(details: &log_details/3, data: image)
|
||||||
|> redirect(to: Routes.image_path(conn, :show, image))
|
|> redirect(to: Routes.admin_approval_path(conn, :index))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp log_details(conn, _action, image) do
|
defp log_details(conn, _action, image) do
|
||||||
|
|
|
@ -78,11 +78,11 @@ defmodule PhilomenaWeb.Image.CommentController do
|
||||||
PhilomenaWeb.Api.Json.CommentView.render("show.json", %{comment: comment})
|
PhilomenaWeb.Api.Json.CommentView.render("show.json", %{comment: comment})
|
||||||
)
|
)
|
||||||
|
|
||||||
Comments.notify_comment(comment)
|
|
||||||
Comments.reindex_comment(comment)
|
Comments.reindex_comment(comment)
|
||||||
Images.reindex_image(conn.assigns.image)
|
Images.reindex_image(conn.assigns.image)
|
||||||
|
|
||||||
if comment.approved do
|
if comment.approved do
|
||||||
|
Comments.notify_comment(comment)
|
||||||
UserStatistics.inc_stat(conn.assigns.current_user, :comments_posted)
|
UserStatistics.inc_stat(conn.assigns.current_user, :comments_posted)
|
||||||
else
|
else
|
||||||
Comments.report_non_approved(comment)
|
Comments.report_non_approved(comment)
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
defmodule PhilomenaWeb.Topic.ApproveController do
|
|
||||||
import Plug.Conn
|
|
||||||
use PhilomenaWeb, :controller
|
|
||||||
|
|
||||||
alias Philomena.Forums.Forum
|
|
||||||
alias Philomena.Topics.Topic
|
|
||||||
alias Philomena.Topics
|
|
||||||
|
|
||||||
plug PhilomenaWeb.CanaryMapPlug, create: :show
|
|
||||||
|
|
||||||
plug :load_and_authorize_resource,
|
|
||||||
model: Forum,
|
|
||||||
id_name: "forum_id",
|
|
||||||
id_field: "short_name",
|
|
||||||
persisted: true
|
|
||||||
|
|
||||||
plug PhilomenaWeb.LoadTopicPlug
|
|
||||||
plug PhilomenaWeb.CanaryMapPlug, create: :approve
|
|
||||||
plug :authorize_resource, model: Topic, persisted: true
|
|
||||||
|
|
||||||
def create(conn, _params) do
|
|
||||||
topic = conn.assigns.topic
|
|
||||||
user = conn.assigns.current_user
|
|
||||||
|
|
||||||
case Topics.approve_topic(topic) do
|
|
||||||
{:ok, topic} ->
|
|
||||||
conn
|
|
||||||
|> put_flash(:info, "Topic successfully approved!")
|
|
||||||
|> moderation_log(details: &log_details/3, data: topic)
|
|
||||||
|> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic))
|
|
||||||
|
|
||||||
{:error, _changeset} ->
|
|
||||||
conn
|
|
||||||
|> put_flash(:error, "Unable to approve the topic!")
|
|
||||||
|> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp log_details(conn, action, topic) do
|
|
||||||
%{
|
|
||||||
body: "Approved topic '#{topic.title}' in #{topic.forum.name}",
|
|
||||||
subject_path: Routes.forum_topic_path(conn, :show, topic.forum, topic)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -18,8 +18,6 @@ defmodule PhilomenaWeb.Topic.Post.ApproveController do
|
||||||
|
|
||||||
case Posts.approve_post(post, user) do
|
case Posts.approve_post(post, user) do
|
||||||
{:ok, post} ->
|
{:ok, post} ->
|
||||||
UserStatistics.inc_stat(post.user(:forum_posts))
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, "Post successfully approved.")
|
|> put_flash(:info, "Post successfully approved.")
|
||||||
|> moderation_log(details: &log_details/3, data: post)
|
|> moderation_log(details: &log_details/3, data: post)
|
||||||
|
|
|
@ -35,9 +35,8 @@ defmodule PhilomenaWeb.Topic.PostController do
|
||||||
|
|
||||||
case Posts.create_post(topic, attributes, post_params) do
|
case Posts.create_post(topic, attributes, post_params) do
|
||||||
{:ok, %{post: post}} ->
|
{:ok, %{post: post}} ->
|
||||||
Posts.notify_post(post)
|
|
||||||
|
|
||||||
if post.approved do
|
if post.approved do
|
||||||
|
Posts.notify_post(post)
|
||||||
UserStatistics.inc_stat(conn.assigns.current_user, :forum_posts)
|
UserStatistics.inc_stat(conn.assigns.current_user, :forum_posts)
|
||||||
else
|
else
|
||||||
Posts.report_non_approved(post)
|
Posts.report_non_approved(post)
|
||||||
|
|
|
@ -63,6 +63,7 @@ defmodule PhilomenaWeb.ImageLoader do
|
||||||
]
|
]
|
||||||
|> maybe_show_deleted(show_hidden?, del)
|
|> maybe_show_deleted(show_hidden?, del)
|
||||||
|> maybe_custom_hide(user, hidden)
|
|> maybe_custom_hide(user, hidden)
|
||||||
|
|> hide_non_approved()
|
||||||
end
|
end
|
||||||
|
|
||||||
# Allow moderators to index hidden images
|
# Allow moderators to index hidden images
|
||||||
|
@ -94,6 +95,10 @@ defmodule PhilomenaWeb.ImageLoader do
|
||||||
defp maybe_custom_hide(filters, _user, _param),
|
defp maybe_custom_hide(filters, _user, _param),
|
||||||
do: filters
|
do: filters
|
||||||
|
|
||||||
|
# Hide all images that aren't approved from all search queries.
|
||||||
|
defp hide_non_approved(filters),
|
||||||
|
do: [%{term: %{approved: false}} | filters]
|
||||||
|
|
||||||
# TODO: the search parser should try to optimize queries
|
# TODO: the search parser should try to optimize queries
|
||||||
defp search_tag_name(%{term: %{"namespaced_tags.name" => tag_name}}), do: [tag_name]
|
defp search_tag_name(%{term: %{"namespaced_tags.name" => tag_name}}), do: [tag_name]
|
||||||
defp search_tag_name(_other_query), do: []
|
defp search_tag_name(_other_query), do: []
|
||||||
|
|
|
@ -75,7 +75,7 @@ defmodule PhilomenaWeb.ImageSorter do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp parse_sf(_params, sd, query) do
|
defp parse_sf(_params, sd, query) do
|
||||||
%{query: query, sorts: [%{"id" => sd}]}
|
%{query: query, sorts: [%{"first_seen_at" => sd}]}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp random_query(seed, sd, query) do
|
defp random_query(seed, sd, query) do
|
||||||
|
|
|
@ -58,6 +58,9 @@ defmodule PhilomenaWeb.MarkdownRenderer do
|
||||||
image.hidden_from_users ->
|
image.hidden_from_users ->
|
||||||
" (deleted)"
|
" (deleted)"
|
||||||
|
|
||||||
|
not image.approved ->
|
||||||
|
" (pending approval)"
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
""
|
""
|
||||||
end
|
end
|
||||||
|
@ -75,7 +78,7 @@ defmodule PhilomenaWeb.MarkdownRenderer do
|
||||||
cond do
|
cond do
|
||||||
img != nil ->
|
img != nil ->
|
||||||
case group do
|
case group do
|
||||||
[_id, "p"] when not img.hidden_from_users ->
|
[_id, "p"] when not img.hidden_from_users and img.approved ->
|
||||||
Phoenix.View.render(@image_view, "_image_target.html",
|
Phoenix.View.render(@image_view, "_image_target.html",
|
||||||
image: img,
|
image: img,
|
||||||
size: :medium,
|
size: :medium,
|
||||||
|
@ -83,7 +86,7 @@ defmodule PhilomenaWeb.MarkdownRenderer do
|
||||||
)
|
)
|
||||||
|> safe_to_string()
|
|> safe_to_string()
|
||||||
|
|
||||||
[_id, "t"] when not img.hidden_from_users ->
|
[_id, "t"] when not img.hidden_from_users and img.approved ->
|
||||||
Phoenix.View.render(@image_view, "_image_target.html",
|
Phoenix.View.render(@image_view, "_image_target.html",
|
||||||
image: img,
|
image: img,
|
||||||
size: :small,
|
size: :small,
|
||||||
|
@ -91,7 +94,7 @@ defmodule PhilomenaWeb.MarkdownRenderer do
|
||||||
)
|
)
|
||||||
|> safe_to_string()
|
|> safe_to_string()
|
||||||
|
|
||||||
[_id, "s"] when not img.hidden_from_users ->
|
[_id, "s"] when not img.hidden_from_users and img.approved ->
|
||||||
Phoenix.View.render(@image_view, "_image_target.html",
|
Phoenix.View.render(@image_view, "_image_target.html",
|
||||||
image: img,
|
image: img,
|
||||||
size: :thumb_small,
|
size: :thumb_small,
|
||||||
|
@ -99,6 +102,9 @@ defmodule PhilomenaWeb.MarkdownRenderer do
|
||||||
)
|
)
|
||||||
|> safe_to_string()
|
|> safe_to_string()
|
||||||
|
|
||||||
|
[id, suffix] when not img.approved ->
|
||||||
|
">>#{img.id}#{suffix}#{link_suffix(img)}"
|
||||||
|
|
||||||
[_id, ""] ->
|
[_id, ""] ->
|
||||||
link(">>#{img.id}#{link_suffix(img)}", to: "/images/#{img.id}")
|
link(">>#{img.id}#{link_suffix(img)}", to: "/images/#{img.id}")
|
||||||
|> safe_to_string()
|
|> safe_to_string()
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule PhilomenaWeb.AdminCountersPlug do
|
||||||
alias Philomena.Reports
|
alias Philomena.Reports
|
||||||
alias Philomena.ArtistLinks
|
alias Philomena.ArtistLinks
|
||||||
alias Philomena.DnpEntries
|
alias Philomena.DnpEntries
|
||||||
|
alias Philomena.Images
|
||||||
|
|
||||||
import Plug.Conn, only: [assign: 3]
|
import Plug.Conn, only: [assign: 3]
|
||||||
|
|
||||||
|
@ -31,12 +32,14 @@ defmodule PhilomenaWeb.AdminCountersPlug do
|
||||||
defp maybe_assign_admin_metrics(conn, _user, false), do: conn
|
defp maybe_assign_admin_metrics(conn, _user, false), do: conn
|
||||||
|
|
||||||
defp maybe_assign_admin_metrics(conn, user, true) do
|
defp maybe_assign_admin_metrics(conn, user, true) do
|
||||||
|
pending_approvals = Images.count_pending_approvals()
|
||||||
duplicate_reports = DuplicateReports.count_duplicate_reports(user)
|
duplicate_reports = DuplicateReports.count_duplicate_reports(user)
|
||||||
reports = Reports.count_reports(user)
|
reports = Reports.count_reports(user)
|
||||||
artist_links = ArtistLinks.count_artist_links(user)
|
artist_links = ArtistLinks.count_artist_links(user)
|
||||||
dnps = DnpEntries.count_dnp_entries(user)
|
dnps = DnpEntries.count_dnp_entries(user)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|> assign(:pending_approval_count, pending_approvals)
|
||||||
|> assign(:duplicate_report_count, duplicate_reports)
|
|> assign(:duplicate_report_count, duplicate_reports)
|
||||||
|> assign(:report_count, reports)
|
|> assign(:report_count, reports)
|
||||||
|> assign(:artist_link_count, artist_links)
|
|> assign(:artist_link_count, artist_links)
|
||||||
|
|
|
@ -177,10 +177,15 @@ defmodule PhilomenaWeb.Router do
|
||||||
|
|
||||||
resources "/conversations", ConversationController, only: [:index, :show, :new, :create] do
|
resources "/conversations", ConversationController, only: [:index, :show, :new, :create] do
|
||||||
resources "/reports", Conversation.ReportController, only: [:new, :create]
|
resources "/reports", Conversation.ReportController, only: [:new, :create]
|
||||||
resources "/messages", Conversation.MessageController, only: [:create]
|
|
||||||
|
resources "/messages", Conversation.MessageController, only: [:create] do
|
||||||
|
resources "/approve", Conversation.Message.ApproveController,
|
||||||
|
only: [:create],
|
||||||
|
singleton: true
|
||||||
|
end
|
||||||
|
|
||||||
resources "/read", Conversation.ReadController, only: [:create, :delete], singleton: true
|
resources "/read", Conversation.ReadController, only: [:create, :delete], singleton: true
|
||||||
resources "/hide", Conversation.HideController, only: [:create, :delete], singleton: true
|
resources "/hide", Conversation.HideController, only: [:create, :delete], singleton: true
|
||||||
resources "/approve", Conversation.ApproveController, only: [:create], singleton: true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
resources "/images", ImageController, only: [] do
|
resources "/images", ImageController, only: [] do
|
||||||
|
@ -240,7 +245,6 @@ defmodule PhilomenaWeb.Router do
|
||||||
resources "/stick", Topic.StickController, only: [:create, :delete], singleton: true
|
resources "/stick", Topic.StickController, only: [:create, :delete], singleton: true
|
||||||
resources "/lock", Topic.LockController, only: [:create, :delete], singleton: true
|
resources "/lock", Topic.LockController, only: [:create, :delete], singleton: true
|
||||||
resources "/hide", Topic.HideController, only: [:create, :delete], singleton: true
|
resources "/hide", Topic.HideController, only: [:create, :delete], singleton: true
|
||||||
resources "/approve", Topic.ApproveController, only: [:create], singleton: true
|
|
||||||
|
|
||||||
resources "/posts", Topic.PostController, only: [:edit, :update] do
|
resources "/posts", Topic.PostController, only: [:edit, :update] do
|
||||||
resources "/hide", Topic.Post.HideController, only: [:create, :delete], singleton: true
|
resources "/hide", Topic.Post.HideController, only: [:create, :delete], singleton: true
|
||||||
|
@ -340,6 +344,8 @@ defmodule PhilomenaWeb.Router do
|
||||||
resources "/close", Report.CloseController, only: [:create], singleton: true
|
resources "/close", Report.CloseController, only: [:create], singleton: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources "/approvals", ApprovalController, only: [:index]
|
||||||
|
|
||||||
resources "/artist_links", ArtistLinkController, only: [:index] do
|
resources "/artist_links", ArtistLinkController, only: [:index] do
|
||||||
resources "/verification", ArtistLink.VerificationController,
|
resources "/verification", ArtistLink.VerificationController,
|
||||||
only: [:create],
|
only: [:create],
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
.block
|
||||||
|
.block__header
|
||||||
|
.block__header__title.approval-grid
|
||||||
|
.approval-items--main
|
||||||
|
span ID
|
||||||
|
span Image
|
||||||
|
span Uploader
|
||||||
|
span Time
|
||||||
|
.approval-items--footer.hide-mobile
|
||||||
|
span.hide-mobile Actions
|
||||||
|
= for image <- @images do
|
||||||
|
.block__content.alternating-color
|
||||||
|
.approval-grid
|
||||||
|
.approval-items--main
|
||||||
|
span = link ">>#{image.id}", to: Routes.image_path(@conn, :show, image)
|
||||||
|
span = image_thumb(@conn, image)
|
||||||
|
span
|
||||||
|
= if image.user do
|
||||||
|
= link image.user.name, to: Routes.profile_path(@conn, :show, image.user)
|
||||||
|
- else
|
||||||
|
em>
|
||||||
|
= truncated_ip_link(@conn, image.ip)
|
||||||
|
= link_to_fingerprint(@conn, image.fingerprint)
|
||||||
|
span = pretty_time(image.created_at)
|
||||||
|
.approval-items--footer
|
||||||
|
= if can?(@conn, :approve, image) do
|
||||||
|
= button_to "Approve", Routes.image_approve_path(@conn, :create, image), method: "post", class: "button button--state-success"
|
||||||
|
= if can?(@conn, :hide, image) do
|
||||||
|
= form_for :image, Routes.image_delete_path(@conn, :create, image), [method: "post"], fn f ->
|
||||||
|
.field.field--inline
|
||||||
|
= text_input f, :deletion_reason, class: "input input--wide", placeholder: "Rule violation", required: true
|
||||||
|
= submit "Delete", class: "button button--state-danger button--separate-left"
|
16
lib/philomena_web/templates/admin/approval/index.html.slime
Normal file
16
lib/philomena_web/templates/admin/approval/index.html.slime
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
- route = fn p -> Routes.admin_approval_path(@conn, :index, p) end
|
||||||
|
- pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @images, route: route
|
||||||
|
|
||||||
|
h1 Approval Queue
|
||||||
|
|
||||||
|
.block
|
||||||
|
.block__header
|
||||||
|
= pagination
|
||||||
|
|
||||||
|
= if Enum.any?(@images) do
|
||||||
|
= render PhilomenaWeb.Admin.ApprovalView, "_approvals.html", images: @images, conn: @conn
|
||||||
|
- else
|
||||||
|
' No images are pending approval. Good job!
|
||||||
|
|
||||||
|
.block__header.block__header--light
|
||||||
|
= pagination
|
|
@ -10,6 +10,13 @@ h1 Reports
|
||||||
.block__content
|
.block__content
|
||||||
= render PhilomenaWeb.Admin.ReportView, "_reports.html", reports: @my_reports, conn: @conn
|
= render PhilomenaWeb.Admin.ReportView, "_reports.html", reports: @my_reports, conn: @conn
|
||||||
|
|
||||||
|
= if Enum.any?(@system_reports) do
|
||||||
|
.block
|
||||||
|
.block__header.block--danger
|
||||||
|
span.block__header__title System Reports
|
||||||
|
.block__content
|
||||||
|
= render PhilomenaWeb.Admin.ReportView, "_reports.html", reports: @system_reports, conn: @conn
|
||||||
|
|
||||||
.block
|
.block
|
||||||
.block__header
|
.block__header
|
||||||
span.block__header__title All Reports
|
span.block__header__title All Reports
|
||||||
|
|
|
@ -5,6 +5,21 @@ h1 New Conversation
|
||||||
' »
|
' »
|
||||||
span.block__header__title New Conversation
|
span.block__header__title New Conversation
|
||||||
|
|
||||||
|
= case DateTime.compare(DateTime.utc_now(), DateTime.add(@conn.assigns.current_user.created_at, 1_209_600)) do
|
||||||
|
- :lt ->
|
||||||
|
.block.block--fixed.block--warning.hidden.js-hidden-warning
|
||||||
|
h2 Warning!
|
||||||
|
p
|
||||||
|
strong> Your account is too new, so your PM will need to be reviewed by staff members.
|
||||||
|
' This is because it contains an external image. If you are not okay with a moderator viewing this PM conversation, please consider linking the image instead of embedding it (change
|
||||||
|
code<> ![
|
||||||
|
' to
|
||||||
|
code<
|
||||||
|
| [
|
||||||
|
| ).
|
||||||
|
- _ ->
|
||||||
|
/ Nothing
|
||||||
|
|
||||||
= form_for @changeset, Routes.conversation_path(@conn, :create), fn f ->
|
= form_for @changeset, Routes.conversation_path(@conn, :create), fn f ->
|
||||||
= if @changeset.action do
|
= if @changeset.action do
|
||||||
.alert.alert-danger
|
.alert.alert-danger
|
||||||
|
|
|
@ -31,6 +31,21 @@ h1 = @conversation.title
|
||||||
.block__header.block__header--light.page__header
|
.block__header.block__header--light.page__header
|
||||||
.page__pagination = pagination
|
.page__pagination = pagination
|
||||||
|
|
||||||
|
= case DateTime.compare(DateTime.utc_now(), DateTime.add(@conn.assigns.current_user.created_at, 1_209_600)) do
|
||||||
|
- :lt ->
|
||||||
|
.block.block--fixed.block--warning.hidden.js-hidden-warning
|
||||||
|
h2 Warning!
|
||||||
|
p
|
||||||
|
strong> Your account is too new, so your PM will need to be reviewed by staff members.
|
||||||
|
' This is because it contains an external image. If you are not okay with a moderator viewing this PM conversation, please consider linking the image instead of embedding it (change
|
||||||
|
code<> ![
|
||||||
|
' to
|
||||||
|
code<
|
||||||
|
| [
|
||||||
|
| ).
|
||||||
|
- _ ->
|
||||||
|
/ Nothing
|
||||||
|
|
||||||
= cond do
|
= cond do
|
||||||
- @conn.assigns.current_ban ->
|
- @conn.assigns.current_ban ->
|
||||||
= render PhilomenaWeb.BanView, "_ban_reason.html", conn: @conn
|
= render PhilomenaWeb.BanView, "_ban_reason.html", conn: @conn
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
= if not @image.approved and not @image.hidden_from_users do
|
||||||
|
.block.block--fixed.block--warning
|
||||||
|
h2 Hold up!
|
||||||
|
p This image is pending approval from a staff member. It will appear on the site once it's reviewed and approved.
|
||||||
|
p
|
||||||
|
' Don't worry,
|
||||||
|
strong
|
||||||
|
' the image will not lose any viewership,
|
||||||
|
' it will appear on the homepage and in search results as normal (as if it was uploaded at the time of approval).
|
||||||
|
p
|
||||||
|
a href="/pages/approval"
|
||||||
|
strong Click here to learn more about image approval and verification.
|
|
@ -145,5 +145,7 @@
|
||||||
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"
|
= link "Lock specific tags", to: Routes.image_tag_lock_path(@conn, :show, @image), class: "button"
|
||||||
|
= if not @image.approved and can?(@conn, :approve, @image) do
|
||||||
|
= button_to "Approve image", Routes.image_approve_path(@conn, :create, @image), method: "post", class: "button button--state-success", data: [confirm: "Are you sure?"]
|
||||||
= if @image.hidden_from_users and can?(@conn, :destroy, @image) do
|
= 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?"]
|
= 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?"]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
= render PhilomenaWeb.ImageView, "_image_approval_banner.html", image: @image, conn: @conn
|
||||||
= render PhilomenaWeb.ImageView, "_image_meta.html", image: @image, watching: @watching, user_galleries: @user_galleries, changeset: @image_changeset, conn: @conn
|
= render PhilomenaWeb.ImageView, "_image_meta.html", image: @image, watching: @watching, user_galleries: @user_galleries, changeset: @image_changeset, conn: @conn
|
||||||
= render PhilomenaWeb.ImageView, "_image_page.html", image: @image, conn: @conn
|
= render PhilomenaWeb.ImageView, "_image_page.html", image: @image, conn: @conn
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,12 @@
|
||||||
i.fa.fa-fw.fa-list-alt>
|
i.fa.fa-fw.fa-list-alt>
|
||||||
' Mod Logs
|
' Mod Logs
|
||||||
|
|
||||||
|
= if @pending_approval_count do
|
||||||
|
= link to: Routes.admin_approval_path(@conn, :index), class: "header__link", title: "Approval Queue" do
|
||||||
|
' Q
|
||||||
|
span.header__counter__admin
|
||||||
|
= @pending_approval_count
|
||||||
|
|
||||||
= if @duplicate_report_count do
|
= if @duplicate_report_count do
|
||||||
= link to: Routes.duplicate_report_path(@conn, :index), class: "header__link", title: "Duplicates" do
|
= link to: Routes.duplicate_report_path(@conn, :index), class: "header__link", title: "Duplicates" do
|
||||||
' D
|
' D
|
||||||
|
|
|
@ -1,4 +1,18 @@
|
||||||
article.block.communication
|
article.block.communication
|
||||||
|
= if not @message.approved and (can?(@conn, :approve, @message) or @message.from_id == @conn.assigns.current_user.id) do
|
||||||
|
.block__content
|
||||||
|
.block.block--fixed.block--danger
|
||||||
|
p
|
||||||
|
i.fas.fa-exclamation-triangle>
|
||||||
|
' This private message is pending approval from a staff member.
|
||||||
|
= if can?(@conn, :approve, @message) do
|
||||||
|
p
|
||||||
|
ul.horizontal-list
|
||||||
|
li
|
||||||
|
= link(to: Routes.conversation_message_approve_path(@conn, :create, @message.conversation_id, @message), data: [confirm: "Are you sure?"], method: "post", class: "button") do
|
||||||
|
i.fas.fa-check>
|
||||||
|
' Approve
|
||||||
|
|
||||||
.block__content.flex.flex--no-wrap
|
.block__content.flex.flex--no-wrap
|
||||||
.flex__fixed.spacing-right
|
.flex__fixed.spacing-right
|
||||||
= render PhilomenaWeb.UserAttributionView, "_user_avatar.html", object: %{user: @message.from}, conn: @conn, class: "avatar--100px"
|
= render PhilomenaWeb.UserAttributionView, "_user_avatar.html", object: %{user: @message.from}, conn: @conn, class: "avatar--100px"
|
||||||
|
|
|
@ -133,9 +133,9 @@ h1 Search
|
||||||
end
|
end
|
||||||
|
|
||||||
sort_fields = [
|
sort_fields = [
|
||||||
|
"Sort by initial post date": :first_seen_at,
|
||||||
"Sort by image ID": :id,
|
"Sort by image ID": :id,
|
||||||
"Sort by last modification date": :updated_at,
|
"Sort by last modification date": :updated_at,
|
||||||
"Sort by initial post date": :first_seen_at,
|
|
||||||
"Sort by aspect ratio": :aspect_ratio,
|
"Sort by aspect ratio": :aspect_ratio,
|
||||||
"Sort by fave count": :faves,
|
"Sort by fave count": :faves,
|
||||||
"Sort by upvotes": :upvotes,
|
"Sort by upvotes": :upvotes,
|
||||||
|
|
16
lib/philomena_web/views/admin/approval_view.ex
Normal file
16
lib/philomena_web/views/admin/approval_view.ex
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
defmodule PhilomenaWeb.Admin.ApprovalView do
|
||||||
|
use PhilomenaWeb, :view
|
||||||
|
|
||||||
|
alias PhilomenaWeb.Admin.ReportView
|
||||||
|
|
||||||
|
# Shamelessly copied from ReportView
|
||||||
|
def truncated_ip_link(conn, ip), do: ReportView.truncated_ip_link(conn, ip)
|
||||||
|
|
||||||
|
def image_thumb(conn, image) do
|
||||||
|
render(PhilomenaWeb.ImageView, "_image_container.html",
|
||||||
|
image: image,
|
||||||
|
size: :thumb_tiny,
|
||||||
|
conn: conn
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -18,12 +18,18 @@ defmodule Philomena.Repo.Migrations.AddApprovalQueue do
|
||||||
add :approved, :boolean, default: false
|
add :approved, :boolean, default: false
|
||||||
end
|
end
|
||||||
|
|
||||||
alter table("topics") do
|
alter table("messages") do
|
||||||
add :approved, :boolean, default: false
|
add :approved, :boolean, default: false
|
||||||
end
|
end
|
||||||
|
|
||||||
alter table("users") do
|
alter table("users") do
|
||||||
add :verified, :boolean, default: false
|
add :verified, :boolean, default: false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create index(:images, [:hidden_from_users, :approved],
|
||||||
|
where: "hidden_from_users = false and approved = false"
|
||||||
|
)
|
||||||
|
|
||||||
|
create index(:reports, [:system], where: "system = true")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1007,7 +1007,8 @@ CREATE TABLE public.messages (
|
||||||
updated_at timestamp without time zone NOT NULL,
|
updated_at timestamp without time zone NOT NULL,
|
||||||
from_id integer NOT NULL,
|
from_id integer NOT NULL,
|
||||||
conversation_id integer NOT NULL,
|
conversation_id integer NOT NULL,
|
||||||
body character varying NOT NULL
|
body character varying NOT NULL,
|
||||||
|
approved boolean DEFAULT false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -1680,8 +1681,7 @@ CREATE TABLE public.topics (
|
||||||
deleted_by_id integer,
|
deleted_by_id integer,
|
||||||
locked_by_id integer,
|
locked_by_id integer,
|
||||||
last_post_id integer,
|
last_post_id integer,
|
||||||
hidden_from_users boolean DEFAULT false NOT NULL,
|
hidden_from_users boolean DEFAULT false NOT NULL
|
||||||
approved boolean DEFAULT false
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -2903,6 +2903,13 @@ CREATE UNIQUE INDEX image_tag_locks_image_id_tag_id_index ON public.image_tag_lo
|
||||||
CREATE INDEX image_tag_locks_tag_id_index ON public.image_tag_locks USING btree (tag_id);
|
CREATE INDEX image_tag_locks_tag_id_index ON public.image_tag_locks USING btree (tag_id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: images_hidden_from_users_approved_index; Type: INDEX; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE INDEX images_hidden_from_users_approved_index ON public.images USING btree (hidden_from_users, approved) WHERE ((hidden_from_users = false) AND (approved = false));
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: index_adverts_on_restrictions; Type: INDEX; Schema: public; Owner: -
|
-- Name: index_adverts_on_restrictions; Type: INDEX; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
@ -4100,6 +4107,13 @@ CREATE INDEX moderation_logs_user_id_created_at_index ON public.moderation_logs
|
||||||
CREATE INDEX moderation_logs_user_id_index ON public.moderation_logs USING btree (user_id);
|
CREATE INDEX moderation_logs_user_id_index ON public.moderation_logs USING btree (user_id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: reports_system_index; Type: INDEX; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE INDEX reports_system_index ON public.reports USING btree (system) WHERE (system = true);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: user_tokens_context_token_index; Type: INDEX; Schema: public; Owner: -
|
-- Name: user_tokens_context_token_index; Type: INDEX; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
|
Loading…
Reference in a new issue