mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-27 05:37:59 +01:00
run formatter
This commit is contained in:
parent
4ac63f9f4e
commit
ed44160603
368 changed files with 3392 additions and 1388 deletions
12
.iex.exs
12
.iex.exs
|
@ -1,3 +1,13 @@
|
||||||
alias Philomena.{Repo, Comments.Comment, Galleries.Gallery, Posts.Post, Images.Image, Topics.Topic, Tags.Tag, Users.User}
|
alias Philomena.{
|
||||||
|
Repo,
|
||||||
|
Comments.Comment,
|
||||||
|
Galleries.Gallery,
|
||||||
|
Posts.Post,
|
||||||
|
Images.Image,
|
||||||
|
Topics.Topic,
|
||||||
|
Tags.Tag,
|
||||||
|
Users.User
|
||||||
|
}
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
|
@ -68,11 +68,9 @@ config :logger, :console, format: "[$level] $message\n"
|
||||||
config :logger, compile_time_purge_matching: [[application: :remote_ip]]
|
config :logger, compile_time_purge_matching: [[application: :remote_ip]]
|
||||||
|
|
||||||
# Set up mailer
|
# Set up mailer
|
||||||
config :philomena, PhilomenaWeb.Mailer,
|
config :philomena, PhilomenaWeb.Mailer, adapter: Bamboo.LocalAdapter
|
||||||
adapter: Bamboo.LocalAdapter
|
|
||||||
|
|
||||||
config :philomena, :mailer_address,
|
config :philomena, :mailer_address, "noreply@philomena.lc"
|
||||||
"noreply@philomena.lc"
|
|
||||||
|
|
||||||
# Use this to debug slime templates
|
# Use this to debug slime templates
|
||||||
# config :slime, :keep_lines, true
|
# config :slime, :keep_lines, true
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Camo.Image do
|
||||||
input
|
input
|
||||||
else
|
else
|
||||||
camo_digest = :crypto.hmac(:sha, camo_key(), input) |> Base.encode16(case: :lower)
|
camo_digest = :crypto.hmac(:sha, camo_key(), input) |> Base.encode16(case: :lower)
|
||||||
|
|
||||||
camo_uri = %URI{
|
camo_uri = %URI{
|
||||||
host: camo_host(),
|
host: camo_host(),
|
||||||
path: "/" <> camo_digest,
|
path: "/" <> camo_digest,
|
||||||
|
|
|
@ -2,7 +2,16 @@ defmodule Mix.Tasks.ReindexAll do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
|
||||||
alias Philomena.Elasticsearch
|
alias Philomena.Elasticsearch
|
||||||
alias Philomena.{Comments.Comment, Galleries.Gallery, Posts.Post, Images.Image, Reports.Report, Tags.Tag}
|
|
||||||
|
alias Philomena.{
|
||||||
|
Comments.Comment,
|
||||||
|
Galleries.Gallery,
|
||||||
|
Posts.Post,
|
||||||
|
Images.Image,
|
||||||
|
Reports.Report,
|
||||||
|
Tags.Tag
|
||||||
|
}
|
||||||
|
|
||||||
alias Philomena.{Comments, Galleries, Posts, Images, Tags}
|
alias Philomena.{Comments, Galleries, Posts, Images, Tags}
|
||||||
alias Philomena.Polymorphic
|
alias Philomena.Polymorphic
|
||||||
alias Philomena.Repo
|
alias Philomena.Repo
|
||||||
|
@ -10,13 +19,19 @@ defmodule Mix.Tasks.ReindexAll do
|
||||||
|
|
||||||
@shortdoc "Destroys and recreates all Elasticsearch indices."
|
@shortdoc "Destroys and recreates all Elasticsearch indices."
|
||||||
def run(_) do
|
def run(_) do
|
||||||
if Mix.env == "prod" do
|
if Mix.env() == "prod" do
|
||||||
raise "do not run this task in production"
|
raise "do not run this task in production"
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok, _apps} = Application.ensure_all_started(:philomena)
|
{:ok, _apps} = Application.ensure_all_started(:philomena)
|
||||||
|
|
||||||
for {context, schema} <- [{Images, Image}, {Comments, Comment}, {Galleries, Gallery}, {Tags, Tag}, {Posts, Post}] do
|
for {context, schema} <- [
|
||||||
|
{Images, Image},
|
||||||
|
{Comments, Comment},
|
||||||
|
{Galleries, Gallery},
|
||||||
|
{Tags, Tag},
|
||||||
|
{Posts, Post}
|
||||||
|
] do
|
||||||
Elasticsearch.delete_index!(schema)
|
Elasticsearch.delete_index!(schema)
|
||||||
Elasticsearch.create_index!(schema)
|
Elasticsearch.create_index!(schema)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ defmodule Philomena.Adverts do
|
||||||
alias Philomena.Adverts.Advert
|
alias Philomena.Adverts.Advert
|
||||||
alias Philomena.Adverts.Uploader
|
alias Philomena.Adverts.Uploader
|
||||||
|
|
||||||
|
|
||||||
def random_live do
|
def random_live do
|
||||||
now = DateTime.utc_now()
|
now = DateTime.utc_now()
|
||||||
|
|
||||||
|
@ -38,18 +37,19 @@ defmodule Philomena.Adverts do
|
||||||
end
|
end
|
||||||
|
|
||||||
def click(%Advert{} = advert) do
|
def click(%Advert{} = advert) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
query = where(Advert, id: ^advert.id)
|
query = where(Advert, id: ^advert.id)
|
||||||
Repo.update_all(query, inc: [clicks: 1])
|
Repo.update_all(query, inc: [clicks: 1])
|
||||||
end
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp record_impression(nil), do: nil
|
defp record_impression(nil), do: nil
|
||||||
|
|
||||||
defp record_impression(advert) do
|
defp record_impression(advert) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
query = where(Advert, id: ^advert.id)
|
query = where(Advert, id: ^advert.id)
|
||||||
Repo.update_all(query, inc: [impressions: 1])
|
Repo.update_all(query, inc: [impressions: 1])
|
||||||
end
|
end)
|
||||||
|
|
||||||
advert
|
advert
|
||||||
end
|
end
|
||||||
|
|
|
@ -50,8 +50,13 @@ defmodule Philomena.Adverts.Advert do
|
||||||
def image_changeset(advert, attrs) do
|
def image_changeset(advert, attrs) do
|
||||||
advert
|
advert
|
||||||
|> cast(attrs, [
|
|> cast(attrs, [
|
||||||
:image, :image_mime_type, :image_size, :image_width, :image_height,
|
:image,
|
||||||
:uploaded_image, :removed_image
|
:image_mime_type,
|
||||||
|
:image_size,
|
||||||
|
:image_width,
|
||||||
|
:image_height,
|
||||||
|
:uploaded_image,
|
||||||
|
:removed_image
|
||||||
])
|
])
|
||||||
|> validate_required([:image])
|
|> validate_required([:image])
|
||||||
|> validate_inclusion(:image_mime_type, ["image/png", "image/jpeg", "image/gif"])
|
|> validate_inclusion(:image_mime_type, ["image/png", "image/jpeg", "image/gif"])
|
||||||
|
|
|
@ -41,15 +41,16 @@ defmodule Philomena.Analyzers do
|
||||||
"""
|
"""
|
||||||
@spec analyze(Plug.Upload.t() | String.t()) :: {:ok, map()} | :error
|
@spec analyze(Plug.Upload.t() | String.t()) :: {:ok, map()} | :error
|
||||||
def analyze(%Plug.Upload{path: path}), do: analyze(path)
|
def analyze(%Plug.Upload{path: path}), do: analyze(path)
|
||||||
|
|
||||||
def analyze(path) when is_binary(path) do
|
def analyze(path) when is_binary(path) do
|
||||||
with {:ok, mime} <- Mime.file(path),
|
with {:ok, mime} <- Mime.file(path),
|
||||||
{:ok, analyzer} <- analyzer(mime)
|
{:ok, analyzer} <- analyzer(mime) do
|
||||||
do
|
|
||||||
{:ok, analyzer.analyze(path)}
|
{:ok, analyzer.analyze(path)}
|
||||||
else
|
else
|
||||||
error ->
|
error ->
|
||||||
error
|
error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def analyze(_path), do: :error
|
def analyze(_path), do: :error
|
||||||
end
|
end
|
|
@ -29,10 +29,20 @@ defmodule Philomena.Analyzers.Gif do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp duration(false, _file), do: 0.0
|
defp duration(false, _file), do: 0.0
|
||||||
|
|
||||||
defp duration(true, file) do
|
defp duration(true, file) do
|
||||||
with {output, 0} <- System.cmd("ffprobe", ["-i", file, "-show_entries", "format=duration", "-v", "quiet", "-of", "csv=p=0"]),
|
with {output, 0} <-
|
||||||
{duration, _} <- Float.parse(output)
|
System.cmd("ffprobe", [
|
||||||
do
|
"-i",
|
||||||
|
file,
|
||||||
|
"-show_entries",
|
||||||
|
"format=duration",
|
||||||
|
"-v",
|
||||||
|
"quiet",
|
||||||
|
"-of",
|
||||||
|
"csv=p=0"
|
||||||
|
]),
|
||||||
|
{duration, _} <- Float.parse(output) do
|
||||||
duration
|
duration
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
|
|
@ -13,7 +13,16 @@ defmodule Philomena.Analyzers.Png do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp animated?(file) do
|
defp animated?(file) do
|
||||||
System.cmd("ffprobe", ["-i", file, "-v", "quiet", "-show_entries", "stream=codec_name", "-of", "csv=p=0"])
|
System.cmd("ffprobe", [
|
||||||
|
"-i",
|
||||||
|
file,
|
||||||
|
"-v",
|
||||||
|
"quiet",
|
||||||
|
"-show_entries",
|
||||||
|
"stream=codec_name",
|
||||||
|
"-of",
|
||||||
|
"csv=p=0"
|
||||||
|
])
|
||||||
|> case do
|
|> case do
|
||||||
{"apng\n", 0} ->
|
{"apng\n", 0} ->
|
||||||
true
|
true
|
||||||
|
|
|
@ -10,9 +10,18 @@ defmodule Philomena.Analyzers.Webm do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp duration(file) do
|
defp duration(file) do
|
||||||
with {output, 0} <- System.cmd("ffprobe", ["-i", file, "-show_entries", "format=duration", "-v", "quiet", "-of", "csv=p=0"]),
|
with {output, 0} <-
|
||||||
{duration, _} <- Float.parse(output)
|
System.cmd("ffprobe", [
|
||||||
do
|
"-i",
|
||||||
|
file,
|
||||||
|
"-show_entries",
|
||||||
|
"format=duration",
|
||||||
|
"-v",
|
||||||
|
"quiet",
|
||||||
|
"-of",
|
||||||
|
"csv=p=0"
|
||||||
|
]),
|
||||||
|
{duration, _} <- Float.parse(output) do
|
||||||
duration
|
duration
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -21,7 +30,16 @@ defmodule Philomena.Analyzers.Webm do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp dimensions(file) do
|
defp dimensions(file) do
|
||||||
System.cmd("ffprobe", ["-i", file, "-show_entries", "stream=width,height", "-v", "quiet", "-of", "csv=p=0"])
|
System.cmd("ffprobe", [
|
||||||
|
"-i",
|
||||||
|
file,
|
||||||
|
"-show_entries",
|
||||||
|
"stream=width,height",
|
||||||
|
"-v",
|
||||||
|
"quiet",
|
||||||
|
"-of",
|
||||||
|
"csv=p=0"
|
||||||
|
])
|
||||||
|> case do
|
|> case do
|
||||||
{output, 0} ->
|
{output, 0} ->
|
||||||
[width, height] =
|
[width, height] =
|
||||||
|
|
|
@ -30,5 +30,6 @@ defmodule Philomena.Badges.Award do
|
||||||
|
|
||||||
put_change(changeset, :awarded_on, now)
|
put_change(changeset, :awarded_on, now)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp put_awarded_on(changeset), do: changeset
|
defp put_awarded_on(changeset), do: changeset
|
||||||
end
|
end
|
||||||
|
|
|
@ -331,10 +331,11 @@ defmodule Philomena.Bans do
|
||||||
"""
|
"""
|
||||||
def exists_for?(user, ip, fingerprint) do
|
def exists_for?(user, ip, fingerprint) do
|
||||||
now = DateTime.utc_now()
|
now = DateTime.utc_now()
|
||||||
|
|
||||||
queries =
|
queries =
|
||||||
subnet_query(ip, now) ++
|
subnet_query(ip, now) ++
|
||||||
fingerprint_query(fingerprint, now) ++
|
fingerprint_query(fingerprint, now) ++
|
||||||
user_query(user, now)
|
user_query(user, now)
|
||||||
|
|
||||||
bans =
|
bans =
|
||||||
queries
|
queries
|
||||||
|
@ -343,38 +344,56 @@ defmodule Philomena.Bans do
|
||||||
|
|
||||||
# Don't return a ban if the user is currently signed in.
|
# Don't return a ban if the user is currently signed in.
|
||||||
case is_nil(user) do
|
case is_nil(user) do
|
||||||
true -> Enum.at(bans, 0)
|
true -> Enum.at(bans, 0)
|
||||||
false -> user_ban(bans)
|
false -> user_ban(bans)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fingerprint_query(nil, _now), do: []
|
defp fingerprint_query(nil, _now), do: []
|
||||||
|
|
||||||
defp fingerprint_query(fingerprint, now) do
|
defp fingerprint_query(fingerprint, now) do
|
||||||
[
|
[
|
||||||
Fingerprint
|
Fingerprint
|
||||||
|> select([f], %{reason: f.reason, valid_until: f.valid_until, generated_ban_id: f.generated_ban_id, type: ^"FingerprintBan"})
|
|> select([f], %{
|
||||||
|
reason: f.reason,
|
||||||
|
valid_until: f.valid_until,
|
||||||
|
generated_ban_id: f.generated_ban_id,
|
||||||
|
type: ^"FingerprintBan"
|
||||||
|
})
|
||||||
|> where([f], f.enabled and f.valid_until > ^now)
|
|> where([f], f.enabled and f.valid_until > ^now)
|
||||||
|> where([f], f.fingerprint == ^fingerprint)
|
|> where([f], f.fingerprint == ^fingerprint)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp subnet_query(nil, _now), do: []
|
defp subnet_query(nil, _now), do: []
|
||||||
|
|
||||||
defp subnet_query(ip, now) do
|
defp subnet_query(ip, now) do
|
||||||
{:ok, inet} = EctoNetwork.INET.cast(ip)
|
{:ok, inet} = EctoNetwork.INET.cast(ip)
|
||||||
|
|
||||||
[
|
[
|
||||||
Subnet
|
Subnet
|
||||||
|> select([s], %{reason: s.reason, valid_until: s.valid_until, generated_ban_id: s.generated_ban_id, type: ^"SubnetBan"})
|
|> select([s], %{
|
||||||
|
reason: s.reason,
|
||||||
|
valid_until: s.valid_until,
|
||||||
|
generated_ban_id: s.generated_ban_id,
|
||||||
|
type: ^"SubnetBan"
|
||||||
|
})
|
||||||
|> where([s], s.enabled and s.valid_until > ^now)
|
|> where([s], s.enabled and s.valid_until > ^now)
|
||||||
|> where(fragment("specification >>= ?", ^inet))
|
|> where(fragment("specification >>= ?", ^inet))
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp user_query(nil, _now), do: []
|
defp user_query(nil, _now), do: []
|
||||||
|
|
||||||
defp user_query(user, now) do
|
defp user_query(user, now) do
|
||||||
[
|
[
|
||||||
User
|
User
|
||||||
|> select([u], %{reason: u.reason, valid_until: u.valid_until, generated_ban_id: u.generated_ban_id, type: ^"UserBan"})
|
|> select([u], %{
|
||||||
|
reason: u.reason,
|
||||||
|
valid_until: u.valid_until,
|
||||||
|
generated_ban_id: u.generated_ban_id,
|
||||||
|
type: ^"UserBan"
|
||||||
|
})
|
||||||
|> where([u], u.enabled and u.valid_until > ^now)
|
|> where([u], u.enabled and u.valid_until > ^now)
|
||||||
|> where([u], u.user_id == ^user.id)
|
|> where([u], u.user_id == ^user.id)
|
||||||
]
|
]
|
||||||
|
@ -382,12 +401,13 @@ defmodule Philomena.Bans do
|
||||||
|
|
||||||
defp union_all_queries([query]),
|
defp union_all_queries([query]),
|
||||||
do: query
|
do: query
|
||||||
|
|
||||||
defp union_all_queries([query | rest]),
|
defp union_all_queries([query | rest]),
|
||||||
do: query |> union_all(^union_all_queries(rest))
|
do: query |> union_all(^union_all_queries(rest))
|
||||||
|
|
||||||
defp user_ban(bans) do
|
defp user_ban(bans) do
|
||||||
bans
|
bans
|
||||||
|> Enum.filter(& &1.type == "UserBan")
|
|> Enum.filter(&(&1.type == "UserBan"))
|
||||||
|> Enum.at(0)
|
|> Enum.at(0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,6 +27,7 @@ defmodule Philomena.Batch do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp query_batches(_queryable, _opts, _callback, []), do: []
|
defp query_batches(_queryable, _opts, _callback, []), do: []
|
||||||
|
|
||||||
defp query_batches(queryable, opts, callback, ids) do
|
defp query_batches(queryable, opts, callback, ids) do
|
||||||
id_field = Keyword.get(opts, :id_field, :id)
|
id_field = Keyword.get(opts, :id_field, :id)
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,15 @@ defmodule Philomena.Captcha do
|
||||||
@background_file @base_path <> "/background.png"
|
@background_file @base_path <> "/background.png"
|
||||||
|
|
||||||
@geometry %{
|
@geometry %{
|
||||||
1 => "+0+0", 2 => "+120+0", 3 => "+240+0",
|
1 => "+0+0",
|
||||||
4 => "+0+120", 5 => "+120+120", 6 => "+240+120",
|
2 => "+120+0",
|
||||||
7 => "+0+240", 8 => "+120+240", 9 => "+240+240"
|
3 => "+240+0",
|
||||||
|
4 => "+0+120",
|
||||||
|
5 => "+120+120",
|
||||||
|
6 => "+240+120",
|
||||||
|
7 => "+0+240",
|
||||||
|
8 => "+120+240",
|
||||||
|
9 => "+240+240"
|
||||||
}
|
}
|
||||||
|
|
||||||
@distortion_1 [
|
@distortion_1 [
|
||||||
|
@ -60,7 +66,8 @@ defmodule Philomena.Captcha do
|
||||||
|
|
||||||
# Base arguments
|
# Base arguments
|
||||||
args = [
|
args = [
|
||||||
"-page", "360x360",
|
"-page",
|
||||||
|
"360x360",
|
||||||
@background_file
|
@background_file
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -71,8 +78,18 @@ defmodule Philomena.Captcha do
|
||||||
|> Enum.flat_map(fn {num, index} ->
|
|> Enum.flat_map(fn {num, index} ->
|
||||||
if num do
|
if num do
|
||||||
[
|
[
|
||||||
"(", @image_files[solution[num]], ")", "-geometry", @geometry[index + 1], "-composite",
|
"(",
|
||||||
"(", @number_files[num], ")", "-geometry", @geometry[index + 1], "-composite"
|
@image_files[solution[num]],
|
||||||
|
")",
|
||||||
|
"-geometry",
|
||||||
|
@geometry[index + 1],
|
||||||
|
"-composite",
|
||||||
|
"(",
|
||||||
|
@number_files[num],
|
||||||
|
")",
|
||||||
|
"-geometry",
|
||||||
|
@geometry[index + 1],
|
||||||
|
"-composite"
|
||||||
]
|
]
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
|
@ -99,6 +116,7 @@ defmodule Philomena.Captcha do
|
||||||
solution_id =
|
solution_id =
|
||||||
:crypto.strong_rand_bytes(12)
|
:crypto.strong_rand_bytes(12)
|
||||||
|> Base.encode16(case: :lower)
|
|> Base.encode16(case: :lower)
|
||||||
|
|
||||||
solution_id = "cp_" <> solution_id
|
solution_id = "cp_" <> solution_id
|
||||||
|
|
||||||
{:ok, _ok} = Redix.command(:redix, ["SET", solution_id, Jason.encode!(solution)])
|
{:ok, _ok} = Redix.command(:redix, ["SET", solution_id, Jason.encode!(solution)])
|
||||||
|
@ -116,8 +134,7 @@ defmodule Philomena.Captcha do
|
||||||
# have minimal impact if the race succeeds.
|
# have minimal impact if the race succeeds.
|
||||||
with {:ok, sol} <- Redix.command(:redix, ["GET", solution_id]),
|
with {:ok, sol} <- Redix.command(:redix, ["GET", solution_id]),
|
||||||
{:ok, _del} <- Redix.command(:redix, ["DEL", solution_id]),
|
{:ok, _del} <- Redix.command(:redix, ["DEL", solution_id]),
|
||||||
{:ok, sol} <- Jason.decode(to_string(sol))
|
{:ok, sol} <- Jason.decode(to_string(sol)) do
|
||||||
do
|
|
||||||
Map.equal?(solution, sol)
|
Map.equal?(solution, sol)
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
|
|
@ -118,6 +118,7 @@ defmodule Philomena.Channels do
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def create_subscription(_channel, nil), do: {:ok, nil}
|
def create_subscription(_channel, nil), do: {:ok, nil}
|
||||||
|
|
||||||
def create_subscription(channel, user) do
|
def create_subscription(channel, user) do
|
||||||
%Subscription{channel_id: channel.id, user_id: user.id}
|
%Subscription{channel_id: channel.id, user_id: user.id}
|
||||||
|> Subscription.changeset(%{})
|
|> Subscription.changeset(%{})
|
||||||
|
@ -144,6 +145,7 @@ defmodule Philomena.Channels do
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscribed?(_channel, nil), do: false
|
def subscribed?(_channel, nil), do: false
|
||||||
|
|
||||||
def subscribed?(channel, user) do
|
def subscribed?(channel, user) do
|
||||||
Subscription
|
Subscription
|
||||||
|> where(channel_id: ^channel.id, user_id: ^user.id)
|
|> where(channel_id: ^channel.id, user_id: ^user.id)
|
||||||
|
@ -151,6 +153,7 @@ defmodule Philomena.Channels do
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscriptions(_channels, nil), do: %{}
|
def subscriptions(_channels, nil), do: %{}
|
||||||
|
|
||||||
def subscriptions(channels, user) do
|
def subscriptions(channels, user) do
|
||||||
channel_ids = Enum.map(channels, & &1.id)
|
channel_ids = Enum.map(channels, & &1.id)
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ defmodule Philomena.Comments do
|
||||||
Image
|
Image
|
||||||
|> where(id: ^image.id)
|
|> where(id: ^image.id)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.insert(:comment, comment)
|
|> Multi.insert(:comment, comment)
|
||||||
|> Multi.update_all(:image, image_query, inc: [comments_count: 1])
|
|> Multi.update_all(:image, image_query, inc: [comments_count: 1])
|
||||||
|> Multi.run(:subscribe, fn _repo, _changes ->
|
|> Multi.run(:subscribe, fn _repo, _changes ->
|
||||||
|
@ -62,7 +62,7 @@ defmodule Philomena.Comments do
|
||||||
end
|
end
|
||||||
|
|
||||||
def notify_comment(comment) do
|
def notify_comment(comment) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
image =
|
image =
|
||||||
comment
|
comment
|
||||||
|> Repo.preload(:image)
|
|> Repo.preload(:image)
|
||||||
|
@ -84,7 +84,7 @@ defmodule Philomena.Comments do
|
||||||
action: "commented on"
|
action: "commented on"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
end
|
end)
|
||||||
|
|
||||||
comment
|
comment
|
||||||
end
|
end
|
||||||
|
@ -106,10 +106,9 @@ defmodule Philomena.Comments do
|
||||||
current_body = comment.body
|
current_body = comment.body
|
||||||
current_reason = comment.edit_reason
|
current_reason = comment.edit_reason
|
||||||
|
|
||||||
comment_changes =
|
comment_changes = Comment.changeset(comment, attrs, now)
|
||||||
Comment.changeset(comment, attrs, now)
|
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.update(:comment, comment_changes)
|
|> Multi.update(:comment, comment_changes)
|
||||||
|> Multi.run(:version, fn _repo, _changes ->
|
|> Multi.run(:version, fn _repo, _changes ->
|
||||||
Versions.create_version("Comment", comment.id, editor.id, %{
|
Versions.create_version("Comment", comment.id, editor.id, %{
|
||||||
|
@ -190,24 +189,24 @@ defmodule Philomena.Comments do
|
||||||
end
|
end
|
||||||
|
|
||||||
def reindex_comment(%Comment{} = comment) do
|
def reindex_comment(%Comment{} = comment) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
Comment
|
Comment
|
||||||
|> preload(^indexing_preloads())
|
|> preload(^indexing_preloads())
|
||||||
|> where(id: ^comment.id)
|
|> where(id: ^comment.id)
|
||||||
|> Repo.one()
|
|> Repo.one()
|
||||||
|> Elasticsearch.index_document(Comment)
|
|> Elasticsearch.index_document(Comment)
|
||||||
end
|
end)
|
||||||
|
|
||||||
comment
|
comment
|
||||||
end
|
end
|
||||||
|
|
||||||
def reindex_comments(image) do
|
def reindex_comments(image) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
Comment
|
Comment
|
||||||
|> preload(^indexing_preloads())
|
|> preload(^indexing_preloads())
|
||||||
|> where(image_id: ^image.id)
|
|> where(image_id: ^image.id)
|
||||||
|> Elasticsearch.reindex(Comment)
|
|> Elasticsearch.reindex(Comment)
|
||||||
end
|
end)
|
||||||
|
|
||||||
image
|
image
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,8 +33,10 @@ defmodule Philomena.Comments.ElasticsearchIndex do
|
||||||
user_id: %{type: "keyword"},
|
user_id: %{type: "keyword"},
|
||||||
author: %{type: "keyword"},
|
author: %{type: "keyword"},
|
||||||
image_tag_ids: %{type: "keyword"},
|
image_tag_ids: %{type: "keyword"},
|
||||||
anonymous: %{type: "keyword"}, # boolean
|
# boolean
|
||||||
hidden_from_users: %{type: "keyword"}, # boolean
|
anonymous: %{type: "keyword"},
|
||||||
|
# boolean
|
||||||
|
hidden_from_users: %{type: "keyword"},
|
||||||
body: %{type: "text", analyzer: "snowball"}
|
body: %{type: "text", analyzer: "snowball"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,23 +60,23 @@ defmodule Philomena.Comments.Query do
|
||||||
defp user_fields do
|
defp user_fields do
|
||||||
fields = anonymous_fields()
|
fields = anonymous_fields()
|
||||||
|
|
||||||
Keyword.merge(fields, [
|
Keyword.merge(fields,
|
||||||
custom_fields: fields[:custom_fields] ++ ~W(my),
|
custom_fields: fields[:custom_fields] ++ ~W(my),
|
||||||
transforms: Map.merge(fields[:transforms], %{"my" => &user_my_transform/2})
|
transforms: Map.merge(fields[:transforms], %{"my" => &user_my_transform/2})
|
||||||
])
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp moderator_fields do
|
defp moderator_fields do
|
||||||
fields = user_fields()
|
fields = user_fields()
|
||||||
|
|
||||||
Keyword.merge(fields, [
|
Keyword.merge(fields,
|
||||||
literal_fields: ~W(image_id user_id author fingerprint),
|
literal_fields: ~W(image_id user_id author fingerprint),
|
||||||
ip_fields: ~W(ip),
|
ip_fields: ~W(ip),
|
||||||
bool_fields: ~W(anonymous deleted),
|
bool_fields: ~W(anonymous deleted),
|
||||||
custom_fields: fields[:custom_fields] -- ~W(author user_id),
|
custom_fields: fields[:custom_fields] -- ~W(author user_id),
|
||||||
aliases: Map.merge(fields[:aliases], %{"deleted" => "hidden_from_users"}),
|
aliases: Map.merge(fields[:aliases], %{"deleted" => "hidden_from_users"}),
|
||||||
transforms: Map.drop(fields[:transforms], ~W(author user_id))
|
transforms: Map.drop(fields[:transforms], ~W(author user_id))
|
||||||
])
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp parse(fields, context, query_string) do
|
defp parse(fields, context, query_string) do
|
||||||
|
|
|
@ -25,7 +25,15 @@ defmodule Philomena.Commissions.Commission do
|
||||||
@doc false
|
@doc false
|
||||||
def changeset(commission, attrs) do
|
def changeset(commission, attrs) do
|
||||||
commission
|
commission
|
||||||
|> cast(attrs, [:information, :contact, :will_create, :will_not_create, :open, :sheet_image_id, :categories])
|
|> cast(attrs, [
|
||||||
|
:information,
|
||||||
|
:contact,
|
||||||
|
:will_create,
|
||||||
|
:will_not_create,
|
||||||
|
:open,
|
||||||
|
:sheet_image_id,
|
||||||
|
:categories
|
||||||
|
])
|
||||||
|> drop_blank_categories()
|
|> drop_blank_categories()
|
||||||
|> validate_required([:user_id, :information, :contact, :open])
|
|> validate_required([:user_id, :information, :contact, :open])
|
||||||
|> validate_length(:information, max: 700, count: :bytes)
|
|> validate_length(:information, max: 700, count: :bytes)
|
||||||
|
@ -37,25 +45,25 @@ defmodule Philomena.Commissions.Commission do
|
||||||
categories =
|
categories =
|
||||||
changeset
|
changeset
|
||||||
|> get_field(:categories)
|
|> get_field(:categories)
|
||||||
|> Enum.filter(& &1 not in [nil, ""])
|
|> Enum.filter(&(&1 not in [nil, ""]))
|
||||||
|
|
||||||
change(changeset, categories: categories)
|
change(changeset, categories: categories)
|
||||||
end
|
end
|
||||||
|
|
||||||
def categories do
|
def categories do
|
||||||
[
|
[
|
||||||
"Anthro": "Anthro",
|
Anthro: "Anthro",
|
||||||
"Canon Characters": "Canon Characters",
|
"Canon Characters": "Canon Characters",
|
||||||
"Comics": "Comics",
|
Comics: "Comics",
|
||||||
"Fetish Art": "Fetish Art",
|
"Fetish Art": "Fetish Art",
|
||||||
"Human and EqG": "Human and EqG",
|
"Human and EqG": "Human and EqG",
|
||||||
"NSFW": "NSFW",
|
NSFW: "NSFW",
|
||||||
"Original Characters": "Original Characters",
|
"Original Characters": "Original Characters",
|
||||||
"Original Species": "Original Species",
|
"Original Species": "Original Species",
|
||||||
"Pony": "Pony",
|
Pony: "Pony",
|
||||||
"Requests": "Requests",
|
Requests: "Requests",
|
||||||
"Safe": "Safe",
|
Safe: "Safe",
|
||||||
"Shipping": "Shipping",
|
Shipping: "Shipping",
|
||||||
"Violence and Gore": "Violence and Gore"
|
"Violence and Gore": "Violence and Gore"
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -92,52 +92,64 @@ defmodule Philomena.Conversations do
|
||||||
|
|
||||||
def count_unread_conversations(user) do
|
def count_unread_conversations(user) do
|
||||||
Conversation
|
Conversation
|
||||||
|> where([c],
|
|> where(
|
||||||
(
|
[c],
|
||||||
(c.to_id == ^user.id and c.to_read == false) or
|
((c.to_id == ^user.id and c.to_read == false) or
|
||||||
(c.from_id == ^user.id and c.from_read == false)
|
(c.from_id == ^user.id and c.from_read == false)) and
|
||||||
)
|
not ((c.to_id == ^user.id and c.to_hidden == true) or
|
||||||
and not (
|
(c.from_id == ^user.id and c.from_hidden == true))
|
||||||
(c.to_id == ^user.id and c.to_hidden == true) or
|
|
||||||
(c.from_id == ^user.id and c.from_hidden == true)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|> Repo.aggregate(:count, :id)
|
|> Repo.aggregate(:count, :id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def mark_conversation_read(conversation, user, read \\ true)
|
def mark_conversation_read(conversation, user, read \\ true)
|
||||||
|
|
||||||
def mark_conversation_read(%Conversation{to_id: user_id, from_id: user_id} = conversation, %{id: user_id}, read) do
|
def mark_conversation_read(
|
||||||
|
%Conversation{to_id: user_id, from_id: user_id} = conversation,
|
||||||
|
%{id: user_id},
|
||||||
|
read
|
||||||
|
) do
|
||||||
conversation
|
conversation
|
||||||
|> Conversation.read_changeset(%{to_read: read, from_read: read})
|
|> Conversation.read_changeset(%{to_read: read, from_read: read})
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_conversation_read(%Conversation{to_id: user_id} = conversation, %{id: user_id}, read) do
|
def mark_conversation_read(%Conversation{to_id: user_id} = conversation, %{id: user_id}, read) do
|
||||||
conversation
|
conversation
|
||||||
|> Conversation.read_changeset(%{to_read: read})
|
|> Conversation.read_changeset(%{to_read: read})
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_conversation_read(%Conversation{from_id: user_id} = conversation, %{id: user_id}, read) do
|
def mark_conversation_read(%Conversation{from_id: user_id} = conversation, %{id: user_id}, read) do
|
||||||
conversation
|
conversation
|
||||||
|> Conversation.read_changeset(%{from_read: read})
|
|> Conversation.read_changeset(%{from_read: read})
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
def mark_conversation_read(_conversation, _user, _read), do: {:ok, nil}
|
|
||||||
|
|
||||||
|
def mark_conversation_read(_conversation, _user, _read), do: {:ok, nil}
|
||||||
|
|
||||||
def mark_conversation_hidden(conversation, user, hidden \\ true)
|
def mark_conversation_hidden(conversation, user, hidden \\ true)
|
||||||
|
|
||||||
def mark_conversation_hidden(%Conversation{to_id: user_id} = conversation, %{id: user_id}, hidden) do
|
def mark_conversation_hidden(
|
||||||
|
%Conversation{to_id: user_id} = conversation,
|
||||||
|
%{id: user_id},
|
||||||
|
hidden
|
||||||
|
) do
|
||||||
conversation
|
conversation
|
||||||
|> Conversation.hidden_changeset(%{to_hidden: hidden})
|
|> Conversation.hidden_changeset(%{to_hidden: hidden})
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
def mark_conversation_hidden(%Conversation{from_id: user_id} = conversation, %{id: user_id}, hidden) do
|
|
||||||
|
def mark_conversation_hidden(
|
||||||
|
%Conversation{from_id: user_id} = conversation,
|
||||||
|
%{id: user_id},
|
||||||
|
hidden
|
||||||
|
) do
|
||||||
conversation
|
conversation
|
||||||
|> Conversation.hidden_changeset(%{from_hidden: hidden})
|
|> Conversation.hidden_changeset(%{from_hidden: hidden})
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_conversation_hidden(_conversation, _user, _read), do: {:ok, nil}
|
def mark_conversation_hidden(_conversation, _user, _read), do: {:ok, nil}
|
||||||
|
|
||||||
alias Philomena.Conversations.Message
|
alias Philomena.Conversations.Message
|
||||||
|
@ -181,9 +193,11 @@ defmodule Philomena.Conversations do
|
||||||
|
|
||||||
now = DateTime.utc_now()
|
now = DateTime.utc_now()
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.insert(:message, message)
|
|> Multi.insert(:message, message)
|
||||||
|> Multi.update_all(:conversation, conversation_query, set: [from_read: false, to_read: false, last_message_at: now])
|
|> Multi.update_all(:conversation, conversation_query,
|
||||||
|
set: [from_read: false, to_read: false, last_message_at: now]
|
||||||
|
)
|
||||||
|> Repo.isolated_transaction(:serializable)
|
|> Repo.isolated_transaction(:serializable)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ defmodule Philomena.DnpEntries do
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def create_dnp_entry(user, tags, attrs \\ %{}) do
|
def create_dnp_entry(user, tags, attrs \\ %{}) do
|
||||||
tag = Enum.find(tags, &to_string(&1.id) == attrs["tag_id"])
|
tag = Enum.find(tags, &(to_string(&1.id) == attrs["tag_id"]))
|
||||||
|
|
||||||
%DnpEntry{}
|
%DnpEntry{}
|
||||||
|> DnpEntry.creation_changeset(attrs, tag, user)
|
|> DnpEntry.creation_changeset(attrs, tag, user)
|
||||||
|
@ -70,7 +70,7 @@ defmodule Philomena.DnpEntries do
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def update_dnp_entry(%DnpEntry{} = dnp_entry, tags, attrs) do
|
def update_dnp_entry(%DnpEntry{} = dnp_entry, tags, attrs) do
|
||||||
tag = Enum.find(tags, &to_string(&1.id) == attrs["tag_id"])
|
tag = Enum.find(tags, &(to_string(&1.id) == attrs["tag_id"]))
|
||||||
|
|
||||||
dnp_entry
|
dnp_entry
|
||||||
|> DnpEntry.update_changeset(attrs, tag)
|
|> DnpEntry.update_changeset(attrs, tag)
|
||||||
|
|
|
@ -53,6 +53,7 @@ defmodule Philomena.DnpEntries.DnpEntry do
|
||||||
|
|
||||||
defp validate_conditions(%Ecto.Changeset{changes: %{dnp_type: "Other"}} = changeset),
|
defp validate_conditions(%Ecto.Changeset{changes: %{dnp_type: "Other"}} = changeset),
|
||||||
do: validate_required(changeset, [:conditions])
|
do: validate_required(changeset, [:conditions])
|
||||||
|
|
||||||
defp validate_conditions(changeset),
|
defp validate_conditions(changeset),
|
||||||
do: changeset
|
do: changeset
|
||||||
|
|
||||||
|
@ -70,12 +71,18 @@ defmodule Philomena.DnpEntries.DnpEntry do
|
||||||
|
|
||||||
def reasons do
|
def reasons do
|
||||||
[
|
[
|
||||||
{"No Edits", "I would like to prevent edited versions of my artwork from being uploaded in the future"},
|
{"No Edits",
|
||||||
{"Artist Tag Change", "I would like my artist tag to be changed to something that can not be connected to my current name"},
|
"I would like to prevent edited versions of my artwork from being uploaded in the future"},
|
||||||
{"Uploader Credit Change", "I would like the uploader credit for already existing uploads of my art to be assigned to me"},
|
{"Artist Tag Change",
|
||||||
{"Certain Type/Location Only", "I only want to allow art of a certain type or from a certain location to be uploaded to Derpibooru"},
|
"I would like my artist tag to be changed to something that can not be connected to my current name"},
|
||||||
{"With Permission Only", "I only want people with my permission to be allowed to upload my art to Derpibooru"},
|
{"Uploader Credit Change",
|
||||||
{"Artist Upload Only", "I want to be the only person allowed to upload my art to Derpibooru"},
|
"I would like the uploader credit for already existing uploads of my art to be assigned to me"},
|
||||||
|
{"Certain Type/Location Only",
|
||||||
|
"I only want to allow art of a certain type or from a certain location to be uploaded to Derpibooru"},
|
||||||
|
{"With Permission Only",
|
||||||
|
"I only want people with my permission to be allowed to upload my art to Derpibooru"},
|
||||||
|
{"Artist Upload Only",
|
||||||
|
"I want to be the only person allowed to upload my art to Derpibooru"},
|
||||||
{"Other", "I would like a DNP entry under other conditions"}
|
{"Other", "I would like a DNP entry under other conditions"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,7 +20,9 @@ defmodule Philomena.DuplicateReports do
|
||||||
|> where([i, _it], i.duplication_checked != true)
|
|> where([i, _it], i.duplication_checked != true)
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|> Enum.map(fn target ->
|
|> Enum.map(fn target ->
|
||||||
create_duplicate_report(source, target, %{}, %{"reason" => "Automated Perceptual dedupe match"})
|
create_duplicate_report(source, target, %{}, %{
|
||||||
|
"reason" => "Automated Perceptual dedupe match"
|
||||||
|
})
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -32,7 +34,9 @@ defmodule Philomena.DuplicateReports do
|
||||||
where: it.ne >= ^(intensities.ne - dist) and it.ne <= ^(intensities.ne + dist),
|
where: it.ne >= ^(intensities.ne - dist) and it.ne <= ^(intensities.ne + dist),
|
||||||
where: it.sw >= ^(intensities.sw - dist) and it.sw <= ^(intensities.sw + dist),
|
where: it.sw >= ^(intensities.sw - dist) and it.sw <= ^(intensities.sw + dist),
|
||||||
where: it.se >= ^(intensities.se - dist) and it.se <= ^(intensities.se + dist),
|
where: it.se >= ^(intensities.se - dist) and it.se <= ^(intensities.se + dist),
|
||||||
where: i.image_aspect_ratio >= ^(aspect_ratio - aspect_dist) and i.image_aspect_ratio <= ^(aspect_ratio + aspect_dist),
|
where:
|
||||||
|
i.image_aspect_ratio >= ^(aspect_ratio - aspect_dist) and
|
||||||
|
i.image_aspect_ratio <= ^(aspect_ratio + aspect_dist),
|
||||||
limit: 20
|
limit: 20
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -83,8 +87,10 @@ defmodule Philomena.DuplicateReports do
|
||||||
DuplicateReport
|
DuplicateReport
|
||||||
|> where(
|
|> where(
|
||||||
[dr],
|
[dr],
|
||||||
(dr.image_id == ^duplicate_report.image_id and dr.duplicate_of_image_id == ^duplicate_report.duplicate_of_image_id) or
|
(dr.image_id == ^duplicate_report.image_id and
|
||||||
(dr.image_id == ^duplicate_report.duplicate_of_image_id and dr.duplicate_of_image_id == ^duplicate_report.image_id)
|
dr.duplicate_of_image_id == ^duplicate_report.duplicate_of_image_id) or
|
||||||
|
(dr.image_id == ^duplicate_report.duplicate_of_image_id and
|
||||||
|
dr.duplicate_of_image_id == ^duplicate_report.image_id)
|
||||||
)
|
)
|
||||||
|> where([dr], dr.id != ^duplicate_report.id)
|
|> where([dr], dr.id != ^duplicate_report.id)
|
||||||
|> repo.update_all(set: [state: "rejected"])
|
|> repo.update_all(set: [state: "rejected"])
|
||||||
|
|
|
@ -62,7 +62,7 @@ defmodule Philomena.DuplicateReports.DuplicateReport do
|
||||||
target_id = get_field(changeset, :duplicate_of_image_id)
|
target_id = get_field(changeset, :duplicate_of_image_id)
|
||||||
|
|
||||||
case source_id == target_id do
|
case source_id == target_id do
|
||||||
true -> add_error(changeset, :image_id, "must be different from the target")
|
true -> add_error(changeset, :image_id, "must be different from the target")
|
||||||
false -> changeset
|
false -> changeset
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -109,7 +109,13 @@ defmodule Philomena.Elasticsearch do
|
||||||
def search_results(module, elastic_query, pagination_params \\ %{}) do
|
def search_results(module, elastic_query, pagination_params \\ %{}) do
|
||||||
page_number = pagination_params[:page_number] || 1
|
page_number = pagination_params[:page_number] || 1
|
||||||
page_size = pagination_params[:page_size] || 25
|
page_size = pagination_params[:page_size] || 25
|
||||||
elastic_query = Map.merge(elastic_query, %{from: (page_number - 1) * page_size, size: page_size, _source: false})
|
|
||||||
|
elastic_query =
|
||||||
|
Map.merge(elastic_query, %{
|
||||||
|
from: (page_number - 1) * page_size,
|
||||||
|
size: page_size,
|
||||||
|
_source: false
|
||||||
|
})
|
||||||
|
|
||||||
results = search(module, elastic_query)
|
results = search(module, elastic_query)
|
||||||
time = results["took"]
|
time = results["took"]
|
||||||
|
|
|
@ -139,8 +139,8 @@ defmodule Philomena.Filters do
|
||||||
end)
|
end)
|
||||||
|> Enum.group_by(
|
|> Enum.group_by(
|
||||||
fn
|
fn
|
||||||
%{recent: "t"} -> "Recent Filters"
|
%{recent: "t"} -> "Recent Filters"
|
||||||
_user -> "Your Filters"
|
_user -> "Your Filters"
|
||||||
end,
|
end,
|
||||||
fn %{id: id, name: name} ->
|
fn %{id: id, name: name} ->
|
||||||
[key: name, value: id]
|
[key: name, value: id]
|
||||||
|
|
|
@ -34,7 +34,14 @@ defmodule Philomena.Filters.Filter do
|
||||||
|> Map.get(:user)
|
|> Map.get(:user)
|
||||||
|
|
||||||
filter
|
filter
|
||||||
|> cast(attrs, [:spoilered_tag_list, :hidden_tag_list, :description, :name, :spoilered_complex_str, :hidden_complex_str])
|
|> cast(attrs, [
|
||||||
|
:spoilered_tag_list,
|
||||||
|
:hidden_tag_list,
|
||||||
|
:description,
|
||||||
|
:name,
|
||||||
|
:spoilered_complex_str,
|
||||||
|
:hidden_complex_str
|
||||||
|
])
|
||||||
|> TagList.propagate_tag_list(:spoilered_tag_list, :spoilered_tag_ids)
|
|> TagList.propagate_tag_list(:spoilered_tag_list, :spoilered_tag_ids)
|
||||||
|> TagList.propagate_tag_list(:hidden_tag_list, :hidden_tag_ids)
|
|> TagList.propagate_tag_list(:hidden_tag_list, :hidden_tag_ids)
|
||||||
|> validate_required([:name, :description])
|
|> validate_required([:name, :description])
|
||||||
|
|
|
@ -105,6 +105,7 @@ defmodule Philomena.Forums do
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscribed?(_forum, nil), do: false
|
def subscribed?(_forum, nil), do: false
|
||||||
|
|
||||||
def subscribed?(forum, user) do
|
def subscribed?(forum, user) do
|
||||||
Subscription
|
Subscription
|
||||||
|> where(forum_id: ^forum.id, user_id: ^user.id)
|
|> where(forum_id: ^forum.id, user_id: ^user.id)
|
||||||
|
@ -112,6 +113,7 @@ defmodule Philomena.Forums do
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_subscription(_forum, nil), do: {:ok, nil}
|
def create_subscription(_forum, nil), do: {:ok, nil}
|
||||||
|
|
||||||
def create_subscription(forum, user) do
|
def create_subscription(forum, user) do
|
||||||
%Subscription{forum_id: forum.id, user_id: user.id}
|
%Subscription{forum_id: forum.id, user_id: user.id}
|
||||||
|> Subscription.changeset(%{})
|
|> Subscription.changeset(%{})
|
||||||
|
@ -138,6 +140,7 @@ defmodule Philomena.Forums do
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_notification(_forum, nil), do: nil
|
def clear_notification(_forum, nil), do: nil
|
||||||
|
|
||||||
def clear_notification(forum, user) do
|
def clear_notification(forum, user) do
|
||||||
Notifications.delete_unread_notification("Forum", forum.id, user)
|
Notifications.delete_unread_notification("Forum", forum.id, user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,7 +28,9 @@ defmodule Philomena.Forums.Forum do
|
||||||
|> cast(attrs, [:name, :short_name, :description, :access_level])
|
|> cast(attrs, [:name, :short_name, :description, :access_level])
|
||||||
|> validate_required([:name, :short_name, :description, :access_level])
|
|> validate_required([:name, :short_name, :description, :access_level])
|
||||||
|> validate_inclusion(:access_level, ~W(normal assistant staff))
|
|> validate_inclusion(:access_level, ~W(normal assistant staff))
|
||||||
|> validate_format(:short_name, ~r/\A[a-z]+\z/, message: "must consist only of lowercase letters")
|
|> validate_format(:short_name, ~r/\A[a-z]+\z/,
|
||||||
|
message: "must consist only of lowercase letters"
|
||||||
|
)
|
||||||
|> unique_constraint(:short_name, name: :index_forums_on_short_name)
|
|> unique_constraint(:short_name, name: :index_forums_on_short_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -123,21 +123,21 @@ defmodule Philomena.Galleries do
|
||||||
end
|
end
|
||||||
|
|
||||||
def reindex_gallery(%Gallery{} = gallery) do
|
def reindex_gallery(%Gallery{} = gallery) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
Gallery
|
Gallery
|
||||||
|> preload(^indexing_preloads())
|
|> preload(^indexing_preloads())
|
||||||
|> where(id: ^gallery.id)
|
|> where(id: ^gallery.id)
|
||||||
|> Repo.one()
|
|> Repo.one()
|
||||||
|> Elasticsearch.index_document(Gallery)
|
|> Elasticsearch.index_document(Gallery)
|
||||||
end
|
end)
|
||||||
|
|
||||||
gallery
|
gallery
|
||||||
end
|
end
|
||||||
|
|
||||||
def unindex_gallery(%Gallery{} = gallery) do
|
def unindex_gallery(%Gallery{} = gallery) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
Elasticsearch.delete_document(gallery.id, Gallery)
|
Elasticsearch.delete_document(gallery.id, Gallery)
|
||||||
end
|
end)
|
||||||
|
|
||||||
gallery
|
gallery
|
||||||
end
|
end
|
||||||
|
@ -198,7 +198,7 @@ defmodule Philomena.Galleries do
|
||||||
end
|
end
|
||||||
|
|
||||||
def notify_gallery(gallery) do
|
def notify_gallery(gallery) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
subscriptions =
|
subscriptions =
|
||||||
gallery
|
gallery
|
||||||
|> Repo.preload(:subscriptions)
|
|> Repo.preload(:subscriptions)
|
||||||
|
@ -215,13 +215,13 @@ defmodule Philomena.Galleries do
|
||||||
action: "added images to"
|
action: "added images to"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
end
|
end)
|
||||||
|
|
||||||
gallery
|
gallery
|
||||||
end
|
end
|
||||||
|
|
||||||
def reorder_gallery(gallery, image_ids) do
|
def reorder_gallery(gallery, image_ids) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
interactions =
|
interactions =
|
||||||
Interaction
|
Interaction
|
||||||
|> where([gi], gi.image_id in ^image_ids)
|
|> where([gi], gi.image_id in ^image_ids)
|
||||||
|
@ -234,6 +234,7 @@ defmodule Philomena.Galleries do
|
||||||
|> Map.new(fn {interaction, index} -> {index, interaction.position} end)
|
|> Map.new(fn {interaction, index} -> {index, interaction.position} end)
|
||||||
|
|
||||||
images_present = Map.new(interactions, &{&1.image_id, true})
|
images_present = Map.new(interactions, &{&1.image_id, true})
|
||||||
|
|
||||||
requested =
|
requested =
|
||||||
image_ids
|
image_ids
|
||||||
|> Enum.filter(&images_present[&1])
|
|> Enum.filter(&images_present[&1])
|
||||||
|
@ -272,7 +273,7 @@ defmodule Philomena.Galleries do
|
||||||
|
|
||||||
# Now update all the associated images
|
# Now update all the associated images
|
||||||
Images.reindex_images(Map.keys(requested))
|
Images.reindex_images(Map.keys(requested))
|
||||||
end
|
end)
|
||||||
|
|
||||||
gallery
|
gallery
|
||||||
end
|
end
|
||||||
|
@ -283,6 +284,7 @@ defmodule Philomena.Galleries do
|
||||||
alias Philomena.Galleries.Subscription
|
alias Philomena.Galleries.Subscription
|
||||||
|
|
||||||
def subscribed?(_gallery, nil), do: false
|
def subscribed?(_gallery, nil), do: false
|
||||||
|
|
||||||
def subscribed?(gallery, user) do
|
def subscribed?(gallery, user) do
|
||||||
Subscription
|
Subscription
|
||||||
|> where(gallery_id: ^gallery.id, user_id: ^user.id)
|
|> where(gallery_id: ^gallery.id, user_id: ^user.id)
|
||||||
|
@ -325,6 +327,7 @@ defmodule Philomena.Galleries do
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_notification(_gallery, nil), do: nil
|
def clear_notification(_gallery, nil), do: nil
|
||||||
|
|
||||||
def clear_notification(gallery, user) do
|
def clear_notification(gallery, user) do
|
||||||
Notifications.delete_unread_notification("Gallery", gallery.id, user)
|
Notifications.delete_unread_notification("Gallery", gallery.id, user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,15 +25,18 @@ defmodule Philomena.Galleries.ElasticsearchIndex do
|
||||||
_all: %{enabled: false},
|
_all: %{enabled: false},
|
||||||
dynamic: false,
|
dynamic: false,
|
||||||
properties: %{
|
properties: %{
|
||||||
id: %{type: "integer"}, # keyword
|
# keyword
|
||||||
|
id: %{type: "integer"},
|
||||||
image_count: %{type: "integer"},
|
image_count: %{type: "integer"},
|
||||||
watcher_count: %{type: "integer"},
|
watcher_count: %{type: "integer"},
|
||||||
updated_at: %{type: "date"},
|
updated_at: %{type: "date"},
|
||||||
created_at: %{type: "date"},
|
created_at: %{type: "date"},
|
||||||
title: %{type: "keyword"},
|
title: %{type: "keyword"},
|
||||||
creator: %{type: "keyword"}, # missing creator_id
|
# missing creator_id
|
||||||
|
creator: %{type: "keyword"},
|
||||||
image_ids: %{type: "keyword"},
|
image_ids: %{type: "keyword"},
|
||||||
watcher_ids: %{type: "keyword"}, # ???
|
# ???
|
||||||
|
watcher_ids: %{type: "keyword"},
|
||||||
description: %{type: "text", analyzer: "snowball"}
|
description: %{type: "text", analyzer: "snowball"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
defmodule Philomena.Http do
|
defmodule Philomena.Http do
|
||||||
@user_agent ["User-Agent": "Mozilla/5.0 (X11; Philomena; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0"]
|
@user_agent [
|
||||||
|
"User-Agent":
|
||||||
|
"Mozilla/5.0 (X11; Philomena; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0"
|
||||||
|
]
|
||||||
|
|
||||||
def get!(url, headers \\ [], options \\ []) do
|
def get!(url, headers \\ [], options \\ []) do
|
||||||
headers = Keyword.merge(@user_agent, headers) |> add_host(url)
|
headers = Keyword.merge(@user_agent, headers) |> add_host(url)
|
||||||
|
@ -19,7 +22,7 @@ defmodule Philomena.Http do
|
||||||
defp add_host(headers, url) do
|
defp add_host(headers, url) do
|
||||||
%{host: host} = URI.parse(url)
|
%{host: host} = URI.parse(url)
|
||||||
|
|
||||||
Keyword.merge(["Host": host, "Connection": "close"], headers)
|
Keyword.merge([Host: host, Connection: "close"], headers)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp proxy_host do
|
defp proxy_host do
|
||||||
|
|
|
@ -23,7 +23,7 @@ defmodule Philomena.ImageFaves do
|
||||||
Image
|
Image
|
||||||
|> where(id: ^image.id)
|
|> where(id: ^image.id)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.insert(:fave, fave)
|
|> Multi.insert(:fave, fave)
|
||||||
|> Multi.update_all(:inc_faves_count, image_query, inc: [faves_count: 1])
|
|> Multi.update_all(:inc_faves_count, image_query, inc: [faves_count: 1])
|
||||||
|> Multi.run(:inc_fave_stat, fn _repo, _changes ->
|
|> Multi.run(:inc_fave_stat, fn _repo, _changes ->
|
||||||
|
@ -45,7 +45,7 @@ defmodule Philomena.ImageFaves do
|
||||||
Image
|
Image
|
||||||
|> where(id: ^image.id)
|
|> where(id: ^image.id)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.delete_all(:unfave, fave_query)
|
|> Multi.delete_all(:unfave, fave_query)
|
||||||
|> Multi.run(:dec_faves_count, fn repo, %{unfave: {faves, nil}} ->
|
|> Multi.run(:dec_faves_count, fn repo, %{unfave: {faves, nil}} ->
|
||||||
{count, nil} =
|
{count, nil} =
|
||||||
|
|
|
@ -22,7 +22,7 @@ defmodule Philomena.ImageHides do
|
||||||
Image
|
Image
|
||||||
|> where(id: ^image.id)
|
|> where(id: ^image.id)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.insert(:hide, hide)
|
|> Multi.insert(:hide, hide)
|
||||||
|> Multi.update_all(:inc_hides_count, image_query, inc: [hides_count: 1])
|
|> Multi.update_all(:inc_hides_count, image_query, inc: [hides_count: 1])
|
||||||
end
|
end
|
||||||
|
@ -41,7 +41,7 @@ defmodule Philomena.ImageHides do
|
||||||
Image
|
Image
|
||||||
|> where(id: ^image.id)
|
|> where(id: ^image.id)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.delete_all(:unhide, hide_query)
|
|> Multi.delete_all(:unhide, hide_query)
|
||||||
|> Multi.run(:dec_hides_count, fn repo, %{unhide: {hides, nil}} ->
|
|> Multi.run(:dec_hides_count, fn repo, %{unhide: {hides, nil}} ->
|
||||||
{count, nil} =
|
{count, nil} =
|
||||||
|
|
|
@ -70,7 +70,7 @@ defmodule Philomena.ImageNavigator do
|
||||||
defp extract_filters(%{"galleries.position" => term} = sort, image, rel) do
|
defp extract_filters(%{"galleries.position" => term} = sort, image, rel) do
|
||||||
# Extract gallery ID and current position
|
# Extract gallery ID and current position
|
||||||
gid = term.nested.filter.term["galleries.id"]
|
gid = term.nested.filter.term["galleries.id"]
|
||||||
pos = Enum.find(image[:galleries], & &1.id == gid).position
|
pos = Enum.find(image[:galleries], &(&1.id == gid)).position
|
||||||
|
|
||||||
# Sort in the other direction if we are going backwards
|
# Sort in the other direction if we are going backwards
|
||||||
sd = term.order
|
sd = term.order
|
||||||
|
@ -111,7 +111,9 @@ defmodule Philomena.ImageNavigator do
|
||||||
filters
|
filters
|
||||||
|> Enum.with_index()
|
|> Enum.with_index()
|
||||||
|> Enum.map(fn
|
|> Enum.map(fn
|
||||||
{filter, 0} -> filter.this
|
{filter, 0} ->
|
||||||
|
filter.this
|
||||||
|
|
||||||
{filter, i} ->
|
{filter, i} ->
|
||||||
filters_so_far =
|
filters_so_far =
|
||||||
filters
|
filters
|
||||||
|
@ -144,13 +146,13 @@ defmodule Philomena.ImageNavigator do
|
||||||
%{
|
%{
|
||||||
this: %{
|
this: %{
|
||||||
nested: %{
|
nested: %{
|
||||||
path: :galleries,
|
path: :galleries,
|
||||||
query: %{range: %{"galleries.position" => %{dir => val}}}
|
query: %{range: %{"galleries.position" => %{dir => val}}}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
next: %{
|
next: %{
|
||||||
nested: %{
|
nested: %{
|
||||||
path: :galleries,
|
path: :galleries,
|
||||||
query: %{range: %{"galleries.position" => %{@range_map[dir] => val}}}
|
query: %{range: %{"galleries.position" => %{@range_map[dir] => val}}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ defmodule Philomena.ImageScope do
|
||||||
defp scope(list, conn, key, key_atom) do
|
defp scope(list, conn, key, key_atom) do
|
||||||
case conn.params[key] do
|
case conn.params[key] do
|
||||||
nil -> list
|
nil -> list
|
||||||
"" -> list
|
"" -> list
|
||||||
val -> [{key_atom, val} | list]
|
val -> [{key_atom, val} | list]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -52,17 +52,19 @@ defmodule Philomena.ImageSorter do
|
||||||
{gallery, _rest} ->
|
{gallery, _rest} ->
|
||||||
%{
|
%{
|
||||||
queries: [],
|
queries: [],
|
||||||
sorts: [%{
|
sorts: [
|
||||||
"galleries.position" => %{
|
%{
|
||||||
order: sd,
|
"galleries.position" => %{
|
||||||
nested: %{
|
order: sd,
|
||||||
path: :galleries,
|
nested: %{
|
||||||
filter: %{
|
path: :galleries,
|
||||||
term: %{"galleries.id" => gallery}
|
filter: %{
|
||||||
|
term: %{"galleries.id" => gallery}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}],
|
],
|
||||||
constant_score: true
|
constant_score: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,13 +79,15 @@ defmodule Philomena.ImageSorter do
|
||||||
|
|
||||||
defp random_query(seed, sd) do
|
defp random_query(seed, sd) do
|
||||||
%{
|
%{
|
||||||
queries: [%{
|
queries: [
|
||||||
function_score: %{
|
%{
|
||||||
query: %{match_all: %{}},
|
function_score: %{
|
||||||
random_score: %{seed: seed, field: :id},
|
query: %{match_all: %{}},
|
||||||
boost_mode: :replace
|
random_score: %{seed: seed, field: :id},
|
||||||
|
boost_mode: :replace
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}],
|
],
|
||||||
sorts: [%{"_score" => sd}],
|
sorts: [%{"_score" => sd}],
|
||||||
constant_score: true
|
constant_score: true
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,14 @@ defmodule Philomena.ImageVotes do
|
||||||
Image
|
Image
|
||||||
|> where(id: ^image.id)
|
|> where(id: ^image.id)
|
||||||
|
|
||||||
upvotes = if up, do: 1, else: 0
|
upvotes = if up, do: 1, else: 0
|
||||||
downvotes = if up, do: 0, else: 1
|
downvotes = if up, do: 0, else: 1
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.insert(:vote, vote)
|
|> Multi.insert(:vote, vote)
|
||||||
|> Multi.update_all(:inc_vote_count, image_query, inc: [upvotes_count: upvotes, downvotes_count: downvotes, score: upvotes - downvotes])
|
|> Multi.update_all(:inc_vote_count, image_query,
|
||||||
|
inc: [upvotes_count: upvotes, downvotes_count: downvotes, score: upvotes - downvotes]
|
||||||
|
)
|
||||||
|> Multi.run(:inc_vote_stat, fn _repo, _changes ->
|
|> Multi.run(:inc_vote_stat, fn _repo, _changes ->
|
||||||
UserStatistics.inc_stat(user, :votes_cast, 1)
|
UserStatistics.inc_stat(user, :votes_cast, 1)
|
||||||
end)
|
end)
|
||||||
|
@ -55,13 +57,16 @@ defmodule Philomena.ImageVotes do
|
||||||
Image
|
Image
|
||||||
|> where(id: ^image.id)
|
|> where(id: ^image.id)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.delete_all(:unupvote, upvote_query)
|
|> Multi.delete_all(:unupvote, upvote_query)
|
||||||
|> Multi.delete_all(:undownvote, downvote_query)
|
|> Multi.delete_all(:undownvote, downvote_query)
|
||||||
|> Multi.run(:dec_votes_count, fn repo, %{unupvote: {upvotes, nil}, undownvote: {downvotes, nil}} ->
|
|> Multi.run(:dec_votes_count, fn repo,
|
||||||
|
%{unupvote: {upvotes, nil}, undownvote: {downvotes, nil}} ->
|
||||||
{count, nil} =
|
{count, nil} =
|
||||||
image_query
|
image_query
|
||||||
|> repo.update_all(inc: [upvotes_count: -upvotes, downvotes_count: -downvotes, score: downvotes - upvotes])
|
|> repo.update_all(
|
||||||
|
inc: [upvotes_count: -upvotes, downvotes_count: -downvotes, score: downvotes - upvotes]
|
||||||
|
)
|
||||||
|
|
||||||
UserStatistics.inc_stat(user, :votes_cast, -(upvotes + downvotes))
|
UserStatistics.inc_stat(user, :votes_cast, -(upvotes + downvotes))
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ defmodule Philomena.Images do
|
||||||
|> Image.tag_changeset(attrs, [], tags)
|
|> Image.tag_changeset(attrs, [], tags)
|
||||||
|> Uploader.analyze_upload(attrs)
|
|> Uploader.analyze_upload(attrs)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.insert(:image, image)
|
|> Multi.insert(:image, image)
|
||||||
|> Multi.run(:name_caches, fn repo, %{image: image} ->
|
|> Multi.run(:name_caches, fn repo, %{image: image} ->
|
||||||
image
|
image
|
||||||
|
@ -217,7 +217,7 @@ defmodule Philomena.Images do
|
||||||
Ecto.build_assoc(image, :source_changes)
|
Ecto.build_assoc(image, :source_changes)
|
||||||
|> SourceChange.creation_changeset(attrs, attribution)
|
|> SourceChange.creation_changeset(attrs, attribution)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.update(:image, image_changes)
|
|> Multi.update(:image, image_changes)
|
||||||
|> Multi.run(:source_change, fn repo, _changes ->
|
|> Multi.run(:source_change, fn repo, _changes ->
|
||||||
case image_changes.changes do
|
case image_changes.changes do
|
||||||
|
@ -235,7 +235,7 @@ defmodule Philomena.Images do
|
||||||
old_tags = Tags.get_or_create_tags(attrs["old_tag_input"])
|
old_tags = Tags.get_or_create_tags(attrs["old_tag_input"])
|
||||||
new_tags = Tags.get_or_create_tags(attrs["tag_input"])
|
new_tags = Tags.get_or_create_tags(attrs["tag_input"])
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.run(:image, fn repo, _chg ->
|
|> Multi.run(:image, fn repo, _chg ->
|
||||||
image
|
image
|
||||||
|> repo.preload(:tags)
|
|> repo.preload(:tags)
|
||||||
|
@ -296,9 +296,10 @@ defmodule Philomena.Images do
|
||||||
|
|
||||||
defp tag_change_attributes(attribution, image, tag, added, user) do
|
defp tag_change_attributes(attribution, image, tag, added, user) do
|
||||||
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
||||||
|
|
||||||
user_id =
|
user_id =
|
||||||
case user do
|
case user do
|
||||||
nil -> nil
|
nil -> nil
|
||||||
user -> user.id
|
user -> user.id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -340,7 +341,12 @@ defmodule Philomena.Images do
|
||||||
|
|
||||||
case result do
|
case result do
|
||||||
{:ok, changes} ->
|
{:ok, changes} ->
|
||||||
update_first_seen_at(duplicate_of_image, image.first_seen_at, duplicate_of_image.first_seen_at)
|
update_first_seen_at(
|
||||||
|
duplicate_of_image,
|
||||||
|
image.first_seen_at,
|
||||||
|
duplicate_of_image.first_seen_at
|
||||||
|
)
|
||||||
|
|
||||||
tags = Tags.copy_tags(image, duplicate_of_image)
|
tags = Tags.copy_tags(image, duplicate_of_image)
|
||||||
Comments.migrate_comments(image, duplicate_of_image)
|
Comments.migrate_comments(image, duplicate_of_image)
|
||||||
Interactions.migrate_interactions(image, duplicate_of_image)
|
Interactions.migrate_interactions(image, duplicate_of_image)
|
||||||
|
@ -356,7 +362,7 @@ defmodule Philomena.Images do
|
||||||
min_time =
|
min_time =
|
||||||
case NaiveDateTime.compare(time_1, time_2) do
|
case NaiveDateTime.compare(time_1, time_2) do
|
||||||
:gt -> time_2
|
:gt -> time_2
|
||||||
_ -> time_1
|
_ -> time_1
|
||||||
end
|
end
|
||||||
|
|
||||||
Image
|
Image
|
||||||
|
@ -398,7 +404,7 @@ defmodule Philomena.Images do
|
||||||
def unhide_image(%Image{hidden_from_users: true} = image) do
|
def unhide_image(%Image{hidden_from_users: true} = image) do
|
||||||
key = image.hidden_image_key
|
key = image.hidden_image_key
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.update(:image, Image.unhide_changeset(image))
|
|> Multi.update(:image, Image.unhide_changeset(image))
|
||||||
|> Multi.run(:tags, fn repo, %{image: image} ->
|
|> Multi.run(:tags, fn repo, %{image: image} ->
|
||||||
image = Repo.preload(image, :tags, force: true)
|
image = Repo.preload(image, :tags, force: true)
|
||||||
|
@ -417,6 +423,7 @@ defmodule Philomena.Images do
|
||||||
end)
|
end)
|
||||||
|> Repo.isolated_transaction(:serializable)
|
|> Repo.isolated_transaction(:serializable)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unhide_image(image), do: {:ok, image}
|
def unhide_image(image), do: {:ok, image}
|
||||||
|
|
||||||
def batch_update(image_ids, added_tags, removed_tags, tag_change_attributes) do
|
def batch_update(image_ids, added_tags, removed_tags, tag_change_attributes) do
|
||||||
|
@ -445,26 +452,33 @@ defmodule Philomena.Images do
|
||||||
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
||||||
tag_change_attributes = Map.merge(tag_change_attributes, %{created_at: now, updated_at: now})
|
tag_change_attributes = Map.merge(tag_change_attributes, %{created_at: now, updated_at: now})
|
||||||
|
|
||||||
Repo.transaction fn ->
|
Repo.transaction(fn ->
|
||||||
{added_count, inserted} = Repo.insert_all(Tagging, insertions, on_conflict: :nothing, returning: [:image_id, :tag_id])
|
{added_count, inserted} =
|
||||||
|
Repo.insert_all(Tagging, insertions,
|
||||||
|
on_conflict: :nothing,
|
||||||
|
returning: [:image_id, :tag_id]
|
||||||
|
)
|
||||||
|
|
||||||
{removed_count, deleted} = Repo.delete_all(deletions)
|
{removed_count, deleted} = Repo.delete_all(deletions)
|
||||||
|
|
||||||
inserted = Enum.map(inserted, &[&1.image_id, &1.tag_id])
|
inserted = Enum.map(inserted, &[&1.image_id, &1.tag_id])
|
||||||
|
|
||||||
added_changes = Enum.map(inserted, fn [image_id, tag_id] ->
|
added_changes =
|
||||||
Map.merge(tag_change_attributes, %{image_id: image_id, tag_id: tag_id, added: true})
|
Enum.map(inserted, fn [image_id, tag_id] ->
|
||||||
end)
|
Map.merge(tag_change_attributes, %{image_id: image_id, tag_id: tag_id, added: true})
|
||||||
|
end)
|
||||||
|
|
||||||
removed_changes = Enum.map(deleted, fn [image_id, tag_id] ->
|
removed_changes =
|
||||||
Map.merge(tag_change_attributes, %{image_id: image_id, tag_id: tag_id, added: false})
|
Enum.map(deleted, fn [image_id, tag_id] ->
|
||||||
end)
|
Map.merge(tag_change_attributes, %{image_id: image_id, tag_id: tag_id, added: false})
|
||||||
|
end)
|
||||||
|
|
||||||
changes = added_changes ++ removed_changes
|
changes = added_changes ++ removed_changes
|
||||||
|
|
||||||
Repo.insert_all(TagChange, changes)
|
Repo.insert_all(TagChange, changes)
|
||||||
Repo.update_all(where(Tag, [t], t.id in ^added_tags), inc: [images_count: added_count])
|
Repo.update_all(where(Tag, [t], t.id in ^added_tags), inc: [images_count: added_count])
|
||||||
Repo.update_all(where(Tag, [t], t.id in ^removed_tags), inc: [images_count: -removed_count])
|
Repo.update_all(where(Tag, [t], t.id in ^removed_tags), inc: [images_count: -removed_count])
|
||||||
end
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -503,23 +517,33 @@ defmodule Philomena.Images do
|
||||||
end
|
end
|
||||||
|
|
||||||
def reindex_images(image_ids) do
|
def reindex_images(image_ids) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
Image
|
Image
|
||||||
|> preload(^indexing_preloads())
|
|> preload(^indexing_preloads())
|
||||||
|> where([i], i.id in ^image_ids)
|
|> where([i], i.id in ^image_ids)
|
||||||
|> Elasticsearch.reindex(Image)
|
|> Elasticsearch.reindex(Image)
|
||||||
end
|
end)
|
||||||
|
|
||||||
image_ids
|
image_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
def indexing_preloads do
|
def indexing_preloads do
|
||||||
[:user, :favers, :downvoters, :upvoters, :hiders, :deleter, :gallery_interactions, tags: [:aliases, :aliased_tag]]
|
[
|
||||||
|
:user,
|
||||||
|
:favers,
|
||||||
|
:downvoters,
|
||||||
|
:upvoters,
|
||||||
|
:hiders,
|
||||||
|
:deleter,
|
||||||
|
:gallery_interactions,
|
||||||
|
tags: [:aliases, :aliased_tag]
|
||||||
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
alias Philomena.Images.Subscription
|
alias Philomena.Images.Subscription
|
||||||
|
|
||||||
def subscribed?(_image, nil), do: false
|
def subscribed?(_image, nil), do: false
|
||||||
|
|
||||||
def subscribed?(image, user) do
|
def subscribed?(image, user) do
|
||||||
Subscription
|
Subscription
|
||||||
|> where(image_id: ^image.id, user_id: ^user.id)
|
|> where(image_id: ^image.id, user_id: ^user.id)
|
||||||
|
@ -539,6 +563,7 @@ defmodule Philomena.Images do
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def create_subscription(_image, nil), do: {:ok, nil}
|
def create_subscription(_image, nil), do: {:ok, nil}
|
||||||
|
|
||||||
def create_subscription(image, user) do
|
def create_subscription(image, user) do
|
||||||
%Subscription{image_id: image.id, user_id: user.id}
|
%Subscription{image_id: image.id, user_id: user.id}
|
||||||
|> Subscription.changeset(%{})
|
|> Subscription.changeset(%{})
|
||||||
|
@ -565,6 +590,7 @@ defmodule Philomena.Images do
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_notification(_image, nil), do: nil
|
def clear_notification(_image, nil), do: nil
|
||||||
|
|
||||||
def clear_notification(image, user) do
|
def clear_notification(image, user) do
|
||||||
Notifications.delete_unread_notification("Image", image.id, user)
|
Notifications.delete_unread_notification("Image", image.id, user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,7 +24,8 @@ defmodule Philomena.Images.Hider do
|
||||||
do: Path.join([image_thumbnail_root(), time_identifier(created_at), to_string(id)])
|
do: Path.join([image_thumbnail_root(), time_identifier(created_at), to_string(id)])
|
||||||
|
|
||||||
defp image_thumb_dir(%Image{created_at: created_at, id: id}, key),
|
defp image_thumb_dir(%Image{created_at: created_at, id: id}, key),
|
||||||
do: Path.join([image_thumbnail_root(), time_identifier(created_at), to_string(id) <> "-" <> key])
|
do:
|
||||||
|
Path.join([image_thumbnail_root(), time_identifier(created_at), to_string(id) <> "-" <> key])
|
||||||
|
|
||||||
defp time_identifier(time),
|
defp time_identifier(time),
|
||||||
do: Enum.join([time.year, time.month, time.day], "/")
|
do: Enum.join([time.year, time.month, time.day], "/")
|
||||||
|
|
|
@ -122,22 +122,41 @@ defmodule Philomena.Images.Image do
|
||||||
def image_changeset(image, attrs) do
|
def image_changeset(image, attrs) do
|
||||||
image
|
image
|
||||||
|> cast(attrs, [
|
|> cast(attrs, [
|
||||||
:image, :image_name, :image_width, :image_height, :image_size,
|
:image,
|
||||||
:image_format, :image_mime_type, :image_aspect_ratio,
|
:image_name,
|
||||||
:image_orig_sha512_hash, :image_sha512_hash, :uploaded_image,
|
:image_width,
|
||||||
:removed_image, :image_is_animated
|
:image_height,
|
||||||
])
|
:image_size,
|
||||||
|> validate_required([
|
:image_format,
|
||||||
:image, :image_width, :image_height, :image_size,
|
:image_mime_type,
|
||||||
:image_format, :image_mime_type, :image_aspect_ratio,
|
:image_aspect_ratio,
|
||||||
:image_orig_sha512_hash, :image_sha512_hash, :uploaded_image,
|
:image_orig_sha512_hash,
|
||||||
|
:image_sha512_hash,
|
||||||
|
:uploaded_image,
|
||||||
|
:removed_image,
|
||||||
:image_is_animated
|
:image_is_animated
|
||||||
])
|
])
|
||||||
|> validate_number(:image_size, greater_than: 0, less_than_or_equal_to: 26214400)
|
|> validate_required([
|
||||||
|
:image,
|
||||||
|
:image_width,
|
||||||
|
:image_height,
|
||||||
|
:image_size,
|
||||||
|
:image_format,
|
||||||
|
:image_mime_type,
|
||||||
|
:image_aspect_ratio,
|
||||||
|
:image_orig_sha512_hash,
|
||||||
|
:image_sha512_hash,
|
||||||
|
:uploaded_image,
|
||||||
|
:image_is_animated
|
||||||
|
])
|
||||||
|
|> validate_number(:image_size, greater_than: 0, less_than_or_equal_to: 26_214_400)
|
||||||
|> validate_number(:image_width, greater_than: 0, less_than_or_equal_to: 32767)
|
|> validate_number(:image_width, greater_than: 0, less_than_or_equal_to: 32767)
|
||||||
|> validate_number(:image_height, greater_than: 0, less_than_or_equal_to: 32767)
|
|> validate_number(:image_height, greater_than: 0, less_than_or_equal_to: 32767)
|
||||||
|> validate_length(:image_name, max: 255, count: :bytes)
|
|> validate_length(:image_name, max: 255, count: :bytes)
|
||||||
|> validate_inclusion(:image_mime_type, ~W(image/gif image/jpeg image/png image/svg+xml video/webm))
|
|> validate_inclusion(
|
||||||
|
:image_mime_type,
|
||||||
|
~W(image/gif image/jpeg image/png image/svg+xml video/webm)
|
||||||
|
)
|
||||||
|> unsafe_validate_unique([:image_orig_sha512_hash], Repo)
|
|> unsafe_validate_unique([:image_orig_sha512_hash], Repo)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -260,8 +279,7 @@ defmodule Philomena.Images.Image do
|
||||||
tags
|
tags
|
||||||
|> Enum.map_join(", ", & &1.name)
|
|> Enum.map_join(", ", & &1.name)
|
||||||
|
|
||||||
tag_ids =
|
tag_ids = tags |> Enum.map(& &1.id)
|
||||||
tags |> Enum.map(& &1.id)
|
|
||||||
|
|
||||||
aliases =
|
aliases =
|
||||||
Tag
|
Tag
|
||||||
|
|
|
@ -61,10 +61,12 @@ defmodule Philomena.Images.Query do
|
||||||
|
|
||||||
defp anonymous_fields do
|
defp anonymous_fields do
|
||||||
[
|
[
|
||||||
int_fields: ~W(id width height comment_count score upvotes downvotes faves uploader_id faved_by_id tag_count),
|
int_fields:
|
||||||
|
~W(id width height comment_count score upvotes downvotes faves uploader_id faved_by_id tag_count),
|
||||||
float_fields: ~W(aspect_ratio wilson_score),
|
float_fields: ~W(aspect_ratio wilson_score),
|
||||||
date_fields: ~W(created_at updated_at first_seen_at),
|
date_fields: ~W(created_at updated_at first_seen_at),
|
||||||
literal_fields: ~W(faved_by orig_sha512_hash sha512_hash uploader source_url original_format),
|
literal_fields:
|
||||||
|
~W(faved_by orig_sha512_hash sha512_hash uploader source_url original_format),
|
||||||
ngram_fields: ~W(description),
|
ngram_fields: ~W(description),
|
||||||
custom_fields: ~W(gallery_id),
|
custom_fields: ~W(gallery_id),
|
||||||
default_field: {"namespaced_tags.name", :term},
|
default_field: {"namespaced_tags.name", :term},
|
||||||
|
@ -79,30 +81,35 @@ defmodule Philomena.Images.Query do
|
||||||
defp user_fields do
|
defp user_fields do
|
||||||
fields = anonymous_fields()
|
fields = anonymous_fields()
|
||||||
|
|
||||||
Keyword.merge(fields, [
|
Keyword.merge(fields,
|
||||||
custom_fields: fields[:custom_fields] ++ ~W(my),
|
custom_fields: fields[:custom_fields] ++ ~W(my),
|
||||||
transforms: Map.merge(fields[:transforms], %{"my" => &user_my_transform/2})
|
transforms: Map.merge(fields[:transforms], %{"my" => &user_my_transform/2})
|
||||||
])
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp moderator_fields do
|
defp moderator_fields do
|
||||||
fields = user_fields()
|
fields = user_fields()
|
||||||
|
|
||||||
Keyword.merge(fields, [
|
Keyword.merge(fields,
|
||||||
int_fields: fields[:int_fields] ++ ~W(upvoted_by_id downvoted_by_id true_uploader_id hidden_by_id deleted_by_user_id),
|
int_fields:
|
||||||
literal_fields: fields[:literal_fields] ++ ~W(fingerprint upvoted_by downvoted_by true_uploader hidden_by deleted_by_user),
|
fields[:int_fields] ++
|
||||||
|
~W(upvoted_by_id downvoted_by_id true_uploader_id hidden_by_id deleted_by_user_id),
|
||||||
|
literal_fields:
|
||||||
|
fields[:literal_fields] ++
|
||||||
|
~W(fingerprint upvoted_by downvoted_by true_uploader hidden_by deleted_by_user),
|
||||||
ip_fields: ~W(ip),
|
ip_fields: ~W(ip),
|
||||||
bool_fields: ~W(deleted),
|
bool_fields: ~W(deleted),
|
||||||
aliases: Map.merge(fields[:aliases], %{
|
aliases:
|
||||||
"upvoted_by" => "upvoters",
|
Map.merge(fields[:aliases], %{
|
||||||
"downvoted_by" => "downvoters",
|
"upvoted_by" => "upvoters",
|
||||||
"upvoted_by_id" => "upvoter_ids",
|
"downvoted_by" => "downvoters",
|
||||||
"downvoted_by_id" => "downvoter_ids",
|
"upvoted_by_id" => "upvoter_ids",
|
||||||
"hidden_by" => "hidden_by_users",
|
"downvoted_by_id" => "downvoter_ids",
|
||||||
"hidden_by_id" => "hidden_by_user_ids",
|
"hidden_by" => "hidden_by_users",
|
||||||
"deleted" => "hidden_from_users"
|
"hidden_by_id" => "hidden_by_user_ids",
|
||||||
})
|
"deleted" => "hidden_from_users"
|
||||||
])
|
})
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp parse(fields, context, query_string) do
|
defp parse(fields, context, query_string) do
|
||||||
|
|
|
@ -9,12 +9,11 @@ defmodule Philomena.Images.TagDiffer do
|
||||||
old_set = to_set(old_tags)
|
old_set = to_set(old_tags)
|
||||||
new_set = to_set(new_tags)
|
new_set = to_set(new_tags)
|
||||||
|
|
||||||
tags = changeset |> get_field(:tags)
|
tags = changeset |> get_field(:tags)
|
||||||
added_tags = added_set(old_set, new_set)
|
added_tags = added_set(old_set, new_set)
|
||||||
removed_tags = removed_set(old_set, new_set)
|
removed_tags = removed_set(old_set, new_set)
|
||||||
|
|
||||||
{tags, actually_added, actually_removed} =
|
{tags, actually_added, actually_removed} = apply_changes(tags, added_tags, removed_tags)
|
||||||
apply_changes(tags, added_tags, removed_tags)
|
|
||||||
|
|
||||||
changeset
|
changeset
|
||||||
|> put_change(:added_tags, actually_added)
|
|> put_change(:added_tags, actually_added)
|
||||||
|
@ -34,8 +33,7 @@ defmodule Philomena.Images.TagDiffer do
|
||||||
|> List.flatten()
|
|> List.flatten()
|
||||||
|> to_set()
|
|> to_set()
|
||||||
|
|
||||||
added_and_implied_set =
|
added_and_implied_set = Map.merge(added_set, implied_set)
|
||||||
Map.merge(added_set, implied_set)
|
|
||||||
|
|
||||||
oc_set =
|
oc_set =
|
||||||
added_and_implied_set
|
added_and_implied_set
|
||||||
|
@ -51,6 +49,7 @@ defmodule Philomena.Images.TagDiffer do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_oc_tag([]), do: Map.new()
|
defp get_oc_tag([]), do: Map.new()
|
||||||
|
|
||||||
defp get_oc_tag(_any_oc_tag) do
|
defp get_oc_tag(_any_oc_tag) do
|
||||||
Tag
|
Tag
|
||||||
|> where(name: "oc")
|
|> where(name: "oc")
|
||||||
|
@ -82,8 +81,8 @@ defmodule Philomena.Images.TagDiffer do
|
||||||
tag_set
|
tag_set
|
||||||
|> Map.drop(Map.keys(desired_tags))
|
|> Map.drop(Map.keys(desired_tags))
|
||||||
|
|
||||||
tags = desired_tags |> to_tag_list()
|
tags = desired_tags |> to_tag_list()
|
||||||
actually_added = actually_added |> to_tag_list()
|
actually_added = actually_added |> to_tag_list()
|
||||||
actually_removed = actually_removed |> to_tag_list()
|
actually_removed = actually_removed |> to_tag_list()
|
||||||
|
|
||||||
{tags, actually_added, actually_removed}
|
{tags, actually_added, actually_removed}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
defmodule Philomena.Images.TagValidator do
|
defmodule Philomena.Images.TagValidator do
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
@safe_rating MapSet.new(["safe"])
|
@safe_rating MapSet.new(["safe"])
|
||||||
@sexual_ratings MapSet.new(["suggestive", "questionable", "explicit"])
|
@sexual_ratings MapSet.new(["suggestive", "questionable", "explicit"])
|
||||||
@horror_ratings MapSet.new(["semi-grimdark", "grimdark"])
|
@horror_ratings MapSet.new(["semi-grimdark", "grimdark"])
|
||||||
@gross_rating MapSet.new(["grotesque"])
|
@gross_rating MapSet.new(["grotesque"])
|
||||||
@empty MapSet.new()
|
@empty MapSet.new()
|
||||||
|
|
||||||
def validate_tags(changeset) do
|
def validate_tags(changeset) do
|
||||||
tags = changeset |> get_field(:tags)
|
tags = changeset |> get_field(:tags)
|
||||||
|
@ -14,7 +14,7 @@ defmodule Philomena.Images.TagValidator do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate_tag_input(changeset, tags) do
|
defp validate_tag_input(changeset, tags) do
|
||||||
tag_set = extract_names(tags)
|
tag_set = extract_names(tags)
|
||||||
rating_set = ratings(tag_set)
|
rating_set = ratings(tag_set)
|
||||||
|
|
||||||
changeset
|
changeset
|
||||||
|
@ -26,10 +26,10 @@ defmodule Philomena.Images.TagValidator do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp ratings(%MapSet{} = tag_set) do
|
defp ratings(%MapSet{} = tag_set) do
|
||||||
safe = MapSet.intersection(tag_set, @safe_rating)
|
safe = MapSet.intersection(tag_set, @safe_rating)
|
||||||
sexual = MapSet.intersection(tag_set, @sexual_ratings)
|
sexual = MapSet.intersection(tag_set, @sexual_ratings)
|
||||||
horror = MapSet.intersection(tag_set, @horror_ratings)
|
horror = MapSet.intersection(tag_set, @horror_ratings)
|
||||||
gross = MapSet.intersection(tag_set, @gross_rating)
|
gross = MapSet.intersection(tag_set, @gross_rating)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
safe: safe,
|
safe: safe,
|
||||||
|
@ -50,16 +50,20 @@ defmodule Philomena.Images.TagValidator do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate_has_rating(changeset, %{safe: s, sexual: x, horror: h, gross: g}) when s == @empty and x == @empty and h == @empty and g == @empty do
|
defp validate_has_rating(changeset, %{safe: s, sexual: x, horror: h, gross: g})
|
||||||
|
when s == @empty and x == @empty and h == @empty and g == @empty do
|
||||||
changeset
|
changeset
|
||||||
|> add_error(:tag_input, "must contain at least one rating tag")
|
|> add_error(:tag_input, "must contain at least one rating tag")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate_has_rating(changeset, _ratings), do: changeset
|
defp validate_has_rating(changeset, _ratings), do: changeset
|
||||||
|
|
||||||
defp validate_safe(changeset, %{safe: s, sexual: x, horror: h, gross: g}) when s != @empty and (x != @empty or h != @empty or g != @empty) do
|
defp validate_safe(changeset, %{safe: s, sexual: x, horror: h, gross: g})
|
||||||
|
when s != @empty and (x != @empty or h != @empty or g != @empty) do
|
||||||
changeset
|
changeset
|
||||||
|> add_error(:tag_input, "may not contain any other rating if safe")
|
|> add_error(:tag_input, "may not contain any other rating if safe")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate_safe(changeset, _ratings), do: changeset
|
defp validate_safe(changeset, _ratings), do: changeset
|
||||||
|
|
||||||
defp validate_sexual_exclusion(changeset, %{sexual: x}) do
|
defp validate_sexual_exclusion(changeset, %{sexual: x}) do
|
||||||
|
|
|
@ -22,7 +22,6 @@ defmodule Philomena.Images.Thumbnailer do
|
||||||
full: nil
|
full: nil
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def generate_thumbnails(image_id) do
|
def generate_thumbnails(image_id) do
|
||||||
image = Repo.get!(Image, image_id)
|
image = Repo.get!(Image, image_id)
|
||||||
file = image_file(image)
|
file = image_file(image)
|
||||||
|
@ -36,11 +35,9 @@ defmodule Philomena.Images.Thumbnailer do
|
||||||
recompute_meta(image, file, &Image.process_changeset/2)
|
recompute_meta(image, file, &Image.process_changeset/2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
defp apply_edit_script(image, changes),
|
defp apply_edit_script(image, changes),
|
||||||
do: Enum.map(changes, &apply_change(image, &1))
|
do: Enum.map(changes, &apply_change(image, &1))
|
||||||
|
|
||||||
|
|
||||||
defp apply_change(image, {:intensities, intensities}),
|
defp apply_change(image, {:intensities, intensities}),
|
||||||
do: ImageIntensities.create_image_intensity(image, intensities)
|
do: ImageIntensities.create_image_intensity(image, intensities)
|
||||||
|
|
||||||
|
@ -50,14 +47,12 @@ defmodule Philomena.Images.Thumbnailer do
|
||||||
defp apply_change(image, {:thumbnails, thumbnails}),
|
defp apply_change(image, {:thumbnails, thumbnails}),
|
||||||
do: Enum.map(thumbnails, &apply_thumbnail(image, image_thumb_dir(image), &1))
|
do: Enum.map(thumbnails, &apply_thumbnail(image, image_thumb_dir(image), &1))
|
||||||
|
|
||||||
|
|
||||||
defp apply_thumbnail(_image, thumb_dir, {:copy, new_file, destination}),
|
defp apply_thumbnail(_image, thumb_dir, {:copy, new_file, destination}),
|
||||||
do: copy(new_file, Path.join(thumb_dir, destination))
|
do: copy(new_file, Path.join(thumb_dir, destination))
|
||||||
|
|
||||||
defp apply_thumbnail(image, thumb_dir, {:symlink_original, destination}),
|
defp apply_thumbnail(image, thumb_dir, {:symlink_original, destination}),
|
||||||
do: symlink(image_file(image), Path.join(thumb_dir, destination))
|
do: symlink(image_file(image), Path.join(thumb_dir, destination))
|
||||||
|
|
||||||
|
|
||||||
defp generate_dupe_reports(image),
|
defp generate_dupe_reports(image),
|
||||||
do: DuplicateReports.generate_reports(image)
|
do: DuplicateReports.generate_reports(image)
|
||||||
|
|
||||||
|
@ -70,7 +65,6 @@ defmodule Philomena.Images.Thumbnailer do
|
||||||
|> Repo.update!()
|
|> Repo.update!()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Copy from source to destination, creating parent directories along
|
# Copy from source to destination, creating parent directories along
|
||||||
# the way and setting the appropriate permission bits when necessary.
|
# the way and setting the appropriate permission bits when necessary.
|
||||||
defp copy(source, destination) do
|
defp copy(source, destination) do
|
||||||
|
@ -109,7 +103,6 @@ defmodule Philomena.Images.Thumbnailer do
|
||||||
|> File.mkdir_p!()
|
|> File.mkdir_p!()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
defp image_file(%Image{image: image}),
|
defp image_file(%Image{image: image}),
|
||||||
do: Path.join(image_file_root(), image)
|
do: Path.join(image_file_root(), image)
|
||||||
|
|
||||||
|
@ -119,7 +112,6 @@ defmodule Philomena.Images.Thumbnailer do
|
||||||
defp time_identifier(time),
|
defp time_identifier(time),
|
||||||
do: Enum.join([time.year, time.month, time.day], "/")
|
do: Enum.join([time.year, time.month, time.day], "/")
|
||||||
|
|
||||||
|
|
||||||
defp image_file_root,
|
defp image_file_root,
|
||||||
do: Application.get_env(:philomena, :image_file_root)
|
do: Application.get_env(:philomena, :image_file_root)
|
||||||
|
|
||||||
|
|
|
@ -23,25 +23,45 @@ defmodule Philomena.Interactions do
|
||||||
|
|
||||||
hide_interactions =
|
hide_interactions =
|
||||||
ImageHide
|
ImageHide
|
||||||
|> select([h], %{image_id: h.image_id, user_id: h.user_id, interaction_type: ^"hidden", value: ^""})
|
|> select([h], %{
|
||||||
|
image_id: h.image_id,
|
||||||
|
user_id: h.user_id,
|
||||||
|
interaction_type: ^"hidden",
|
||||||
|
value: ^""
|
||||||
|
})
|
||||||
|> where([h], h.image_id in ^ids)
|
|> where([h], h.image_id in ^ids)
|
||||||
|> where(user_id: ^user.id)
|
|> where(user_id: ^user.id)
|
||||||
|
|
||||||
fave_interactions =
|
fave_interactions =
|
||||||
ImageFave
|
ImageFave
|
||||||
|> select([f], %{image_id: f.image_id, user_id: f.user_id, interaction_type: ^"faved", value: ^""})
|
|> select([f], %{
|
||||||
|
image_id: f.image_id,
|
||||||
|
user_id: f.user_id,
|
||||||
|
interaction_type: ^"faved",
|
||||||
|
value: ^""
|
||||||
|
})
|
||||||
|> where([f], f.image_id in ^ids)
|
|> where([f], f.image_id in ^ids)
|
||||||
|> where(user_id: ^user.id)
|
|> where(user_id: ^user.id)
|
||||||
|
|
||||||
upvote_interactions =
|
upvote_interactions =
|
||||||
ImageVote
|
ImageVote
|
||||||
|> select([v], %{image_id: v.image_id, user_id: v.user_id, interaction_type: ^"voted", value: ^"up"})
|
|> select([v], %{
|
||||||
|
image_id: v.image_id,
|
||||||
|
user_id: v.user_id,
|
||||||
|
interaction_type: ^"voted",
|
||||||
|
value: ^"up"
|
||||||
|
})
|
||||||
|> where([v], v.image_id in ^ids)
|
|> where([v], v.image_id in ^ids)
|
||||||
|> where(user_id: ^user.id, up: true)
|
|> where(user_id: ^user.id, up: true)
|
||||||
|
|
||||||
downvote_interactions =
|
downvote_interactions =
|
||||||
ImageVote
|
ImageVote
|
||||||
|> select([v], %{image_id: v.image_id, user_id: v.user_id, interaction_type: ^"voted", value: ^"down"})
|
|> select([v], %{
|
||||||
|
image_id: v.image_id,
|
||||||
|
user_id: v.user_id,
|
||||||
|
interaction_type: ^"voted",
|
||||||
|
value: ^"down"
|
||||||
|
})
|
||||||
|> where([v], v.image_id in ^ids)
|
|> where([v], v.image_id in ^ids)
|
||||||
|> where(user_id: ^user.id, up: false)
|
|> where(user_id: ^user.id, up: false)
|
||||||
|
|
||||||
|
@ -61,10 +81,20 @@ defmodule Philomena.Interactions do
|
||||||
|
|
||||||
new_hides = Enum.map(source.hiders, &%{image_id: target.id, user_id: &1.id, created_at: now})
|
new_hides = Enum.map(source.hiders, &%{image_id: target.id, user_id: &1.id, created_at: now})
|
||||||
new_faves = Enum.map(source.favers, &%{image_id: target.id, user_id: &1.id, created_at: now})
|
new_faves = Enum.map(source.favers, &%{image_id: target.id, user_id: &1.id, created_at: now})
|
||||||
new_upvotes = Enum.map(source.upvoters, &%{image_id: target.id, user_id: &1.id, created_at: now, up: true})
|
|
||||||
new_downvotes = Enum.map(source.downvoters, &%{image_id: target.id, user_id: &1.id, created_at: now, up: false})
|
|
||||||
|
|
||||||
Multi.new
|
new_upvotes =
|
||||||
|
Enum.map(
|
||||||
|
source.upvoters,
|
||||||
|
&%{image_id: target.id, user_id: &1.id, created_at: now, up: true}
|
||||||
|
)
|
||||||
|
|
||||||
|
new_downvotes =
|
||||||
|
Enum.map(
|
||||||
|
source.downvoters,
|
||||||
|
&%{image_id: target.id, user_id: &1.id, created_at: now, up: false}
|
||||||
|
)
|
||||||
|
|
||||||
|
Multi.new()
|
||||||
|> Multi.run(:hides, fn repo, %{} ->
|
|> Multi.run(:hides, fn repo, %{} ->
|
||||||
{count, nil} = repo.insert_all(ImageHide, new_hides, on_conflict: :nothing)
|
{count, nil} = repo.insert_all(ImageHide, new_hides, on_conflict: :nothing)
|
||||||
|
|
||||||
|
@ -85,7 +115,8 @@ defmodule Philomena.Interactions do
|
||||||
|
|
||||||
{:ok, count}
|
{:ok, count}
|
||||||
end)
|
end)
|
||||||
|> Multi.run(:image, fn repo, %{hides: hides, faves: faves, upvotes: upvotes, downvotes: downvotes} ->
|
|> Multi.run(:image, fn repo,
|
||||||
|
%{hides: hides, faves: faves, upvotes: upvotes, downvotes: downvotes} ->
|
||||||
image_query = where(Image, id: ^target.id)
|
image_query = where(Image, id: ^target.id)
|
||||||
|
|
||||||
repo.update_all(
|
repo.update_all(
|
||||||
|
@ -106,6 +137,7 @@ defmodule Philomena.Interactions do
|
||||||
|
|
||||||
defp union_all_queries([query]),
|
defp union_all_queries([query]),
|
||||||
do: query
|
do: query
|
||||||
|
|
||||||
defp union_all_queries([query | rest]),
|
defp union_all_queries([query | rest]),
|
||||||
do: query |> union_all(^union_all_queries(rest))
|
do: query |> union_all(^union_all_queries(rest))
|
||||||
end
|
end
|
|
@ -30,8 +30,8 @@ defmodule Philomena.Mime do
|
||||||
def true_mime("audio/webm"), do: {:ok, "video/webm"}
|
def true_mime("audio/webm"), do: {:ok, "video/webm"}
|
||||||
|
|
||||||
def true_mime(mime)
|
def true_mime(mime)
|
||||||
when mime in ~W(image/gif image/jpeg image/png image/svg+xml video/webm),
|
when mime in ~W(image/gif image/jpeg image/png image/svg+xml video/webm),
|
||||||
do: {:ok, mime}
|
do: {:ok, mime}
|
||||||
|
|
||||||
def true_mime(_mime), do: :error
|
def true_mime(_mime), do: :error
|
||||||
end
|
end
|
|
@ -185,13 +185,14 @@ defmodule Philomena.Notifications do
|
||||||
end
|
end
|
||||||
|
|
||||||
def notify(_actor_child, [], _params), do: nil
|
def notify(_actor_child, [], _params), do: nil
|
||||||
|
|
||||||
def notify(actor_child, subscriptions, params) do
|
def notify(actor_child, subscriptions, params) do
|
||||||
# Don't push to the user that created the notification
|
# Don't push to the user that created the notification
|
||||||
subscriptions =
|
subscriptions =
|
||||||
case actor_child do
|
case actor_child do
|
||||||
%{user_id: id} ->
|
%{user_id: id} ->
|
||||||
subscriptions
|
subscriptions
|
||||||
|> Enum.reject(& &1.user_id == id)
|
|> Enum.reject(&(&1.user_id == id))
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
subscriptions
|
subscriptions
|
||||||
|
|
|
@ -32,6 +32,7 @@ defmodule Philomena.PollOptions.PollOption do
|
||||||
|
|
||||||
defp ignore_if_blank(%{valid?: false, changes: changes} = changeset) when changes == %{},
|
defp ignore_if_blank(%{valid?: false, changes: changes} = changeset) when changes == %{},
|
||||||
do: %{changeset | action: :ignore}
|
do: %{changeset | action: :ignore}
|
||||||
|
|
||||||
defp ignore_if_blank(changeset),
|
defp ignore_if_blank(changeset),
|
||||||
do: changeset
|
do: changeset
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,13 +47,12 @@ defmodule Philomena.PollVotes do
|
||||||
|> Multi.run(:existing_votes, fn _repo, _changes ->
|
|> Multi.run(:existing_votes, fn _repo, _changes ->
|
||||||
# Don't proceed if any votes exist
|
# Don't proceed if any votes exist
|
||||||
case voted?(poll, user) do
|
case voted?(poll, user) do
|
||||||
true -> {:error, []}
|
true -> {:error, []}
|
||||||
_false -> {:ok, []}
|
_false -> {:ok, []}
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|> Multi.run(:new_votes, fn repo, _changes ->
|
|> Multi.run(:new_votes, fn repo, _changes ->
|
||||||
{_count, votes} =
|
{_count, votes} = repo.insert_all(PollVote, poll_votes, returning: true)
|
||||||
repo.insert_all(PollVote, poll_votes, returning: true)
|
|
||||||
|
|
||||||
{:ok, votes}
|
{:ok, votes}
|
||||||
end)
|
end)
|
||||||
|
@ -91,13 +90,15 @@ defmodule Philomena.PollVotes do
|
||||||
|
|
||||||
case poll.vote_method do
|
case poll.vote_method do
|
||||||
"single" -> Enum.take(votes, 1)
|
"single" -> Enum.take(votes, 1)
|
||||||
_other -> votes
|
_other -> votes
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp filter_options(_user, _poll, _now, _attrs), do: []
|
defp filter_options(_user, _poll, _now, _attrs), do: []
|
||||||
|
|
||||||
def voted?(nil, _user), do: false
|
def voted?(nil, _user), do: false
|
||||||
def voted?(_poll, nil), do: false
|
def voted?(_poll, nil), do: false
|
||||||
|
|
||||||
def voted?(%{id: poll_id}, %{id: user_id}) do
|
def voted?(%{id: poll_id}, %{id: user_id}) do
|
||||||
PollVote
|
PollVote
|
||||||
|> join(:inner, [pv], _ in assoc(pv, :poll_option))
|
|> join(:inner, [pv], _ in assoc(pv, :poll_option))
|
||||||
|
|
|
@ -44,6 +44,7 @@ defmodule Philomena.Polls.Poll do
|
||||||
|
|
||||||
defp ignore_if_blank(%{valid?: false, changes: changes} = changeset) when changes == %{},
|
defp ignore_if_blank(%{valid?: false, changes: changes} = changeset) when changes == %{},
|
||||||
do: %{changeset | action: :ignore}
|
do: %{changeset | action: :ignore}
|
||||||
|
|
||||||
defp ignore_if_blank(changeset),
|
defp ignore_if_blank(changeset),
|
||||||
do: changeset
|
do: changeset
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ defmodule Philomena.Polymorphic do
|
||||||
|
|
||||||
{type, ids} ->
|
{type, ids} ->
|
||||||
pre = Map.get(@preloads, type, [])
|
pre = Map.get(@preloads, type, [])
|
||||||
|
|
||||||
rows =
|
rows =
|
||||||
@classes[type]
|
@classes[type]
|
||||||
|> where([m], m.id in ^ids)
|
|> where([m], m.id in ^ids)
|
||||||
|
|
|
@ -55,7 +55,7 @@ defmodule Philomena.Posts do
|
||||||
Forum
|
Forum
|
||||||
|> where(id: ^topic.forum_id)
|
|> where(id: ^topic.forum_id)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.run(:post, fn repo, _ ->
|
|> Multi.run(:post, fn repo, _ ->
|
||||||
last_position =
|
last_position =
|
||||||
Post
|
Post
|
||||||
|
@ -71,7 +71,10 @@ defmodule Philomena.Posts do
|
||||||
end)
|
end)
|
||||||
|> Multi.run(:update_topic, fn repo, %{post: %{id: post_id}} ->
|
|> Multi.run(:update_topic, fn repo, %{post: %{id: post_id}} ->
|
||||||
{count, nil} =
|
{count, nil} =
|
||||||
repo.update_all(topic_query, inc: [post_count: 1], set: [last_post_id: post_id, last_replied_to_at: now])
|
repo.update_all(topic_query,
|
||||||
|
inc: [post_count: 1],
|
||||||
|
set: [last_post_id: post_id, last_replied_to_at: now]
|
||||||
|
)
|
||||||
|
|
||||||
{:ok, count}
|
{:ok, count}
|
||||||
end)
|
end)
|
||||||
|
@ -88,7 +91,7 @@ defmodule Philomena.Posts do
|
||||||
end
|
end
|
||||||
|
|
||||||
def notify_post(post) do
|
def notify_post(post) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
topic =
|
topic =
|
||||||
post
|
post
|
||||||
|> Repo.preload(:topic)
|
|> Repo.preload(:topic)
|
||||||
|
@ -110,7 +113,7 @@ defmodule Philomena.Posts do
|
||||||
action: "posted a new reply in"
|
action: "posted a new reply in"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
end
|
end)
|
||||||
|
|
||||||
post
|
post
|
||||||
end
|
end
|
||||||
|
@ -132,10 +135,9 @@ defmodule Philomena.Posts do
|
||||||
current_body = post.body
|
current_body = post.body
|
||||||
current_reason = post.edit_reason
|
current_reason = post.edit_reason
|
||||||
|
|
||||||
post_changes =
|
post_changes = Post.changeset(post, attrs, now)
|
||||||
Post.changeset(post, attrs, now)
|
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.update(:post, post_changes)
|
|> Multi.update(:post, post_changes)
|
||||||
|> Multi.run(:version, fn _repo, _changes ->
|
|> Multi.run(:version, fn _repo, _changes ->
|
||||||
Versions.create_version("Post", post.id, editor.id, %{
|
Versions.create_version("Post", post.id, editor.id, %{
|
||||||
|
@ -203,13 +205,13 @@ defmodule Philomena.Posts do
|
||||||
end
|
end
|
||||||
|
|
||||||
def reindex_post(%Post{} = post) do
|
def reindex_post(%Post{} = post) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
Post
|
Post
|
||||||
|> preload(^indexing_preloads())
|
|> preload(^indexing_preloads())
|
||||||
|> where(id: ^post.id)
|
|> where(id: ^post.id)
|
||||||
|> Repo.one()
|
|> Repo.one()
|
||||||
|> Elasticsearch.index_document(Post)
|
|> Elasticsearch.index_document(Post)
|
||||||
end
|
end)
|
||||||
|
|
||||||
post
|
post
|
||||||
end
|
end
|
||||||
|
|
|
@ -59,22 +59,22 @@ defmodule Philomena.Posts.Query do
|
||||||
defp user_fields do
|
defp user_fields do
|
||||||
fields = anonymous_fields()
|
fields = anonymous_fields()
|
||||||
|
|
||||||
Keyword.merge(fields, [
|
Keyword.merge(fields,
|
||||||
custom_fields: fields[:custom_fields] ++ ~W(my),
|
custom_fields: fields[:custom_fields] ++ ~W(my),
|
||||||
transforms: Map.merge(fields[:transforms], %{"my" => &user_my_transform/2})
|
transforms: Map.merge(fields[:transforms], %{"my" => &user_my_transform/2})
|
||||||
])
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp moderator_fields do
|
defp moderator_fields do
|
||||||
fields = user_fields()
|
fields = user_fields()
|
||||||
|
|
||||||
Keyword.merge(fields, [
|
Keyword.merge(fields,
|
||||||
literal_fields: ~W(forum_id topic_id user_id author fingerprint),
|
literal_fields: ~W(forum_id topic_id user_id author fingerprint),
|
||||||
ip_fields: ~W(ip),
|
ip_fields: ~W(ip),
|
||||||
bool_fields: ~W(anonymous deleted),
|
bool_fields: ~W(anonymous deleted),
|
||||||
custom_fields: fields[:custom_fields] -- ~W(author user_id),
|
custom_fields: fields[:custom_fields] -- ~W(author user_id),
|
||||||
transforms: Map.drop(fields[:transforms], ["user_id", "author"])
|
transforms: Map.drop(fields[:transforms], ["user_id", "author"])
|
||||||
])
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp parse(fields, context, query_string) do
|
defp parse(fields, context, query_string) do
|
||||||
|
|
|
@ -26,12 +26,10 @@ defmodule Philomena.Processors.Gif do
|
||||||
intensities
|
intensities
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
defp optimize(file) do
|
defp optimize(file) do
|
||||||
optimized = Briefly.create!(extname: ".gif")
|
optimized = Briefly.create!(extname: ".gif")
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} = System.cmd("gifsicle", ["--careful", "-O2", file, "-o", optimized])
|
||||||
System.cmd("gifsicle", ["--careful", "-O2", file, "-o", optimized])
|
|
||||||
|
|
||||||
optimized
|
optimized
|
||||||
end
|
end
|
||||||
|
@ -40,7 +38,18 @@ defmodule Philomena.Processors.Gif do
|
||||||
preview = Briefly.create!(extname: ".png")
|
preview = Briefly.create!(extname: ".png")
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-ss", to_string(duration / 2), "-frames:v", "1", preview])
|
System.cmd("ffmpeg", [
|
||||||
|
"-loglevel",
|
||||||
|
"0",
|
||||||
|
"-y",
|
||||||
|
"-i",
|
||||||
|
file,
|
||||||
|
"-ss",
|
||||||
|
to_string(duration / 2),
|
||||||
|
"-frames:v",
|
||||||
|
"1",
|
||||||
|
preview
|
||||||
|
])
|
||||||
|
|
||||||
preview
|
preview
|
||||||
end
|
end
|
||||||
|
@ -49,7 +58,16 @@ defmodule Philomena.Processors.Gif do
|
||||||
palette = Briefly.create!(extname: ".png")
|
palette = Briefly.create!(extname: ".png")
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-vf", "palettegen=stats_mode=diff", palette])
|
System.cmd("ffmpeg", [
|
||||||
|
"-loglevel",
|
||||||
|
"0",
|
||||||
|
"-y",
|
||||||
|
"-i",
|
||||||
|
file,
|
||||||
|
"-vf",
|
||||||
|
"palettegen=stats_mode=diff",
|
||||||
|
palette
|
||||||
|
])
|
||||||
|
|
||||||
palette
|
palette
|
||||||
end
|
end
|
||||||
|
@ -59,7 +77,12 @@ defmodule Philomena.Processors.Gif do
|
||||||
[{:symlink_original, "full.gif"}] ++ generate_videos(file)
|
[{:symlink_original, "full.gif"}] ++ generate_videos(file)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp scale_if_smaller(palette, file, {width, height}, {thumb_name, {target_width, target_height}}) do
|
defp scale_if_smaller(
|
||||||
|
palette,
|
||||||
|
file,
|
||||||
|
{width, height},
|
||||||
|
{thumb_name, {target_width, target_height}}
|
||||||
|
) do
|
||||||
if width > target_width or height > target_height do
|
if width > target_width or height > target_height do
|
||||||
scaled = scale(palette, file, {target_width, target_height})
|
scaled = scale(palette, file, {target_width, target_height})
|
||||||
|
|
||||||
|
@ -72,24 +95,72 @@ defmodule Philomena.Processors.Gif do
|
||||||
defp scale(palette, file, {width, height}) do
|
defp scale(palette, file, {width, height}) do
|
||||||
scaled = Briefly.create!(extname: ".gif")
|
scaled = Briefly.create!(extname: ".gif")
|
||||||
|
|
||||||
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease"
|
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease"
|
||||||
palette_filter = "paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle"
|
palette_filter = "paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle"
|
||||||
filter_graph = "[0:v] #{scale_filter} [x]; [x][1:v] #{palette_filter}"
|
filter_graph = "[0:v] #{scale_filter} [x]; [x][1:v] #{palette_filter}"
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-i", palette, "-lavfi", filter_graph, scaled])
|
System.cmd("ffmpeg", [
|
||||||
|
"-loglevel",
|
||||||
|
"0",
|
||||||
|
"-y",
|
||||||
|
"-i",
|
||||||
|
file,
|
||||||
|
"-i",
|
||||||
|
palette,
|
||||||
|
"-lavfi",
|
||||||
|
filter_graph,
|
||||||
|
scaled
|
||||||
|
])
|
||||||
|
|
||||||
scaled
|
scaled
|
||||||
end
|
end
|
||||||
|
|
||||||
defp generate_videos(file) do
|
defp generate_videos(file) do
|
||||||
webm = Briefly.create!(extname: ".webm")
|
webm = Briefly.create!(extname: ".webm")
|
||||||
mp4 = Briefly.create!(extname: ".mp4")
|
mp4 = Briefly.create!(extname: ".mp4")
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-pix_fmt", "yuv420p", "-c:v", "libvpx", "-quality", "good", "-b:v", "5M", webm])
|
System.cmd("ffmpeg", [
|
||||||
|
"-loglevel",
|
||||||
|
"0",
|
||||||
|
"-y",
|
||||||
|
"-i",
|
||||||
|
file,
|
||||||
|
"-pix_fmt",
|
||||||
|
"yuv420p",
|
||||||
|
"-c:v",
|
||||||
|
"libvpx",
|
||||||
|
"-quality",
|
||||||
|
"good",
|
||||||
|
"-b:v",
|
||||||
|
"5M",
|
||||||
|
webm
|
||||||
|
])
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-vf", "scale=ceil(iw/2)*2:ceil(ih/2)*2", "-c:v", "libx264", "-preset", "medium", "-pix_fmt", "yuv420p", "-profile:v", "main", "-crf", "18", "-b:v", "5M", mp4])
|
System.cmd("ffmpeg", [
|
||||||
|
"-loglevel",
|
||||||
|
"0",
|
||||||
|
"-y",
|
||||||
|
"-i",
|
||||||
|
file,
|
||||||
|
"-vf",
|
||||||
|
"scale=ceil(iw/2)*2:ceil(ih/2)*2",
|
||||||
|
"-c:v",
|
||||||
|
"libx264",
|
||||||
|
"-preset",
|
||||||
|
"medium",
|
||||||
|
"-pix_fmt",
|
||||||
|
"yuv420p",
|
||||||
|
"-profile:v",
|
||||||
|
"main",
|
||||||
|
"-crf",
|
||||||
|
"18",
|
||||||
|
"-b:v",
|
||||||
|
"5M",
|
||||||
|
mp4
|
||||||
|
])
|
||||||
|
|
||||||
[
|
[
|
||||||
{:copy, webm, "full.webm"},
|
{:copy, webm, "full.webm"},
|
||||||
|
|
|
@ -23,12 +23,10 @@ defmodule Philomena.Processors.Jpeg do
|
||||||
intensities
|
intensities
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
defp strip(file) do
|
defp strip(file) do
|
||||||
stripped = Briefly.create!(extname: ".jpg")
|
stripped = Briefly.create!(extname: ".jpg")
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} = System.cmd("convert", [file, "-auto-orient", "-strip", stripped])
|
||||||
System.cmd("convert", [file, "-auto-orient", "-strip", stripped])
|
|
||||||
|
|
||||||
stripped
|
stripped
|
||||||
end
|
end
|
||||||
|
@ -36,8 +34,7 @@ defmodule Philomena.Processors.Jpeg do
|
||||||
defp optimize(file) do
|
defp optimize(file) do
|
||||||
optimized = Briefly.create!(extname: ".jpg")
|
optimized = Briefly.create!(extname: ".jpg")
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} = System.cmd("jpegtran", ["-optimize", "-outfile", optimized, file])
|
||||||
System.cmd("jpegtran", ["-optimize", "-outfile", optimized, file])
|
|
||||||
|
|
||||||
optimized
|
optimized
|
||||||
end
|
end
|
||||||
|
@ -62,8 +59,8 @@ defmodule Philomena.Processors.Jpeg do
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-vf", scale_filter, scaled])
|
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-vf", scale_filter, scaled])
|
||||||
{_output, 0} =
|
|
||||||
System.cmd("jpegtran", ["-optimize", "-outfile", scaled, scaled])
|
{_output, 0} = System.cmd("jpegtran", ["-optimize", "-outfile", scaled, scaled])
|
||||||
|
|
||||||
scaled
|
scaled
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,7 +23,6 @@ defmodule Philomena.Processors.Png do
|
||||||
intensities
|
intensities
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
defp optimize(file) do
|
defp optimize(file) do
|
||||||
optimized = Briefly.create!(extname: ".png")
|
optimized = Briefly.create!(extname: ".png")
|
||||||
|
|
||||||
|
@ -52,12 +51,14 @@ defmodule Philomena.Processors.Png do
|
||||||
|
|
||||||
defp scale(file, {width, height}) do
|
defp scale(file, {width, height}) do
|
||||||
scaled = Briefly.create!(extname: ".png")
|
scaled = Briefly.create!(extname: ".png")
|
||||||
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease,format=rgb32"
|
|
||||||
|
scale_filter =
|
||||||
|
"scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease,format=rgb32"
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-vf", scale_filter, scaled])
|
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-vf", scale_filter, scaled])
|
||||||
{_output, 0} =
|
|
||||||
System.cmd("optipng", ["-i0", "-o1", "-quiet", "-clobber", scaled])
|
{_output, 0} = System.cmd("optipng", ["-i0", "-o1", "-quiet", "-clobber", scaled])
|
||||||
|
|
||||||
scaled
|
scaled
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,12 +21,10 @@ defmodule Philomena.Processors.Svg do
|
||||||
intensities
|
intensities
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
defp preview(file) do
|
defp preview(file) do
|
||||||
preview = Briefly.create!(extname: ".png")
|
preview = Briefly.create!(extname: ".png")
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} = System.cmd("safe-rsvg-convert", [file, preview])
|
||||||
System.cmd("safe-rsvg-convert", [file, preview])
|
|
||||||
|
|
||||||
preview
|
preview
|
||||||
end
|
end
|
||||||
|
@ -47,8 +45,8 @@ defmodule Philomena.Processors.Svg do
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", preview, "-vf", scale_filter, scaled])
|
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", preview, "-vf", scale_filter, scaled])
|
||||||
{_output, 0} =
|
|
||||||
System.cmd("optipng", ["-i0", "-o1", "-quiet", "-clobber", scaled])
|
{_output, 0} = System.cmd("optipng", ["-i0", "-o1", "-quiet", "-clobber", scaled])
|
||||||
|
|
||||||
scaled
|
scaled
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,12 +25,22 @@ defmodule Philomena.Processors.Webm do
|
||||||
intensities
|
intensities
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
defp preview(duration, file) do
|
defp preview(duration, file) do
|
||||||
preview = Briefly.create!(extname: ".png")
|
preview = Briefly.create!(extname: ".png")
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-ss", to_string(duration / 2), "-frames:v", "1", preview])
|
System.cmd("ffmpeg", [
|
||||||
|
"-loglevel",
|
||||||
|
"0",
|
||||||
|
"-y",
|
||||||
|
"-i",
|
||||||
|
file,
|
||||||
|
"-ss",
|
||||||
|
to_string(duration / 2),
|
||||||
|
"-frames:v",
|
||||||
|
"1",
|
||||||
|
preview
|
||||||
|
])
|
||||||
|
|
||||||
preview
|
preview
|
||||||
end
|
end
|
||||||
|
@ -44,7 +54,13 @@ defmodule Philomena.Processors.Webm do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp scale_if_smaller(file, palette, duration, dimensions, {thumb_name, {target_width, target_height}}) do
|
defp scale_if_smaller(
|
||||||
|
file,
|
||||||
|
palette,
|
||||||
|
duration,
|
||||||
|
dimensions,
|
||||||
|
{thumb_name, {target_width, target_height}}
|
||||||
|
) do
|
||||||
{webm, mp4} = scale_videos(file, palette, dimensions, {target_width, target_height})
|
{webm, mp4} = scale_videos(file, palette, dimensions, {target_width, target_height})
|
||||||
|
|
||||||
cond do
|
cond do
|
||||||
|
@ -68,26 +84,82 @@ defmodule Philomena.Processors.Webm do
|
||||||
defp scale_videos(file, _palette, dimensions, target_dimensions) do
|
defp scale_videos(file, _palette, dimensions, target_dimensions) do
|
||||||
{width, height} = box_dimensions(dimensions, target_dimensions)
|
{width, height} = box_dimensions(dimensions, target_dimensions)
|
||||||
webm = Briefly.create!(extname: ".webm")
|
webm = Briefly.create!(extname: ".webm")
|
||||||
mp4 = Briefly.create!(extname: ".mp4")
|
mp4 = Briefly.create!(extname: ".mp4")
|
||||||
scale_filter = "scale=w=#{width}:h=#{height}"
|
scale_filter = "scale=w=#{width}:h=#{height}"
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-c:v", "libvpx", "-quality", "good", "-cpu-used", "3", "-auto-alt-ref", "0", "-crf", "10", "-b:v", "5M", "-vf", scale_filter, webm])
|
System.cmd("ffmpeg", [
|
||||||
|
"-loglevel",
|
||||||
|
"0",
|
||||||
|
"-y",
|
||||||
|
"-i",
|
||||||
|
file,
|
||||||
|
"-c:v",
|
||||||
|
"libvpx",
|
||||||
|
"-quality",
|
||||||
|
"good",
|
||||||
|
"-cpu-used",
|
||||||
|
"3",
|
||||||
|
"-auto-alt-ref",
|
||||||
|
"0",
|
||||||
|
"-crf",
|
||||||
|
"10",
|
||||||
|
"-b:v",
|
||||||
|
"5M",
|
||||||
|
"-vf",
|
||||||
|
scale_filter,
|
||||||
|
webm
|
||||||
|
])
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-c:v", "libx264", "-pix_fmt", "yuv420p", "-profile:v", "main", "-preset", "medium", "-crf", "18", "-b:v", "5M", "-vf", scale_filter, mp4])
|
System.cmd("ffmpeg", [
|
||||||
|
"-loglevel",
|
||||||
|
"0",
|
||||||
|
"-y",
|
||||||
|
"-i",
|
||||||
|
file,
|
||||||
|
"-c:v",
|
||||||
|
"libx264",
|
||||||
|
"-pix_fmt",
|
||||||
|
"yuv420p",
|
||||||
|
"-profile:v",
|
||||||
|
"main",
|
||||||
|
"-preset",
|
||||||
|
"medium",
|
||||||
|
"-crf",
|
||||||
|
"18",
|
||||||
|
"-b:v",
|
||||||
|
"5M",
|
||||||
|
"-vf",
|
||||||
|
scale_filter,
|
||||||
|
mp4
|
||||||
|
])
|
||||||
|
|
||||||
{webm, mp4}
|
{webm, mp4}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp scale_gif(file, palette, duration, {width, height}) do
|
defp scale_gif(file, palette, duration, {width, height}) do
|
||||||
gif = Briefly.create!(extname: ".gif")
|
gif = Briefly.create!(extname: ".gif")
|
||||||
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease"
|
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease"
|
||||||
palette_filter = "paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle"
|
palette_filter = "paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle"
|
||||||
rate_filter = "fps=1/#{duration / 10},settb=1/2,setpts=N"
|
rate_filter = "fps=1/#{duration / 10},settb=1/2,setpts=N"
|
||||||
filter_graph = "[0:v] #{scale_filter},#{rate_filter} [x]; [x][1:v] #{palette_filter}"
|
filter_graph = "[0:v] #{scale_filter},#{rate_filter} [x]; [x][1:v] #{palette_filter}"
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-i", palette, "-lavfi", filter_graph, "-r", "2", gif])
|
System.cmd("ffmpeg", [
|
||||||
|
"-loglevel",
|
||||||
|
"0",
|
||||||
|
"-y",
|
||||||
|
"-i",
|
||||||
|
file,
|
||||||
|
"-i",
|
||||||
|
palette,
|
||||||
|
"-lavfi",
|
||||||
|
filter_graph,
|
||||||
|
"-r",
|
||||||
|
"2",
|
||||||
|
gif
|
||||||
|
])
|
||||||
|
|
||||||
gif
|
gif
|
||||||
end
|
end
|
||||||
|
@ -96,7 +168,16 @@ defmodule Philomena.Processors.Webm do
|
||||||
palette = Briefly.create!(extname: ".png")
|
palette = Briefly.create!(extname: ".png")
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-vf", "palettegen=stats_mode=diff", palette])
|
System.cmd("ffmpeg", [
|
||||||
|
"-loglevel",
|
||||||
|
"0",
|
||||||
|
"-y",
|
||||||
|
"-i",
|
||||||
|
file,
|
||||||
|
"-vf",
|
||||||
|
"palettegen=stats_mode=diff",
|
||||||
|
palette
|
||||||
|
])
|
||||||
|
|
||||||
palette
|
palette
|
||||||
end
|
end
|
||||||
|
@ -104,8 +185,8 @@ defmodule Philomena.Processors.Webm do
|
||||||
# x264 requires image dimensions to be a multiple of 2
|
# x264 requires image dimensions to be a multiple of 2
|
||||||
# -2 = ~1
|
# -2 = ~1
|
||||||
def box_dimensions({width, height}, {target_width, target_height}) do
|
def box_dimensions({width, height}, {target_width, target_height}) do
|
||||||
ratio = min(target_width / width, target_height / height)
|
ratio = min(target_width / width, target_height / height)
|
||||||
new_width = min(max(trunc(width * ratio) &&& -2, 2), target_width)
|
new_width = min(max(trunc(width * ratio) &&& -2, 2), target_width)
|
||||||
new_height = min(max(trunc(height * ratio) &&& -2, 2), target_height)
|
new_height = min(max(trunc(height * ratio) &&& -2, 2), target_height)
|
||||||
|
|
||||||
{new_width, new_height}
|
{new_width, new_height}
|
||||||
|
|
|
@ -15,7 +15,8 @@ defmodule Philomena.Repo do
|
||||||
|
|
||||||
def isolated_transaction(%Multi{} = multi, level) do
|
def isolated_transaction(%Multi{} = multi, level) do
|
||||||
Multi.append(
|
Multi.append(
|
||||||
Multi.new |> Multi.run(:isolate, fn repo, _chg ->
|
Multi.new()
|
||||||
|
|> Multi.run(:isolate, fn repo, _chg ->
|
||||||
repo.query!("SET TRANSACTION ISOLATION LEVEL #{@levels[level]}")
|
repo.query!("SET TRANSACTION ISOLATION LEVEL #{@levels[level]}")
|
||||||
{:ok, nil}
|
{:ok, nil}
|
||||||
end),
|
end),
|
||||||
|
|
|
@ -123,14 +123,14 @@ defmodule Philomena.Reports do
|
||||||
end
|
end
|
||||||
|
|
||||||
def reindex_reports(report_ids) do
|
def reindex_reports(report_ids) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
Report
|
Report
|
||||||
|> where([r], r.id in ^report_ids)
|
|> where([r], r.id in ^report_ids)
|
||||||
|> preload([:user, :admin])
|
|> preload([:user, :admin])
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|> Polymorphic.load_polymorphic(reportable: [reportable_id: :reportable_type])
|
|> Polymorphic.load_polymorphic(reportable: [reportable_id: :reportable_type])
|
||||||
|> Enum.map(&Elasticsearch.index_document(&1, Report))
|
|> Enum.map(&Elasticsearch.index_document(&1, Report))
|
||||||
end
|
end)
|
||||||
|
|
||||||
report_ids
|
report_ids
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,8 @@ defmodule Philomena.Reports.Query do
|
||||||
[
|
[
|
||||||
int_fields: ~W(id image_id),
|
int_fields: ~W(id image_id),
|
||||||
date_fields: ~W(created_at),
|
date_fields: ~W(created_at),
|
||||||
literal_fields: ~W(state user user_id admin admin_id reportable_type reportable_id fingerprint),
|
literal_fields:
|
||||||
|
~W(state user user_id admin admin_id reportable_type reportable_id fingerprint),
|
||||||
ip_fields: ~W(ip),
|
ip_fields: ~W(ip),
|
||||||
bool_fields: ~W(open),
|
bool_fields: ~W(open),
|
||||||
ngram_fields: ~W(reason),
|
ngram_fields: ~W(reason),
|
||||||
|
|
|
@ -61,7 +61,15 @@ defmodule Philomena.Reports.Report do
|
||||||
|> cast(attrs, [:category, :reason])
|
|> cast(attrs, [:category, :reason])
|
||||||
|> merge_category()
|
|> merge_category()
|
||||||
|> change(attribution)
|
|> change(attribution)
|
||||||
|> validate_required([:reportable_id, :reportable_type, :category, :reason, :ip, :fingerprint, :user_agent])
|
|> validate_required([
|
||||||
|
:reportable_id,
|
||||||
|
:reportable_type,
|
||||||
|
:category,
|
||||||
|
:reason,
|
||||||
|
:ip,
|
||||||
|
:fingerprint,
|
||||||
|
:user_agent
|
||||||
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
defp merge_category(changeset) do
|
defp merge_category(changeset) do
|
||||||
|
|
|
@ -6,5 +6,6 @@ defmodule Philomena.Schema.BanId do
|
||||||
|
|
||||||
put_change(changeset, :generated_ban_id, "#{prefix}#{ban_id}")
|
put_change(changeset, :generated_ban_id, "#{prefix}#{ban_id}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def put_ban_id(changeset, _prefix), do: changeset
|
def put_ban_id(changeset, _prefix), do: changeset
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,6 +47,6 @@ defmodule Philomena.Schema.TagList do
|
||||||
(list || "")
|
(list || "")
|
||||||
|> String.split(",")
|
|> String.split(",")
|
||||||
|> Enum.map(&String.trim(&1))
|
|> Enum.map(&String.trim(&1))
|
||||||
|> Enum.filter(& &1 != "")
|
|> Enum.filter(&(&1 != ""))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,7 +48,8 @@ defmodule Philomena.Scrapers.Deviantart do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp try_intermediary_hires!(%{images: [image]} = data) do
|
defp try_intermediary_hires!(%{images: [image]} = data) do
|
||||||
[domain, object_uuid, object_name] = Regex.run(@cdnint_regex, image.url, capture: :all_but_first)
|
[domain, object_uuid, object_name] =
|
||||||
|
Regex.run(@cdnint_regex, image.url, capture: :all_but_first)
|
||||||
|
|
||||||
built_url = "#{domain}/intermediary/f/#{object_uuid}/#{object_name}"
|
built_url = "#{domain}/intermediary/f/#{object_uuid}/#{object_name}"
|
||||||
|
|
||||||
|
@ -57,13 +58,13 @@ defmodule Philomena.Scrapers.Deviantart do
|
||||||
# This is the high resolution URL.
|
# This is the high resolution URL.
|
||||||
|
|
||||||
%{
|
%{
|
||||||
data |
|
data
|
||||||
images: [
|
| images: [
|
||||||
%{
|
%{
|
||||||
url: built_url,
|
url: built_url,
|
||||||
camo_url: image.camo_url
|
camo_url: image.camo_url
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -76,24 +77,24 @@ defmodule Philomena.Scrapers.Deviantart do
|
||||||
cond do
|
cond do
|
||||||
String.match?(image.url, @png_regex) ->
|
String.match?(image.url, @png_regex) ->
|
||||||
%{
|
%{
|
||||||
data |
|
data
|
||||||
images: [
|
| images: [
|
||||||
%{
|
%{
|
||||||
url: String.replace(image.url, @png_regex, "\\1.png\\3"),
|
url: String.replace(image.url, @png_regex, "\\1.png\\3"),
|
||||||
camo_url: image.camo_url
|
camo_url: image.camo_url
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
String.match?(image.url, @jpg_regex) ->
|
String.match?(image.url, @jpg_regex) ->
|
||||||
%{
|
%{
|
||||||
data |
|
data
|
||||||
images: [
|
| images: [
|
||||||
%{
|
%{
|
||||||
url: String.replace(image.url, @jpg_regex, "\\g{1}100\\3"),
|
url: String.replace(image.url, @jpg_regex, "\\g{1}100\\3"),
|
||||||
camo_url: image.camo_url
|
camo_url: image.camo_url
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
|
@ -104,6 +105,7 @@ defmodule Philomena.Scrapers.Deviantart do
|
||||||
|
|
||||||
defp try_old_hires!(%{source_url: source, images: [image]} = data) do
|
defp try_old_hires!(%{source_url: source, images: [image]} = data) do
|
||||||
[serial] = Regex.run(@serial_regex, source, capture: :all_but_first)
|
[serial] = Regex.run(@serial_regex, source, capture: :all_but_first)
|
||||||
|
|
||||||
base36 =
|
base36 =
|
||||||
serial
|
serial
|
||||||
|> String.to_integer()
|
|> String.to_integer()
|
||||||
|
@ -118,13 +120,13 @@ defmodule Philomena.Scrapers.Deviantart do
|
||||||
{_location, link} = Enum.find(headers, fn {header, _val} -> header == "Location" end)
|
{_location, link} = Enum.find(headers, fn {header, _val} -> header == "Location" end)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
data |
|
data
|
||||||
images: [
|
| images: [
|
||||||
%{
|
%{
|
||||||
url: link,
|
url: link,
|
||||||
camo_url: image.camo_url
|
camo_url: image.camo_url
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -135,6 +137,7 @@ defmodule Philomena.Scrapers.Deviantart do
|
||||||
|
|
||||||
# Workaround for benoitc/hackney#273
|
# Workaround for benoitc/hackney#273
|
||||||
defp follow_redirect(_url, 0), do: nil
|
defp follow_redirect(_url, 0), do: nil
|
||||||
|
|
||||||
defp follow_redirect(url, max_times) do
|
defp follow_redirect(url, max_times) do
|
||||||
case Philomena.Http.get!(url) do
|
case Philomena.Http.get!(url) do
|
||||||
%HTTPoison.Response{headers: headers, status_code: code} when code in [301, 302] ->
|
%HTTPoison.Response{headers: headers, status_code: code} when code in [301, 302] ->
|
||||||
|
|
|
@ -16,7 +16,10 @@ defmodule Philomena.Scrapers.Tumblr do
|
||||||
def scrape(uri, url) do
|
def scrape(uri, url) do
|
||||||
[post_id] = Regex.run(@url_regex, url, capture: :all_but_first)
|
[post_id] = Regex.run(@url_regex, url, capture: :all_but_first)
|
||||||
|
|
||||||
api_url = "https://api.tumblr.com/v2/blog/#{uri.host}/posts/photo?id=#{post_id}&api_key=#{tumblr_api_key()}"
|
api_url =
|
||||||
|
"https://api.tumblr.com/v2/blog/#{uri.host}/posts/photo?id=#{post_id}&api_key=#{
|
||||||
|
tumblr_api_key()
|
||||||
|
}"
|
||||||
|
|
||||||
Philomena.Http.get!(api_url)
|
Philomena.Http.get!(api_url)
|
||||||
|> json!()
|
|> json!()
|
||||||
|
@ -36,7 +39,7 @@ defmodule Philomena.Scrapers.Tumblr do
|
||||||
image = upsize(photo["original_size"]["url"])
|
image = upsize(photo["original_size"]["url"])
|
||||||
|
|
||||||
%{"url" => preview} =
|
%{"url" => preview} =
|
||||||
Enum.find(photo["alt_sizes"], & &1["width"] == 400) || %{"url" => image}
|
Enum.find(photo["alt_sizes"], &(&1["width"] == 400)) || %{"url" => image}
|
||||||
|
|
||||||
%{url: image, camo_url: Camo.Image.image_url(preview)}
|
%{url: image, camo_url: Camo.Image.image_url(preview)}
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -17,7 +17,12 @@ defmodule Philomena.Scrapers.Twitter do
|
||||||
defp extract_data(tweet) do
|
defp extract_data(tweet) do
|
||||||
images =
|
images =
|
||||||
tweet["entities"]["media"]
|
tweet["entities"]["media"]
|
||||||
|> Enum.map(&%{url: &1["media_url_https"] <> ":orig", camo_url: Camo.Image.image_url(&1["media_url_https"])})
|
|> Enum.map(
|
||||||
|
&%{
|
||||||
|
url: &1["media_url_https"] <> ":orig",
|
||||||
|
camo_url: Camo.Image.image_url(&1["media_url_https"])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
source_url: tweet["url"],
|
source_url: tweet["url"],
|
||||||
|
@ -34,7 +39,10 @@ defmodule Philomena.Scrapers.Twitter do
|
||||||
[user, status_id] = Regex.run(@url_regex, url, capture: :all_but_first)
|
[user, status_id] = Regex.run(@url_regex, url, capture: :all_but_first)
|
||||||
|
|
||||||
mobile_url = "https://mobile.twitter.com/#{user}/status/#{status_id}"
|
mobile_url = "https://mobile.twitter.com/#{user}/status/#{status_id}"
|
||||||
api_url = "https://api.twitter.com/2/timeline/conversation/#{status_id}.json?tweet_mode=extended"
|
|
||||||
|
api_url =
|
||||||
|
"https://api.twitter.com/2/timeline/conversation/#{status_id}.json?tweet_mode=extended"
|
||||||
|
|
||||||
url = "https://twitter.com/#{user}/status/#{status_id}"
|
url = "https://twitter.com/#{user}/status/#{status_id}"
|
||||||
|
|
||||||
{gt, bearer} =
|
{gt, bearer} =
|
||||||
|
@ -42,7 +50,7 @@ defmodule Philomena.Scrapers.Twitter do
|
||||||
|> Map.get(:body)
|
|> Map.get(:body)
|
||||||
|> extract_guest_token_and_bearer()
|
|> extract_guest_token_and_bearer()
|
||||||
|
|
||||||
Philomena.Http.get!(api_url, ["Authorization": "Bearer #{bearer}", "x-guest-token": gt])
|
Philomena.Http.get!(api_url, Authorization: "Bearer #{bearer}", "x-guest-token": gt)
|
||||||
|> Map.get(:body)
|
|> Map.get(:body)
|
||||||
|> Jason.decode!()
|
|> Jason.decode!()
|
||||||
|> Map.get("globalObjects")
|
|> Map.get("globalObjects")
|
||||||
|
|
|
@ -43,12 +43,12 @@ defmodule Philomena.Servers.Config do
|
||||||
defp maybe_update_state(state, key, false) do
|
defp maybe_update_state(state, key, false) do
|
||||||
Map.put(state, key, load_config(key))
|
Map.put(state, key, load_config(key))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_update_state(state, _key, _true), do: state
|
defp maybe_update_state(state, _key, _true), do: state
|
||||||
|
|
||||||
defp load_config(name) do
|
defp load_config(name) do
|
||||||
with {:ok, text} <- File.read("#{app_dir()}/config/#{name}.json"),
|
with {:ok, text} <- File.read("#{app_dir()}/config/#{name}.json"),
|
||||||
{:ok, json} <- Jason.decode(text)
|
{:ok, json} <- Jason.decode(text) do
|
||||||
do
|
|
||||||
json
|
json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,10 +14,12 @@ defmodule Philomena.Servers.UserFingerprintUpdater do
|
||||||
{:ok, spawn_link(&init/0)}
|
{:ok, spawn_link(&init/0)}
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast(user_id, <<"c", _rest::binary>> = fingerprint, updated_at) when byte_size(fingerprint) <= 12 do
|
def cast(user_id, <<"c", _rest::binary>> = fingerprint, updated_at)
|
||||||
|
when byte_size(fingerprint) <= 12 do
|
||||||
pid = Process.whereis(:fingerprint_updater)
|
pid = Process.whereis(:fingerprint_updater)
|
||||||
if pid, do: send(pid, {user_id, fingerprint, updated_at})
|
if pid, do: send(pid, {user_id, fingerprint, updated_at})
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast(_user_id, _fingerprint, _updated_at), do: nil
|
def cast(_user_id, _fingerprint, _updated_at), do: nil
|
||||||
|
|
||||||
defp init do
|
defp init do
|
||||||
|
@ -27,9 +29,14 @@ defmodule Philomena.Servers.UserFingerprintUpdater do
|
||||||
|
|
||||||
defp run do
|
defp run do
|
||||||
user_fps = Enum.map(receive_all(), &into_insert_all/1)
|
user_fps = Enum.map(receive_all(), &into_insert_all/1)
|
||||||
update_query = update(UserFingerprint, inc: [uses: 1], set: [updated_at: fragment("EXCLUDED.updated_at")])
|
|
||||||
|
|
||||||
Repo.insert_all(UserFingerprint, user_fps, on_conflict: update_query, conflict_target: [:user_id, :fingerprint])
|
update_query =
|
||||||
|
update(UserFingerprint, inc: [uses: 1], set: [updated_at: fragment("EXCLUDED.updated_at")])
|
||||||
|
|
||||||
|
Repo.insert_all(UserFingerprint, user_fps,
|
||||||
|
on_conflict: update_query,
|
||||||
|
conflict_target: [:user_id, :fingerprint]
|
||||||
|
)
|
||||||
|
|
||||||
:timer.sleep(:timer.seconds(60))
|
:timer.sleep(:timer.seconds(60))
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,9 @@ defmodule Philomena.Servers.UserIpUpdater do
|
||||||
|
|
||||||
defp run do
|
defp run do
|
||||||
user_ips = Enum.map(receive_all(), &into_insert_all/1)
|
user_ips = Enum.map(receive_all(), &into_insert_all/1)
|
||||||
update_query = update(UserIp, inc: [uses: 1], set: [updated_at: fragment("EXCLUDED.updated_at")])
|
|
||||||
|
update_query =
|
||||||
|
update(UserIp, inc: [uses: 1], set: [updated_at: fragment("EXCLUDED.updated_at")])
|
||||||
|
|
||||||
Repo.insert_all(UserIp, user_ips, on_conflict: update_query, conflict_target: [:user_id, :ip])
|
Repo.insert_all(UserIp, user_ips, on_conflict: update_query, conflict_target: [:user_id, :ip])
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,11 @@ defmodule Philomena.Servers.UserLinkUpdater do
|
||||||
alias Philomena.Repo
|
alias Philomena.Repo
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
@seven_days 7*24*60*60
|
@seven_days 7 * 24 * 60 * 60
|
||||||
@three_days 3*24*60*60
|
@three_days 3 * 24 * 60 * 60
|
||||||
@twelve_hours 12*60*60
|
@twelve_hours 12 * 60 * 60
|
||||||
@one_hour 60*60
|
@one_hour 60 * 60
|
||||||
@two_minutes 2*60
|
@two_minutes 2 * 60
|
||||||
|
|
||||||
def child_spec([]) do
|
def child_spec([]) do
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -27,11 +27,16 @@ defmodule Philomena.Slug do
|
||||||
@spec destructive_slug(String.t()) :: String.t()
|
@spec destructive_slug(String.t()) :: String.t()
|
||||||
def destructive_slug(input) when is_binary(input) do
|
def destructive_slug(input) when is_binary(input) do
|
||||||
input
|
input
|
||||||
|> String.replace(~r/[^ -~]/, "") # 1
|
# 1
|
||||||
|> String.replace(~r/[^a-zA-Z0-9]+/, "-") # 2
|
|> String.replace(~r/[^ -~]/, "")
|
||||||
|> String.replace(~r/\A-|-\z/, "") # 3
|
# 2
|
||||||
|> String.downcase() # 4
|
|> String.replace(~r/[^a-zA-Z0-9]+/, "-")
|
||||||
|
# 3
|
||||||
|
|> String.replace(~r/\A-|-\z/, "")
|
||||||
|
# 4
|
||||||
|
|> String.downcase()
|
||||||
end
|
end
|
||||||
|
|
||||||
def destructive_slug(_input), do: ""
|
def destructive_slug(_input), do: ""
|
||||||
|
|
||||||
def slug(string) when is_binary(string) do
|
def slug(string) when is_binary(string) do
|
||||||
|
|
|
@ -42,22 +42,25 @@ defmodule Philomena.TagChanges do
|
||||||
end)
|
end)
|
||||||
|> select([t], [t.image_id, t.tag_id])
|
|> select([t], [t.image_id, t.tag_id])
|
||||||
|
|
||||||
to_add =
|
to_add = Enum.map(removed, &%{image_id: &1.image_id, tag_id: &1.tag_id})
|
||||||
Enum.map(removed, &%{image_id: &1.image_id, tag_id: &1.tag_id})
|
|
||||||
|
|
||||||
Repo.transaction(fn ->
|
Repo.transaction(fn ->
|
||||||
{_count, inserted} = Repo.insert_all(Tagging, to_add, on_conflict: :nothing, returning: [:image_id, :tag_id])
|
{_count, inserted} =
|
||||||
|
Repo.insert_all(Tagging, to_add, on_conflict: :nothing, returning: [:image_id, :tag_id])
|
||||||
|
|
||||||
{_count, deleted} = Repo.delete_all(to_remove)
|
{_count, deleted} = Repo.delete_all(to_remove)
|
||||||
|
|
||||||
inserted = Enum.map(inserted, &[&1.image_id, &1.tag_id])
|
inserted = Enum.map(inserted, &[&1.image_id, &1.tag_id])
|
||||||
|
|
||||||
added_changes = Enum.map(inserted, fn [image_id, tag_id] ->
|
added_changes =
|
||||||
Map.merge(tag_change_attributes, %{image_id: image_id, tag_id: tag_id, added: true})
|
Enum.map(inserted, fn [image_id, tag_id] ->
|
||||||
end)
|
Map.merge(tag_change_attributes, %{image_id: image_id, tag_id: tag_id, added: true})
|
||||||
|
end)
|
||||||
|
|
||||||
removed_changes = Enum.map(deleted, fn [image_id, tag_id] ->
|
removed_changes =
|
||||||
Map.merge(tag_change_attributes, %{image_id: image_id, tag_id: tag_id, added: false})
|
Enum.map(deleted, fn [image_id, tag_id] ->
|
||||||
end)
|
Map.merge(tag_change_attributes, %{image_id: image_id, tag_id: tag_id, added: false})
|
||||||
|
end)
|
||||||
|
|
||||||
Repo.insert_all(TagChange, added_changes ++ removed_changes)
|
Repo.insert_all(TagChange, added_changes ++ removed_changes)
|
||||||
|
|
||||||
|
@ -68,15 +71,18 @@ defmodule Philomena.TagChanges do
|
||||||
added_upserts =
|
added_upserts =
|
||||||
inserted
|
inserted
|
||||||
|> Enum.group_by(fn [_image_id, tag_id] -> tag_id end)
|
|> Enum.group_by(fn [_image_id, tag_id] -> tag_id end)
|
||||||
|> Enum.map(fn {tag_id, instances} -> Map.merge(tag_attributes, %{id: tag_id, images_count: length(instances)}) end)
|
|> Enum.map(fn {tag_id, instances} ->
|
||||||
|
Map.merge(tag_attributes, %{id: tag_id, images_count: length(instances)})
|
||||||
|
end)
|
||||||
|
|
||||||
removed_upserts =
|
removed_upserts =
|
||||||
deleted
|
deleted
|
||||||
|> Enum.group_by(fn [_image_id, tag_id] -> tag_id end)
|
|> Enum.group_by(fn [_image_id, tag_id] -> tag_id end)
|
||||||
|> Enum.map(fn {tag_id, instances} -> Map.merge(tag_attributes, %{id: tag_id, images_count: -length(instances)}) end)
|
|> Enum.map(fn {tag_id, instances} ->
|
||||||
|
Map.merge(tag_attributes, %{id: tag_id, images_count: -length(instances)})
|
||||||
|
end)
|
||||||
|
|
||||||
update_query =
|
update_query = update(Tag, inc: [images_count: fragment("EXCLUDED.images_count")])
|
||||||
update(Tag, inc: [images_count: fragment("EXCLUDED.images_count")])
|
|
||||||
|
|
||||||
upserts = added_upserts ++ removed_upserts
|
upserts = added_upserts ++ removed_upserts
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ defmodule Philomena.Tags do
|
||||||
# Now get rid of the aliases
|
# Now get rid of the aliases
|
||||||
existent_tags =
|
existent_tags =
|
||||||
existent_tags
|
existent_tags
|
||||||
|> Enum.map(& &1.aliased_tag || &1)
|
|> Enum.map(&(&1.aliased_tag || &1))
|
||||||
|
|
||||||
new_tags =
|
new_tags =
|
||||||
nonexistent_tag_names
|
nonexistent_tag_names
|
||||||
|
@ -107,6 +107,7 @@ defmodule Philomena.Tags do
|
||||||
"""
|
"""
|
||||||
def update_tag(%Tag{} = tag, attrs) do
|
def update_tag(%Tag{} = tag, attrs) do
|
||||||
tag_input = Tag.parse_tag_list(attrs["implied_tag_list"])
|
tag_input = Tag.parse_tag_list(attrs["implied_tag_list"])
|
||||||
|
|
||||||
implied_tags =
|
implied_tags =
|
||||||
Tag
|
Tag
|
||||||
|> where([t], t.name in ^tag_input)
|
|> where([t], t.name in ^tag_input)
|
||||||
|
@ -120,7 +121,7 @@ defmodule Philomena.Tags do
|
||||||
def update_tag_image(%Tag{} = tag, attrs) do
|
def update_tag_image(%Tag{} = tag, attrs) do
|
||||||
changeset = Uploader.analyze_upload(tag, attrs)
|
changeset = Uploader.analyze_upload(tag, attrs)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.update(:tag, changeset)
|
|> Multi.update(:tag, changeset)
|
||||||
|> Multi.run(:update_file, fn _repo, %{tag: tag} ->
|
|> Multi.run(:update_file, fn _repo, %{tag: tag} ->
|
||||||
Uploader.persist_upload(tag)
|
Uploader.persist_upload(tag)
|
||||||
|
@ -134,7 +135,7 @@ defmodule Philomena.Tags do
|
||||||
def remove_tag_image(%Tag{} = tag) do
|
def remove_tag_image(%Tag{} = tag) do
|
||||||
changeset = Tag.remove_image_changeset(tag)
|
changeset = Tag.remove_image_changeset(tag)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.update(:tag, changeset)
|
|> Multi.update(:tag, changeset)
|
||||||
|> Multi.run(:remove_file, fn _repo, %{tag: tag} ->
|
|> Multi.run(:remove_file, fn _repo, %{tag: tag} ->
|
||||||
Uploader.unpersist_old_upload(tag)
|
Uploader.unpersist_old_upload(tag)
|
||||||
|
@ -177,9 +178,14 @@ defmodule Philomena.Tags do
|
||||||
def alias_tag(%Tag{} = tag, attrs) do
|
def alias_tag(%Tag{} = tag, attrs) do
|
||||||
target_tag = Repo.get_by!(Tag, name: attrs["target_tag"])
|
target_tag = Repo.get_by!(Tag, name: attrs["target_tag"])
|
||||||
|
|
||||||
filters_hidden = where(Filter, [f], fragment("? @> ARRAY[?]::integer[]", f.hidden_tag_ids, ^tag.id))
|
filters_hidden =
|
||||||
filters_spoilered = where(Filter, [f], fragment("? @> ARRAY[?]::integer[]", f.spoilered_tag_ids, ^tag.id))
|
where(Filter, [f], fragment("? @> ARRAY[?]::integer[]", f.hidden_tag_ids, ^tag.id))
|
||||||
users_watching = where(User, [u], fragment("? @> ARRAY[?]::integer[]", u.watched_tag_ids, ^tag.id))
|
|
||||||
|
filters_spoilered =
|
||||||
|
where(Filter, [f], fragment("? @> ARRAY[?]::integer[]", f.spoilered_tag_ids, ^tag.id))
|
||||||
|
|
||||||
|
users_watching =
|
||||||
|
where(User, [u], fragment("? @> ARRAY[?]::integer[]", u.watched_tag_ids, ^tag.id))
|
||||||
|
|
||||||
array_replace(filters_hidden, :hidden_tag_ids, tag.id, target_tag.id)
|
array_replace(filters_hidden, :hidden_tag_ids, tag.id, target_tag.id)
|
||||||
array_replace(filters_spoilered, :spoilered_tag_ids, tag.id, target_tag.id)
|
array_replace(filters_spoilered, :spoilered_tag_ids, tag.id, target_tag.id)
|
||||||
|
@ -188,10 +194,10 @@ defmodule Philomena.Tags do
|
||||||
# Manual insert all because ecto won't do it for us
|
# Manual insert all because ecto won't do it for us
|
||||||
Repo.query!(
|
Repo.query!(
|
||||||
"INSERT INTO image_taggings (image_id, tag_id) " <>
|
"INSERT INTO image_taggings (image_id, tag_id) " <>
|
||||||
"SELECT i.id, #{target_tag.id} FROM images i " <>
|
"SELECT i.id, #{target_tag.id} FROM images i " <>
|
||||||
"INNER JOIN image_taggings it on it.image_id = i.id " <>
|
"INNER JOIN image_taggings it on it.image_id = i.id " <>
|
||||||
"WHERE it.tag_id = #{tag.id} " <>
|
"WHERE it.tag_id = #{tag.id} " <>
|
||||||
"ON CONFLICT DO NOTHING"
|
"ON CONFLICT DO NOTHING"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Delete taggings on the source tag
|
# Delete taggings on the source tag
|
||||||
|
@ -211,7 +217,9 @@ defmodule Philomena.Tags do
|
||||||
# Update counter
|
# Update counter
|
||||||
Tag
|
Tag
|
||||||
|> where(id: ^tag.id)
|
|> where(id: ^tag.id)
|
||||||
|> Repo.update_all(set: [images_count: 0, aliased_tag_id: target_tag.id, updated_at: DateTime.utc_now()])
|
|> Repo.update_all(
|
||||||
|
set: [images_count: 0, aliased_tag_id: target_tag.id, updated_at: DateTime.utc_now()]
|
||||||
|
)
|
||||||
|
|
||||||
# Finally, reindex
|
# Finally, reindex
|
||||||
reindex_tag_images(target_tag)
|
reindex_tag_images(target_tag)
|
||||||
|
@ -275,8 +283,10 @@ defmodule Philomena.Tags do
|
||||||
|> Enum.map(&%{&1 | image_id: String.to_integer(&1.image_id)})
|
|> Enum.map(&%{&1 | image_id: String.to_integer(&1.image_id)})
|
||||||
|
|
||||||
{:ok, tag_ids} =
|
{:ok, tag_ids} =
|
||||||
Repo.transaction fn ->
|
Repo.transaction(fn ->
|
||||||
{_count, taggings} = Repo.insert_all(Tagging, taggings, on_conflict: :nothing, returning: [:tag_id])
|
{_count, taggings} =
|
||||||
|
Repo.insert_all(Tagging, taggings, on_conflict: :nothing, returning: [:tag_id])
|
||||||
|
|
||||||
tag_ids = Enum.map(taggings, & &1.tag_id)
|
tag_ids = Enum.map(taggings, & &1.tag_id)
|
||||||
|
|
||||||
Tag
|
Tag
|
||||||
|
@ -284,7 +294,7 @@ defmodule Philomena.Tags do
|
||||||
|> Repo.update_all(inc: [images_count: 1])
|
|> Repo.update_all(inc: [images_count: 1])
|
||||||
|
|
||||||
tag_ids
|
tag_ids
|
||||||
end
|
end)
|
||||||
|
|
||||||
Tag
|
Tag
|
||||||
|> where([t], t.id in ^tag_ids)
|
|> where([t], t.id in ^tag_ids)
|
||||||
|
@ -309,7 +319,7 @@ defmodule Philomena.Tags do
|
||||||
end
|
end
|
||||||
|
|
||||||
def reindex_tags(tags) do
|
def reindex_tags(tags) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
ids =
|
ids =
|
||||||
tags
|
tags
|
||||||
|> Enum.map(& &1.id)
|
|> Enum.map(& &1.id)
|
||||||
|
@ -318,7 +328,7 @@ defmodule Philomena.Tags do
|
||||||
|> preload(^indexing_preloads())
|
|> preload(^indexing_preloads())
|
||||||
|> where([t], t.id in ^ids)
|
|> where([t], t.id in ^ids)
|
||||||
|> Elasticsearch.reindex(Tag)
|
|> Elasticsearch.reindex(Tag)
|
||||||
end
|
end)
|
||||||
|
|
||||||
tags
|
tags
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,8 @@ defmodule Philomena.Tags.Query do
|
||||||
defp fields do
|
defp fields do
|
||||||
[
|
[
|
||||||
int_fields: ~W(id images),
|
int_fields: ~W(id images),
|
||||||
literal_fields: ~W(slug name name_in_namespace namespace implies alias_of implied_by aliases category analyzed_name),
|
literal_fields:
|
||||||
|
~W(slug name name_in_namespace namespace implies alias_of implied_by aliases category analyzed_name),
|
||||||
bool_fields: ~W(aliased),
|
bool_fields: ~W(aliased),
|
||||||
ngram_fields: ~W(description short_description),
|
ngram_fields: ~W(description short_description),
|
||||||
default_field: {"analyzed_name", :ngram},
|
default_field: {"analyzed_name", :ngram},
|
||||||
|
|
|
@ -45,8 +45,16 @@ defmodule Philomena.Tags.Tag do
|
||||||
schema "tags" do
|
schema "tags" do
|
||||||
belongs_to :aliased_tag, Tag, source: :aliased_tag_id
|
belongs_to :aliased_tag, Tag, source: :aliased_tag_id
|
||||||
has_many :aliases, Tag, foreign_key: :aliased_tag_id
|
has_many :aliases, Tag, foreign_key: :aliased_tag_id
|
||||||
many_to_many :implied_tags, Tag, join_through: "tags_implied_tags", join_keys: [tag_id: :id, implied_tag_id: :id], on_replace: :delete
|
|
||||||
many_to_many :implied_by_tags, Tag, join_through: "tags_implied_tags", join_keys: [implied_tag_id: :id, tag_id: :id]
|
many_to_many :implied_tags, Tag,
|
||||||
|
join_through: "tags_implied_tags",
|
||||||
|
join_keys: [tag_id: :id, implied_tag_id: :id],
|
||||||
|
on_replace: :delete
|
||||||
|
|
||||||
|
many_to_many :implied_by_tags, Tag,
|
||||||
|
join_through: "tags_implied_tags",
|
||||||
|
join_keys: [implied_tag_id: :id, tag_id: :id]
|
||||||
|
|
||||||
has_many :public_links, UserLink, where: [public: true, aasm_state: "verified"]
|
has_many :public_links, UserLink, where: [public: true, aasm_state: "verified"]
|
||||||
has_many :hidden_links, UserLink, where: [public: false, aasm_state: "verified"]
|
has_many :hidden_links, UserLink, where: [public: false, aasm_state: "verified"]
|
||||||
has_many :dnp_entries, DnpEntry, where: [aasm_state: "listed"]
|
has_many :dnp_entries, DnpEntry, where: [aasm_state: "listed"]
|
||||||
|
@ -118,23 +126,25 @@ defmodule Philomena.Tags.Tag do
|
||||||
|> to_string()
|
|> to_string()
|
||||||
|> String.split(",")
|
|> String.split(",")
|
||||||
|> Enum.map(&clean_tag_name/1)
|
|> Enum.map(&clean_tag_name/1)
|
||||||
|> Enum.reject(&"" == &1)
|
|> Enum.reject(&("" == &1))
|
||||||
end
|
end
|
||||||
|
|
||||||
def display_order(tags) do
|
def display_order(tags) do
|
||||||
tags
|
tags
|
||||||
|> Enum.sort_by(&{
|
|> Enum.sort_by(
|
||||||
&1.category != "error",
|
&{
|
||||||
&1.category != "rating",
|
&1.category != "error",
|
||||||
&1.category != "origin",
|
&1.category != "rating",
|
||||||
&1.category != "character",
|
&1.category != "origin",
|
||||||
&1.category != "oc",
|
&1.category != "character",
|
||||||
&1.category != "species",
|
&1.category != "oc",
|
||||||
&1.category != "content-fanmade",
|
&1.category != "species",
|
||||||
&1.category != "content-official",
|
&1.category != "content-fanmade",
|
||||||
&1.category != "spoiler",
|
&1.category != "content-official",
|
||||||
&1.name
|
&1.category != "spoiler",
|
||||||
})
|
&1.name
|
||||||
|
}
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def categories do
|
def categories do
|
||||||
|
@ -175,13 +185,16 @@ defmodule Philomena.Tags.Tag do
|
||||||
|
|
||||||
defp join_namespace_parts([_name], original_name),
|
defp join_namespace_parts([_name], original_name),
|
||||||
do: original_name
|
do: original_name
|
||||||
|
|
||||||
defp join_namespace_parts([namespace, name], _original_name) when namespace in @namespaces,
|
defp join_namespace_parts([namespace, name], _original_name) when namespace in @namespaces,
|
||||||
do: namespace <> ":" <> name
|
do: namespace <> ":" <> name
|
||||||
|
|
||||||
defp join_namespace_parts([_namespace, _name], original_name),
|
defp join_namespace_parts([_namespace, _name], original_name),
|
||||||
do: original_name
|
do: original_name
|
||||||
|
|
||||||
defp ununderscore(<<"artist:", _rest::binary>> = name),
|
defp ununderscore(<<"artist:", _rest::binary>> = name),
|
||||||
do: name
|
do: name
|
||||||
|
|
||||||
defp ununderscore(name),
|
defp ununderscore(name),
|
||||||
do: String.replace(name, "_", " ")
|
do: String.replace(name, "_", " ")
|
||||||
|
|
||||||
|
@ -222,7 +235,7 @@ defmodule Philomena.Tags.Tag do
|
||||||
namespace = changeset |> get_field(:namespace)
|
namespace = changeset |> get_field(:namespace)
|
||||||
|
|
||||||
case @namespace_categories[namespace] do
|
case @namespace_categories[namespace] do
|
||||||
nil -> changeset
|
nil -> changeset
|
||||||
category -> change(changeset, category: category)
|
category -> change(changeset, category: category)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,11 +23,12 @@ defmodule Philomena.Textile.Renderer do
|
||||||
|> Enum.flat_map(fn tree ->
|
|> Enum.flat_map(fn tree ->
|
||||||
tree
|
tree
|
||||||
|> Enum.flat_map(fn
|
|> Enum.flat_map(fn
|
||||||
{:text, text} ->
|
{:text, text} ->
|
||||||
[text]
|
[text]
|
||||||
_ ->
|
|
||||||
[]
|
_ ->
|
||||||
end)
|
[]
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|> find_images
|
|> find_images
|
||||||
|
|
||||||
|
@ -75,15 +76,27 @@ defmodule Philomena.Textile.Renderer do
|
||||||
match
|
match
|
||||||
|
|
||||||
[image, "p"] ->
|
[image, "p"] ->
|
||||||
Phoenix.View.render(@image_view, "_image_target.html", image: image, size: :medium, conn: conn)
|
Phoenix.View.render(@image_view, "_image_target.html",
|
||||||
|
image: image,
|
||||||
|
size: :medium,
|
||||||
|
conn: conn
|
||||||
|
)
|
||||||
|> safe_to_string()
|
|> safe_to_string()
|
||||||
|
|
||||||
[image, "t"] ->
|
[image, "t"] ->
|
||||||
Phoenix.View.render(@image_view, "_image_target.html", image: image, size: :small, conn: conn)
|
Phoenix.View.render(@image_view, "_image_target.html",
|
||||||
|
image: image,
|
||||||
|
size: :small,
|
||||||
|
conn: conn
|
||||||
|
)
|
||||||
|> safe_to_string()
|
|> safe_to_string()
|
||||||
|
|
||||||
[image, "s"] ->
|
[image, "s"] ->
|
||||||
Phoenix.View.render(@image_view, "_image_target.html", image: image, size: :thumb_small, conn: conn)
|
Phoenix.View.render(@image_view, "_image_target.html",
|
||||||
|
image: image,
|
||||||
|
size: :thumb_small,
|
||||||
|
conn: conn
|
||||||
|
)
|
||||||
|> safe_to_string()
|
|> safe_to_string()
|
||||||
|
|
||||||
[image] ->
|
[image] ->
|
||||||
|
@ -103,6 +116,7 @@ defmodule Philomena.Textile.Renderer do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp load_images([]), do: %{}
|
defp load_images([]), do: %{}
|
||||||
|
|
||||||
defp load_images(ids) do
|
defp load_images(ids) do
|
||||||
Image
|
Image
|
||||||
|> where([i], i.id in ^ids)
|
|> where([i], i.id in ^ids)
|
||||||
|
|
|
@ -41,11 +41,12 @@ defmodule Philomena.Topics do
|
||||||
"""
|
"""
|
||||||
def create_topic(forum, attribution, attrs \\ %{}) do
|
def create_topic(forum, attribution, attrs \\ %{}) do
|
||||||
now = DateTime.utc_now() |> DateTime.truncate(:second)
|
now = DateTime.utc_now() |> DateTime.truncate(:second)
|
||||||
|
|
||||||
topic =
|
topic =
|
||||||
%Topic{}
|
%Topic{}
|
||||||
|> Topic.creation_changeset(attrs, forum, attribution)
|
|> Topic.creation_changeset(attrs, forum, attribution)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.insert(:topic, topic)
|
|> Multi.insert(:topic, topic)
|
||||||
|> Multi.run(:update_topic, fn repo, %{topic: topic} ->
|
|> Multi.run(:update_topic, fn repo, %{topic: topic} ->
|
||||||
{count, nil} =
|
{count, nil} =
|
||||||
|
@ -59,7 +60,10 @@ defmodule Philomena.Topics do
|
||||||
{count, nil} =
|
{count, nil} =
|
||||||
Forum
|
Forum
|
||||||
|> where(id: ^topic.forum_id)
|
|> where(id: ^topic.forum_id)
|
||||||
|> repo.update_all(inc: [post_count: 1, topic_count: 1], set: [last_post_id: hd(topic.posts).id])
|
|> repo.update_all(
|
||||||
|
inc: [post_count: 1, topic_count: 1],
|
||||||
|
set: [last_post_id: hd(topic.posts).id]
|
||||||
|
)
|
||||||
|
|
||||||
{:ok, count}
|
{:ok, count}
|
||||||
end)
|
end)
|
||||||
|
@ -70,7 +74,7 @@ defmodule Philomena.Topics do
|
||||||
end
|
end
|
||||||
|
|
||||||
def notify_topic(topic) do
|
def notify_topic(topic) do
|
||||||
spawn fn ->
|
spawn(fn ->
|
||||||
forum =
|
forum =
|
||||||
topic
|
topic
|
||||||
|> Repo.preload(:forum)
|
|> Repo.preload(:forum)
|
||||||
|
@ -92,7 +96,7 @@ defmodule Philomena.Topics do
|
||||||
action: "posted a new topic in"
|
action: "posted a new topic in"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
end
|
end)
|
||||||
|
|
||||||
topic
|
topic
|
||||||
end
|
end
|
||||||
|
@ -147,6 +151,7 @@ defmodule Philomena.Topics do
|
||||||
alias Philomena.Topics.Subscription
|
alias Philomena.Topics.Subscription
|
||||||
|
|
||||||
def subscribed?(_topic, nil), do: false
|
def subscribed?(_topic, nil), do: false
|
||||||
|
|
||||||
def subscribed?(topic, user) do
|
def subscribed?(topic, user) do
|
||||||
Subscription
|
Subscription
|
||||||
|> where(topic_id: ^topic.id, user_id: ^user.id)
|
|> where(topic_id: ^topic.id, user_id: ^user.id)
|
||||||
|
@ -166,6 +171,7 @@ defmodule Philomena.Topics do
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def create_subscription(_topic, nil), do: {:ok, nil}
|
def create_subscription(_topic, nil), do: {:ok, nil}
|
||||||
|
|
||||||
def create_subscription(topic, user) do
|
def create_subscription(topic, user) do
|
||||||
%Subscription{topic_id: topic.id, user_id: user.id}
|
%Subscription{topic_id: topic.id, user_id: user.id}
|
||||||
|> Subscription.changeset(%{})
|
|> Subscription.changeset(%{})
|
||||||
|
@ -215,7 +221,7 @@ defmodule Philomena.Topics do
|
||||||
old_forum_id = topic.forum_id
|
old_forum_id = topic.forum_id
|
||||||
topic_changes = Topic.move_changeset(topic, new_forum_id)
|
topic_changes = Topic.move_changeset(topic, new_forum_id)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.update(:topic, topic_changes)
|
|> Multi.update(:topic, topic_changes)
|
||||||
|> Multi.run(:update_old_forum, fn repo, %{topic: topic} ->
|
|> Multi.run(:update_old_forum, fn repo, %{topic: topic} ->
|
||||||
{count, nil} =
|
{count, nil} =
|
||||||
|
@ -247,6 +253,7 @@ defmodule Philomena.Topics do
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_notification(_topic, nil), do: nil
|
def clear_notification(_topic, nil), do: nil
|
||||||
|
|
||||||
def clear_notification(topic, user) do
|
def clear_notification(topic, user) do
|
||||||
Notifications.delete_unread_notification("Topic", topic.id, user)
|
Notifications.delete_unread_notification("Topic", topic.id, user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,11 +13,11 @@ defmodule Philomena.Uploader do
|
||||||
callback on the model or changeset passed in with attributes set on
|
callback on the model or changeset passed in with attributes set on
|
||||||
the field_name.
|
the field_name.
|
||||||
"""
|
"""
|
||||||
@spec analyze_upload(any(), String.t(), Plug.Upload.t(), (any(), map() -> Ecto.Changeset.t())) :: Ecto.Changeset.t()
|
@spec analyze_upload(any(), String.t(), Plug.Upload.t(), (any(), map() -> Ecto.Changeset.t())) ::
|
||||||
|
Ecto.Changeset.t()
|
||||||
def analyze_upload(model_or_changeset, field_name, upload_parameter, changeset_fn) do
|
def analyze_upload(model_or_changeset, field_name, upload_parameter, changeset_fn) do
|
||||||
with {:ok, analysis} <- Analyzers.analyze(upload_parameter),
|
with {:ok, analysis} <- Analyzers.analyze(upload_parameter),
|
||||||
analysis <- extra_attributes(analysis, upload_parameter)
|
analysis <- extra_attributes(analysis, upload_parameter) do
|
||||||
do
|
|
||||||
removed =
|
removed =
|
||||||
model_or_changeset
|
model_or_changeset
|
||||||
|> change()
|
|> change()
|
||||||
|
@ -25,16 +25,16 @@ defmodule Philomena.Uploader do
|
||||||
|
|
||||||
attributes =
|
attributes =
|
||||||
%{
|
%{
|
||||||
"name" => analysis.name,
|
"name" => analysis.name,
|
||||||
"width" => analysis.width,
|
"width" => analysis.width,
|
||||||
"height" => analysis.height,
|
"height" => analysis.height,
|
||||||
"size" => analysis.size,
|
"size" => analysis.size,
|
||||||
"format" => analysis.extension,
|
"format" => analysis.extension,
|
||||||
"mime_type" => analysis.mime_type,
|
"mime_type" => analysis.mime_type,
|
||||||
"aspect_ratio" => analysis.aspect_ratio,
|
"aspect_ratio" => analysis.aspect_ratio,
|
||||||
"orig_sha512_hash" => analysis.sha512,
|
"orig_sha512_hash" => analysis.sha512,
|
||||||
"sha512_hash" => analysis.sha512,
|
"sha512_hash" => analysis.sha512,
|
||||||
"is_animated" => analysis.animated?
|
"is_animated" => analysis.animated?
|
||||||
}
|
}
|
||||||
|> prefix_attributes(field_name)
|
|> prefix_attributes(field_name)
|
||||||
|> Map.put(field_name, analysis.new_name)
|
|> Map.put(field_name, analysis.new_name)
|
||||||
|
@ -55,9 +55,9 @@ defmodule Philomena.Uploader do
|
||||||
@spec persist_upload(any(), String.t(), String.t()) :: any()
|
@spec persist_upload(any(), String.t(), String.t()) :: any()
|
||||||
def persist_upload(model, file_root, field_name) do
|
def persist_upload(model, file_root, field_name) do
|
||||||
source = Map.get(model, field(upload_key(field_name)))
|
source = Map.get(model, field(upload_key(field_name)))
|
||||||
dest = Map.get(model, field(field_name))
|
dest = Map.get(model, field(field_name))
|
||||||
target = Path.join(file_root, dest)
|
target = Path.join(file_root, dest)
|
||||||
dir = Path.dirname(target)
|
dir = Path.dirname(target)
|
||||||
|
|
||||||
# Create the target directory if it doesn't exist yet,
|
# Create the target directory if it doesn't exist yet,
|
||||||
# then write the file.
|
# then write the file.
|
||||||
|
@ -78,10 +78,10 @@ defmodule Philomena.Uploader do
|
||||||
|
|
||||||
defp extra_attributes(analysis, %Plug.Upload{path: path, filename: filename}) do
|
defp extra_attributes(analysis, %Plug.Upload{path: path, filename: filename}) do
|
||||||
{width, height} = analysis.dimensions
|
{width, height} = analysis.dimensions
|
||||||
aspect_ratio = aspect_ratio(width, height)
|
aspect_ratio = aspect_ratio(width, height)
|
||||||
|
|
||||||
stat = File.stat!(path)
|
stat = File.stat!(path)
|
||||||
sha512 = Sha512.file(path)
|
sha512 = Sha512.file(path)
|
||||||
new_name = Filename.build(analysis.extension)
|
new_name = Filename.build(analysis.extension)
|
||||||
|
|
||||||
analysis
|
analysis
|
||||||
|
|
|
@ -23,6 +23,7 @@ defmodule Philomena.UserDownvoteWipe do
|
||||||
|> where(user_id: ^user.id, up: true)
|
|> where(user_id: ^user.id, up: true)
|
||||||
|> Batch.query_batches([id_field: :image_id], fn queryable ->
|
|> Batch.query_batches([id_field: :image_id], fn queryable ->
|
||||||
{_, image_ids} = Repo.delete_all(select(queryable, [i_v], i_v.image_id))
|
{_, image_ids} = Repo.delete_all(select(queryable, [i_v], i_v.image_id))
|
||||||
|
|
||||||
Repo.update_all(where(Image, [i], i.id in ^image_ids), inc: [upvotes_count: -1, score: -1])
|
Repo.update_all(where(Image, [i], i.id in ^image_ids), inc: [upvotes_count: -1, score: -1])
|
||||||
|
|
||||||
reindex(image_ids)
|
reindex(image_ids)
|
||||||
|
|
|
@ -92,9 +92,13 @@ defmodule Philomena.UserLinks do
|
||||||
now = DateTime.utc_now() |> DateTime.truncate(:second)
|
now = DateTime.utc_now() |> DateTime.truncate(:second)
|
||||||
|
|
||||||
with badge when not is_nil(badge) <- repo.get_by(limit(Badge, 1), title: "Artist"),
|
with badge when not is_nil(badge) <- repo.get_by(limit(Badge, 1), title: "Artist"),
|
||||||
nil <- repo.get_by(limit(Award, 1), badge_id: badge.id, user_id: user_link.user_id)
|
nil <- repo.get_by(limit(Award, 1), badge_id: badge.id, user_id: user_link.user_id) do
|
||||||
do
|
%Award{
|
||||||
%Award{badge_id: badge.id, user_id: user_link.user_id, awarded_by_id: user.id, awarded_on: now}
|
badge_id: badge.id,
|
||||||
|
user_id: user_link.user_id,
|
||||||
|
awarded_by_id: user.id,
|
||||||
|
awarded_on: now
|
||||||
|
}
|
||||||
|> Award.changeset(%{})
|
|> Award.changeset(%{})
|
||||||
|> repo.insert()
|
|> repo.insert()
|
||||||
else
|
else
|
||||||
|
|
|
@ -24,9 +24,16 @@ defmodule Philomena.UserStatistics do
|
||||||
def inc_stat(user, action, amount \\ 1)
|
def inc_stat(user, action, amount \\ 1)
|
||||||
|
|
||||||
def inc_stat(nil, _action, _amount), do: {:ok, nil}
|
def inc_stat(nil, _action, _amount), do: {:ok, nil}
|
||||||
|
|
||||||
def inc_stat(%{id: user_id}, action, amount)
|
def inc_stat(%{id: user_id}, action, amount)
|
||||||
when action in [:uploads, :images_favourited, :comments_posted, :votes_cast, :metadata_updates, :forum_posts]
|
when action in [
|
||||||
do
|
:uploads,
|
||||||
|
:images_favourited,
|
||||||
|
:comments_posted,
|
||||||
|
:votes_cast,
|
||||||
|
:metadata_updates,
|
||||||
|
:forum_posts
|
||||||
|
] do
|
||||||
now =
|
now =
|
||||||
DateTime.utc_now()
|
DateTime.utc_now()
|
||||||
|> DateTime.to_unix(:second)
|
|> DateTime.to_unix(:second)
|
||||||
|
@ -37,6 +44,7 @@ defmodule Philomena.UserStatistics do
|
||||||
|
|
||||||
run = fn ->
|
run = fn ->
|
||||||
Repo.update_all(user, inc: [{action_count, amount}])
|
Repo.update_all(user, inc: [{action_count, amount}])
|
||||||
|
|
||||||
Repo.insert(
|
Repo.insert(
|
||||||
Map.put(%UserStatistic{day: now, user_id: user_id}, action, amount),
|
Map.put(%UserStatistic{day: now, user_id: user_id}, action, amount),
|
||||||
on_conflict: [inc: [{action, amount}]],
|
on_conflict: [inc: [{action, amount}]],
|
||||||
|
|
|
@ -20,8 +20,12 @@ defmodule Philomena.UserStatistics.UserStatistic do
|
||||||
def changeset(user_statistic, attrs) do
|
def changeset(user_statistic, attrs) do
|
||||||
user_statistic
|
user_statistic
|
||||||
|> cast(attrs, [
|
|> cast(attrs, [
|
||||||
:uploads, :votes_cast, :comments_posted, :metadata_updates,
|
:uploads,
|
||||||
:images_favourited, :forum_posts
|
:votes_cast,
|
||||||
|
:comments_posted,
|
||||||
|
:metadata_updates,
|
||||||
|
:images_favourited,
|
||||||
|
:forum_posts
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,7 +33,13 @@ defmodule Philomena.UserWipe do
|
||||||
|
|
||||||
User
|
User
|
||||||
|> where(id: ^user.id)
|
|> where(id: ^user.id)
|
||||||
|> Repo.update_all(set: [email: "deactivated#{random_hex}@example.com", current_sign_in_ip: @wipe_ip, last_sign_in_ip: @wipe_ip])
|
|> Repo.update_all(
|
||||||
|
set: [
|
||||||
|
email: "deactivated#{random_hex}@example.com",
|
||||||
|
current_sign_in_ip: @wipe_ip,
|
||||||
|
last_sign_in_ip: @wipe_ip
|
||||||
|
]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp in_batches(queryable, mapper) do
|
defp in_batches(queryable, mapper) do
|
||||||
|
|
|
@ -86,7 +86,7 @@ defmodule Philomena.Users do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp clean_roles(nil), do: []
|
defp clean_roles(nil), do: []
|
||||||
defp clean_roles(roles), do: Enum.filter(roles, &"" != &1)
|
defp clean_roles(roles), do: Enum.filter(roles, &("" != &1))
|
||||||
|
|
||||||
def update_spoiler_type(%User{} = user, attrs) do
|
def update_spoiler_type(%User{} = user, attrs) do
|
||||||
user
|
user
|
||||||
|
@ -131,7 +131,7 @@ defmodule Philomena.Users do
|
||||||
def update_avatar(%User{} = user, attrs) do
|
def update_avatar(%User{} = user, attrs) do
|
||||||
changeset = Uploader.analyze_upload(user, attrs)
|
changeset = Uploader.analyze_upload(user, attrs)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.update(:user, changeset)
|
|> Multi.update(:user, changeset)
|
||||||
|> Multi.run(:update_file, fn _repo, %{user: user} ->
|
|> Multi.run(:update_file, fn _repo, %{user: user} ->
|
||||||
Uploader.persist_upload(user)
|
Uploader.persist_upload(user)
|
||||||
|
@ -145,7 +145,7 @@ defmodule Philomena.Users do
|
||||||
def remove_avatar(%User{} = user) do
|
def remove_avatar(%User{} = user) do
|
||||||
changeset = User.remove_avatar_changeset(user)
|
changeset = User.remove_avatar_changeset(user)
|
||||||
|
|
||||||
Multi.new
|
Multi.new()
|
||||||
|> Multi.update(:user, changeset)
|
|> Multi.update(:user, changeset)
|
||||||
|> Multi.run(:remove_file, fn _repo, %{user: user} ->
|
|> Multi.run(:remove_file, fn _repo, %{user: user} ->
|
||||||
Uploader.unpersist_old_upload(user)
|
Uploader.unpersist_old_upload(user)
|
||||||
|
@ -203,12 +203,13 @@ defmodule Philomena.Users do
|
||||||
User
|
User
|
||||||
|> join(:left, [u], _ in assoc(u, :roles))
|
|> join(:left, [u], _ in assoc(u, :roles))
|
||||||
|> join(:left, [u, _], _ in assoc(u, :current_filter))
|
|> join(:left, [u, _], _ in assoc(u, :current_filter))
|
||||||
|> preload([_, r, cf], [current_filter: cf, roles: r])
|
|> preload([_, r, cf], current_filter: cf, roles: r)
|
||||||
|> Repo.get_by(clauses)
|
|> Repo.get_by(clauses)
|
||||||
|> setup_roles()
|
|> setup_roles()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp setup_roles(nil), do: nil
|
defp setup_roles(nil), do: nil
|
||||||
|
|
||||||
defp setup_roles(user) do
|
defp setup_roles(user) do
|
||||||
role_map = Map.new(user.roles, &{&1.resource_type || &1.name, &1.name})
|
role_map = Map.new(user.roles, &{&1.resource_type || &1.name, &1.name})
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,9 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do
|
||||||
|
|
||||||
# View forums
|
# View forums
|
||||||
def can?(%User{role: "moderator"}, :show, %Forum{access_level: level})
|
def can?(%User{role: "moderator"}, :show, %Forum{access_level: level})
|
||||||
when level in ["normal", "assistant", "staff"], do: true
|
when level in ["normal", "assistant", "staff"],
|
||||||
|
do: true
|
||||||
|
|
||||||
def can?(%User{role: "moderator"}, :show, %Topic{hidden_from_users: true}), do: true
|
def can?(%User{role: "moderator"}, :show, %Topic{hidden_from_users: true}), do: true
|
||||||
|
|
||||||
# View conversations
|
# View conversations
|
||||||
|
@ -116,8 +118,11 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do
|
||||||
# And some privileged moderators can...
|
# And some privileged moderators can...
|
||||||
|
|
||||||
# Manage site notices
|
# Manage site notices
|
||||||
def can?(%User{role: "moderator", role_map: %{"SiteNotice" => "admin"}}, _action, SiteNotice), do: true
|
def can?(%User{role: "moderator", role_map: %{"SiteNotice" => "admin"}}, _action, SiteNotice),
|
||||||
def can?(%User{role: "moderator", role_map: %{"SiteNotice" => "admin"}}, _action, %SiteNotice{}), do: true
|
do: true
|
||||||
|
|
||||||
|
def can?(%User{role: "moderator", role_map: %{"SiteNotice" => "admin"}}, _action, %SiteNotice{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
# Manage badges
|
# Manage badges
|
||||||
def can?(%User{role: "moderator", role_map: %{"Badge" => "admin"}}, _action, Badge), do: true
|
def can?(%User{role: "moderator", role_map: %{"Badge" => "admin"}}, _action, Badge), do: true
|
||||||
|
@ -132,58 +137,127 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do
|
||||||
|
|
||||||
# Manage users
|
# Manage users
|
||||||
def can?(%User{role: "moderator", role_map: %{"User" => "moderator"}}, _action, User), do: true
|
def can?(%User{role: "moderator", role_map: %{"User" => "moderator"}}, _action, User), do: true
|
||||||
def can?(%User{role: "moderator", role_map: %{"User" => "moderator"}}, _action, %User{}), do: true
|
|
||||||
|
def can?(%User{role: "moderator", role_map: %{"User" => "moderator"}}, _action, %User{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
# Manage advertisements
|
# Manage advertisements
|
||||||
def can?(%User{role: "moderator", role_map: %{"Advert" => "admin"}}, _action, Advert), do: true
|
def can?(%User{role: "moderator", role_map: %{"Advert" => "admin"}}, _action, Advert), do: true
|
||||||
def can?(%User{role: "moderator", role_map: %{"Advert" => "admin"}}, _action, %Advert{}), do: true
|
|
||||||
|
def can?(%User{role: "moderator", role_map: %{"Advert" => "admin"}}, _action, %Advert{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
# Manage static pages
|
# Manage static pages
|
||||||
def can?(%User{role: "moderator", role_map: %{"StaticPage" => "admin"}}, _action, StaticPage), do: true
|
def can?(%User{role: "moderator", role_map: %{"StaticPage" => "admin"}}, _action, StaticPage),
|
||||||
def can?(%User{role: "moderator", role_map: %{"StaticPage" => "admin"}}, _action, %StaticPage{}), do: true
|
do: true
|
||||||
|
|
||||||
|
def can?(%User{role: "moderator", role_map: %{"StaticPage" => "admin"}}, _action, %StaticPage{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
#
|
#
|
||||||
# Assistants can...
|
# Assistants can...
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
# Image assistant actions
|
# Image assistant actions
|
||||||
def can?(%User{role: "assistant", role_map: %{"Image" => "moderator"}}, :show, %Image{}), do: true
|
def can?(%User{role: "assistant", role_map: %{"Image" => "moderator"}}, :show, %Image{}),
|
||||||
def can?(%User{role: "assistant", role_map: %{"Image" => "moderator"}}, :hide, %Image{}), do: true
|
do: true
|
||||||
def can?(%User{role: "assistant", role_map: %{"Image" => "moderator"}}, :edit, %Image{}), do: true
|
|
||||||
def can?(%User{role: "assistant", role_map: %{"Image" => "moderator"}}, :edit_description, %Image{}), do: true
|
def can?(%User{role: "assistant", role_map: %{"Image" => "moderator"}}, :hide, %Image{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
|
def can?(%User{role: "assistant", role_map: %{"Image" => "moderator"}}, :edit, %Image{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
|
def can?(
|
||||||
|
%User{role: "assistant", role_map: %{"Image" => "moderator"}},
|
||||||
|
:edit_description,
|
||||||
|
%Image{}
|
||||||
|
),
|
||||||
|
do: true
|
||||||
|
|
||||||
# Dupe assistant actions
|
# Dupe assistant actions
|
||||||
def can?(%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}}, :index, DuplicateReport), do: true
|
def can?(
|
||||||
def can?(%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}}, :edit, %DuplicateReport{}), do: true
|
%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}},
|
||||||
def can?(%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}}, :show, %Image{}), do: true
|
:index,
|
||||||
def can?(%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}}, :edit, %Image{}), do: true
|
DuplicateReport
|
||||||
def can?(%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}}, :hide, %Comment{}), do: true
|
),
|
||||||
|
do: true
|
||||||
|
|
||||||
|
def can?(
|
||||||
|
%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}},
|
||||||
|
:edit,
|
||||||
|
%DuplicateReport{}
|
||||||
|
),
|
||||||
|
do: true
|
||||||
|
|
||||||
|
def can?(
|
||||||
|
%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}},
|
||||||
|
:show,
|
||||||
|
%Image{}
|
||||||
|
),
|
||||||
|
do: true
|
||||||
|
|
||||||
|
def can?(
|
||||||
|
%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}},
|
||||||
|
:edit,
|
||||||
|
%Image{}
|
||||||
|
),
|
||||||
|
do: true
|
||||||
|
|
||||||
|
def can?(
|
||||||
|
%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}},
|
||||||
|
:hide,
|
||||||
|
%Comment{}
|
||||||
|
),
|
||||||
|
do: true
|
||||||
|
|
||||||
# Comment assistant actions
|
# Comment assistant actions
|
||||||
def can?(%User{role: "assistant", role_map: %{"Comment" => "moderator"}}, :show, %Comment{}), do: true
|
def can?(%User{role: "assistant", role_map: %{"Comment" => "moderator"}}, :show, %Comment{}),
|
||||||
def can?(%User{role: "assistant", role_map: %{"Comment" => "moderator"}}, :edit, %Comment{}), do: true
|
do: true
|
||||||
def can?(%User{role: "assistant", role_map: %{"Comment" => "moderator"}}, :hide, %Comment{}), do: true
|
|
||||||
|
def can?(%User{role: "assistant", role_map: %{"Comment" => "moderator"}}, :edit, %Comment{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
|
def can?(%User{role: "assistant", role_map: %{"Comment" => "moderator"}}, :hide, %Comment{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
# Topic assistant actions
|
# Topic assistant actions
|
||||||
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :show, %Topic{}), do: true
|
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :show, %Topic{}),
|
||||||
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :edit, %Topic{}), do: true
|
do: true
|
||||||
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :hide, %Topic{}), do: true
|
|
||||||
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :show, %Post{}), do: true
|
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :edit, %Topic{}),
|
||||||
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :edit, %Post{}), do: true
|
do: true
|
||||||
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :hide, %Post{}), do: true
|
|
||||||
|
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :hide, %Topic{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
|
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :show, %Post{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
|
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :edit, %Post{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
|
def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :hide, %Post{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
# Tag assistant actions
|
# Tag assistant actions
|
||||||
def can?(%User{role: "assistant", role_map: %{"Tag" => "moderator"}}, :edit, %Tag{}), do: true
|
def can?(%User{role: "assistant", role_map: %{"Tag" => "moderator"}}, :edit, %Tag{}), do: true
|
||||||
def can?(%User{role: "assistant", role_map: %{"Tag" => "moderator"}}, :batch_update, Tag), do: true
|
|
||||||
|
def can?(%User{role: "assistant", role_map: %{"Tag" => "moderator"}}, :batch_update, Tag),
|
||||||
|
do: true
|
||||||
|
|
||||||
# User link assistant actions
|
# User link assistant actions
|
||||||
def can?(%User{role: "assistant", role_map: %{"UserLink" => "moderator"}}, :show, %UserLink{}), do: true
|
def can?(%User{role: "assistant", role_map: %{"UserLink" => "moderator"}}, :show, %UserLink{}),
|
||||||
def can?(%User{role: "assistant", role_map: %{"UserLink" => "moderator"}}, :edit, %UserLink{}), do: true
|
do: true
|
||||||
|
|
||||||
|
def can?(%User{role: "assistant", role_map: %{"UserLink" => "moderator"}}, :edit, %UserLink{}),
|
||||||
|
do: true
|
||||||
|
|
||||||
# View forums
|
# View forums
|
||||||
def can?(%User{role: "assistant"}, :show, %Forum{access_level: level})
|
def can?(%User{role: "assistant"}, :show, %Forum{access_level: level})
|
||||||
when level in ["normal", "assistant"], do: true
|
when level in ["normal", "assistant"],
|
||||||
|
do: true
|
||||||
|
|
||||||
def can?(%User{role: "assistant"}, :show, %Topic{hidden_from_users: true}), do: true
|
def can?(%User{role: "assistant"}, :show, %Topic{hidden_from_users: true}), do: true
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -205,17 +279,22 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do
|
||||||
def can?(_user, :show, %Filter{system: true}), do: true
|
def can?(_user, :show, %Filter{system: true}), do: true
|
||||||
def can?(_user, :show, %Filter{public: true}), do: true
|
def can?(_user, :show, %Filter{public: true}), do: true
|
||||||
def can?(%User{}, action, Filter) when action in [:index, :new, :create], do: true
|
def can?(%User{}, action, Filter) when action in [:index, :new, :create], do: true
|
||||||
def can?(%User{id: id}, action, %Filter{user_id: id}) when action in [:show, :edit, :update], do: true
|
|
||||||
|
def can?(%User{id: id}, action, %Filter{user_id: id}) when action in [:show, :edit, :update],
|
||||||
|
do: true
|
||||||
|
|
||||||
# Edit filters they own
|
# Edit filters they own
|
||||||
def can?(%User{id: id}, action, %Filter{user_id: id}) when action in [:edit, :update, :delete], do: true
|
def can?(%User{id: id}, action, %Filter{user_id: id}) when action in [:edit, :update, :delete],
|
||||||
|
do: true
|
||||||
|
|
||||||
# View user links they've created
|
# View user links they've created
|
||||||
def can?(%User{id: id}, :create_links, %User{id: id}), do: true
|
def can?(%User{id: id}, :create_links, %User{id: id}), do: true
|
||||||
def can?(%User{id: id}, :show, %UserLink{user_id: id}), do: true
|
def can?(%User{id: id}, :show, %UserLink{user_id: id}), do: true
|
||||||
|
|
||||||
# Edit their commissions
|
# Edit their commissions
|
||||||
def can?(%User{id: id}, action, %Commission{user_id: id}) when action in [:edit, :update, :delete], do: true
|
def can?(%User{id: id}, action, %Commission{user_id: id})
|
||||||
|
when action in [:edit, :update, :delete],
|
||||||
|
do: true
|
||||||
|
|
||||||
# View non-deleted images
|
# View non-deleted images
|
||||||
def can?(_user, action, Image)
|
def can?(_user, action, Image)
|
||||||
|
@ -229,10 +308,12 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do
|
||||||
def can?(_user, :show, %Tag{}), do: true
|
def can?(_user, :show, %Tag{}), do: true
|
||||||
|
|
||||||
# Comment on images where that is allowed
|
# Comment on images where that is allowed
|
||||||
def can?(_user, :create_comment, %Image{hidden_from_users: false, commenting_allowed: true}), do: true
|
def can?(_user, :create_comment, %Image{hidden_from_users: false, commenting_allowed: true}),
|
||||||
|
do: true
|
||||||
|
|
||||||
# Edit comments on images
|
# Edit comments on images
|
||||||
def can?(%User{id: id}, action, %Comment{hidden_from_users: false, user_id: id} = comment) when action in [:edit, :update] do
|
def can?(%User{id: id}, action, %Comment{hidden_from_users: false, user_id: id} = comment)
|
||||||
|
when action in [:edit, :update] do
|
||||||
# comment must have been made no later than 15 minutes ago
|
# comment must have been made no later than 15 minutes ago
|
||||||
time_ago = NaiveDateTime.utc_now() |> NaiveDateTime.add(-15 * 60)
|
time_ago = NaiveDateTime.utc_now() |> NaiveDateTime.add(-15 * 60)
|
||||||
|
|
||||||
|
@ -240,8 +321,15 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Edit metadata on images where that is allowed
|
# Edit metadata on images where that is allowed
|
||||||
def can?(_user, :edit_metadata, %Image{hidden_from_users: false, tag_editing_allowed: true}), do: true
|
def can?(_user, :edit_metadata, %Image{hidden_from_users: false, tag_editing_allowed: true}),
|
||||||
def can?(%User{id: id}, :edit_description, %Image{user_id: id, hidden_from_users: false, description_editing_allowed: true}), do: true
|
do: true
|
||||||
|
|
||||||
|
def can?(%User{id: id}, :edit_description, %Image{
|
||||||
|
user_id: id,
|
||||||
|
hidden_from_users: false,
|
||||||
|
description_editing_allowed: true
|
||||||
|
}),
|
||||||
|
do: true
|
||||||
|
|
||||||
# Vote on images they can see
|
# Vote on images they can see
|
||||||
def can?(user, :vote, image), do: can?(user, :show, image)
|
def can?(user, :vote, image), do: can?(user, :show, image)
|
||||||
|
@ -257,7 +345,10 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do
|
||||||
|
|
||||||
# Create and edit posts
|
# Create and edit posts
|
||||||
def can?(_user, :create_post, %Topic{locked_at: nil, hidden_from_users: false}), do: true
|
def can?(_user, :create_post, %Topic{locked_at: nil, hidden_from_users: false}), do: true
|
||||||
def can?(%User{id: id}, action, %Post{hidden_from_users: false, user_id: id}) when action in [:edit, :update], do: true
|
|
||||||
|
def can?(%User{id: id}, action, %Post{hidden_from_users: false, user_id: id})
|
||||||
|
when action in [:edit, :update],
|
||||||
|
do: true
|
||||||
|
|
||||||
# View profile pages
|
# View profile pages
|
||||||
def can?(_user, :show, %User{}), do: true
|
def can?(_user, :show, %User{}), do: true
|
||||||
|
@ -274,7 +365,10 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do
|
||||||
# Create and edit galleries
|
# Create and edit galleries
|
||||||
def can?(_user, :show, %Gallery{}), do: true
|
def can?(_user, :show, %Gallery{}), do: true
|
||||||
def can?(%User{}, action, Gallery) when action in [:new, :create], do: true
|
def can?(%User{}, action, Gallery) when action in [:new, :create], do: true
|
||||||
def can?(%User{id: id}, action, %Gallery{creator_id: id}) when action in [:edit, :update, :delete], do: true
|
|
||||||
|
def can?(%User{id: id}, action, %Gallery{creator_id: id})
|
||||||
|
when action in [:edit, :update, :delete],
|
||||||
|
do: true
|
||||||
|
|
||||||
# Show static pages
|
# Show static pages
|
||||||
def can?(_user, :show, %StaticPage{}), do: true
|
def can?(_user, :show, %StaticPage{}), do: true
|
||||||
|
|
|
@ -183,7 +183,10 @@ defmodule Philomena.Users.User do
|
||||||
|
|
||||||
changeset
|
changeset
|
||||||
|> put_change(:current_filter_id, filter.id)
|
|> put_change(:current_filter_id, filter.id)
|
||||||
|> put_change(:recent_filter_ids, Enum.take(Enum.uniq([filter.id | user.recent_filter_ids]), 10))
|
|> put_change(
|
||||||
|
:recent_filter_ids,
|
||||||
|
Enum.take(Enum.uniq([filter.id | user.recent_filter_ids]), 10)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def spoiler_type_changeset(user, attrs) do
|
def spoiler_type_changeset(user, attrs) do
|
||||||
|
@ -196,16 +199,32 @@ defmodule Philomena.Users.User do
|
||||||
def settings_changeset(user, attrs) do
|
def settings_changeset(user, attrs) do
|
||||||
user
|
user
|
||||||
|> cast(attrs, [
|
|> cast(attrs, [
|
||||||
:watched_tag_list, :images_per_page, :fancy_tag_field_on_upload,
|
:watched_tag_list,
|
||||||
:fancy_tag_field_on_edit, :anonymous_by_default, :scale_large_images,
|
:images_per_page,
|
||||||
:comments_per_page, :theme, :watched_images_query_str,
|
:fancy_tag_field_on_upload,
|
||||||
:no_spoilered_in_watched, :watched_images_exclude_str,
|
:fancy_tag_field_on_edit,
|
||||||
:use_centered_layout, :hide_vote_counts, :comments_newest_first
|
:anonymous_by_default,
|
||||||
|
:scale_large_images,
|
||||||
|
:comments_per_page,
|
||||||
|
:theme,
|
||||||
|
:watched_images_query_str,
|
||||||
|
:no_spoilered_in_watched,
|
||||||
|
:watched_images_exclude_str,
|
||||||
|
:use_centered_layout,
|
||||||
|
:hide_vote_counts,
|
||||||
|
:comments_newest_first
|
||||||
])
|
])
|
||||||
|> validate_required([
|
|> validate_required([
|
||||||
:images_per_page, :fancy_tag_field_on_upload, :fancy_tag_field_on_edit,
|
:images_per_page,
|
||||||
:anonymous_by_default, :scale_large_images, :comments_per_page, :theme,
|
:fancy_tag_field_on_upload,
|
||||||
:no_spoilered_in_watched, :use_centered_layout, :hide_vote_counts
|
:fancy_tag_field_on_edit,
|
||||||
|
:anonymous_by_default,
|
||||||
|
:scale_large_images,
|
||||||
|
:comments_per_page,
|
||||||
|
:theme,
|
||||||
|
:no_spoilered_in_watched,
|
||||||
|
:use_centered_layout,
|
||||||
|
:hide_vote_counts
|
||||||
])
|
])
|
||||||
|> TagList.propagate_tag_list(:watched_tag_list, :watched_tag_ids)
|
|> TagList.propagate_tag_list(:watched_tag_list, :watched_tag_ids)
|
||||||
|> validate_inclusion(:theme, ~W(default dark red))
|
|> validate_inclusion(:theme, ~W(default dark red))
|
||||||
|
@ -220,7 +239,10 @@ defmodule Philomena.Users.User do
|
||||||
|> cast(attrs, [:description, :personal_title])
|
|> cast(attrs, [:description, :personal_title])
|
||||||
|> validate_length(:description, max: 10_000, count: :bytes)
|
|> validate_length(:description, max: 10_000, count: :bytes)
|
||||||
|> validate_length(:personal_title, max: 24, count: :bytes)
|
|> validate_length(:personal_title, max: 24, count: :bytes)
|
||||||
|> validate_format(:personal_title, ~r/\A((?!site|admin|moderator|assistant|developer|\p{C}).)*\z/iu)
|
|> validate_format(
|
||||||
|
:personal_title,
|
||||||
|
~r/\A((?!site|admin|moderator|assistant|developer|\p{C}).)*\z/iu
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def scratchpad_changeset(user, attrs) do
|
def scratchpad_changeset(user, attrs) do
|
||||||
|
@ -231,11 +253,19 @@ defmodule Philomena.Users.User do
|
||||||
def avatar_changeset(user, attrs) do
|
def avatar_changeset(user, attrs) do
|
||||||
user
|
user
|
||||||
|> cast(attrs, [
|
|> cast(attrs, [
|
||||||
:avatar, :avatar_width, :avatar_height, :avatar_size, :uploaded_avatar,
|
:avatar,
|
||||||
|
:avatar_width,
|
||||||
|
:avatar_height,
|
||||||
|
:avatar_size,
|
||||||
|
:uploaded_avatar,
|
||||||
:removed_avatar
|
:removed_avatar
|
||||||
])
|
])
|
||||||
|> validate_required([
|
|> validate_required([
|
||||||
:avatar, :avatar_width, :avatar_height, :avatar_size, :uploaded_avatar
|
:avatar,
|
||||||
|
:avatar_width,
|
||||||
|
:avatar_height,
|
||||||
|
:avatar_size,
|
||||||
|
:uploaded_avatar
|
||||||
])
|
])
|
||||||
|> validate_number(:avatar_size, greater_than: 0, less_than_or_equal_to: 300_000)
|
|> validate_number(:avatar_size, greater_than: 0, less_than_or_equal_to: 300_000)
|
||||||
|> validate_number(:avatar_width, greater_than: 0, less_than_or_equal_to: 1000)
|
|> validate_number(:avatar_width, greater_than: 0, less_than_or_equal_to: 1000)
|
||||||
|
@ -321,7 +351,7 @@ defmodule Philomena.Users.User do
|
||||||
end
|
end
|
||||||
|
|
||||||
def random_backup_codes do
|
def random_backup_codes do
|
||||||
(1..10)
|
1..10
|
||||||
|> Enum.map(fn _i ->
|
|> Enum.map(fn _i ->
|
||||||
:crypto.strong_rand_bytes(6) |> Base.encode16(case: :lower)
|
:crypto.strong_rand_bytes(6) |> Base.encode16(case: :lower)
|
||||||
end)
|
end)
|
||||||
|
@ -329,14 +359,16 @@ defmodule Philomena.Users.User do
|
||||||
|
|
||||||
def totp_qrcode(user) do
|
def totp_qrcode(user) do
|
||||||
secret = totp_secret(user)
|
secret = totp_secret(user)
|
||||||
|
|
||||||
provisioning_uri = %URI{
|
provisioning_uri = %URI{
|
||||||
scheme: "otpauth",
|
scheme: "otpauth",
|
||||||
host: "totp",
|
host: "totp",
|
||||||
path: "/Derpibooru:" <> user.email,
|
path: "/Derpibooru:" <> user.email,
|
||||||
query: URI.encode_query(%{
|
query:
|
||||||
secret: secret,
|
URI.encode_query(%{
|
||||||
issuer: "Derpibooru"
|
secret: secret,
|
||||||
})
|
issuer: "Derpibooru"
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
png =
|
png =
|
||||||
|
@ -354,7 +386,6 @@ defmodule Philomena.Users.User do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
defp enable_totp_changeset(user, backup_codes) do
|
defp enable_totp_changeset(user, backup_codes) do
|
||||||
hashed_codes =
|
hashed_codes =
|
||||||
backup_codes
|
backup_codes
|
||||||
|
@ -409,7 +440,8 @@ defmodule Philomena.Users.User do
|
||||||
defp totp_valid?(user, token) do
|
defp totp_valid?(user, token) do
|
||||||
case Integer.parse(token) do
|
case Integer.parse(token) do
|
||||||
{int_token, _rest} ->
|
{int_token, _rest} ->
|
||||||
int_token != user.consumed_timestep and :pot.valid_totp(token, totp_secret(user), window: 1)
|
int_token != user.consumed_timestep and
|
||||||
|
:pot.valid_totp(token, totp_secret(user), window: 1)
|
||||||
|
|
||||||
_error ->
|
_error ->
|
||||||
false
|
false
|
||||||
|
|
|
@ -29,15 +29,14 @@ defmodule Philomena.Versions do
|
||||||
body = yaml["body"] || ""
|
body = yaml["body"] || ""
|
||||||
edit_reason = yaml["edit_reason"]
|
edit_reason = yaml["edit_reason"]
|
||||||
|
|
||||||
v =
|
v = %{
|
||||||
%{
|
version
|
||||||
version |
|
| parent: parent,
|
||||||
parent: parent,
|
|
||||||
user: users[version.whodunnit],
|
user: users[version.whodunnit],
|
||||||
body: body,
|
body: body,
|
||||||
edit_reason: edit_reason,
|
edit_reason: edit_reason,
|
||||||
difference: difference(body, previous_body)
|
difference: difference(body, previous_body)
|
||||||
}
|
}
|
||||||
|
|
||||||
{v, body}
|
{v, body}
|
||||||
end)
|
end)
|
||||||
|
@ -61,7 +60,12 @@ defmodule Philomena.Versions do
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def create_version(item_type, item_id, whodunnit, attrs \\ %{}) do
|
def create_version(item_type, item_id, whodunnit, attrs \\ %{}) do
|
||||||
%Version{item_type: item_type, item_id: item_id, event: "update", whodunnit: whodunnit(whodunnit)}
|
%Version{
|
||||||
|
item_type: item_type,
|
||||||
|
item_id: item_id,
|
||||||
|
event: "update",
|
||||||
|
whodunnit: whodunnit(whodunnit)
|
||||||
|
}
|
||||||
|> Version.changeset(attrs, item_id)
|
|> Version.changeset(attrs, item_id)
|
||||||
|> Repo.insert()
|
|> Repo.insert()
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,11 +31,12 @@ defmodule Philomena.Versions.Version do
|
||||||
body = get_field(changeset, :body)
|
body = get_field(changeset, :body)
|
||||||
edit_reason = get_field(changeset, :edit_reason)
|
edit_reason = get_field(changeset, :edit_reason)
|
||||||
|
|
||||||
object = Jason.encode!(%{
|
object =
|
||||||
id: item_id,
|
Jason.encode!(%{
|
||||||
body: body,
|
id: item_id,
|
||||||
edit_reason: edit_reason
|
body: body,
|
||||||
})
|
edit_reason: edit_reason
|
||||||
|
})
|
||||||
|
|
||||||
change(changeset, object: object)
|
change(changeset, object: object)
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,7 +20,11 @@ defmodule PhilomenaWeb.CommentJson do
|
||||||
id: comment.id,
|
id: comment.id,
|
||||||
image_id: comment.image_id,
|
image_id: comment.image_id,
|
||||||
user_id: if(not comment.anonymous, do: comment.user_id),
|
user_id: if(not comment.anonymous, do: comment.user_id),
|
||||||
author: if(comment.anonymous or is_nil(comment.user), do: UserAttributionView.anonymous_name(comment), else: comment.user.name),
|
author:
|
||||||
|
if(comment.anonymous or is_nil(comment.user),
|
||||||
|
do: UserAttributionView.anonymous_name(comment),
|
||||||
|
else: comment.user.name
|
||||||
|
),
|
||||||
body: nil
|
body: nil
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -30,7 +34,11 @@ defmodule PhilomenaWeb.CommentJson do
|
||||||
id: comment.id,
|
id: comment.id,
|
||||||
image_id: comment.image_id,
|
image_id: comment.image_id,
|
||||||
user_id: if(not comment.anonymous, do: comment.user_id),
|
user_id: if(not comment.anonymous, do: comment.user_id),
|
||||||
author: if(comment.anonymous or is_nil(comment.user), do: UserAttributionView.anonymous_name(comment), else: comment.user.name),
|
author:
|
||||||
|
if(comment.anonymous or is_nil(comment.user),
|
||||||
|
do: UserAttributionView.anonymous_name(comment),
|
||||||
|
else: comment.user.name
|
||||||
|
),
|
||||||
body: comment.body
|
body: comment.body
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,16 @@ defmodule PhilomenaWeb.ActivityController do
|
||||||
|
|
||||||
alias PhilomenaWeb.ImageLoader
|
alias PhilomenaWeb.ImageLoader
|
||||||
alias Philomena.Elasticsearch
|
alias Philomena.Elasticsearch
|
||||||
alias Philomena.{Images.Image, ImageFeatures.ImageFeature, Comments.Comment, Channels.Channel, Topics.Topic, Forums.Forum}
|
|
||||||
|
alias Philomena.{
|
||||||
|
Images.Image,
|
||||||
|
ImageFeatures.ImageFeature,
|
||||||
|
Comments.Comment,
|
||||||
|
Channels.Channel,
|
||||||
|
Topics.Topic,
|
||||||
|
Forums.Forum
|
||||||
|
}
|
||||||
|
|
||||||
alias Philomena.Interactions
|
alias Philomena.Interactions
|
||||||
alias Philomena.Repo
|
alias Philomena.Repo
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
@ -47,16 +56,17 @@ defmodule PhilomenaWeb.ActivityController do
|
||||||
Comment |> preload([:user, image: [:tags]])
|
Comment |> preload([:user, image: [:tags]])
|
||||||
)
|
)
|
||||||
|
|
||||||
watched = if !!user do
|
watched =
|
||||||
{:ok, {watched_images, _tags}} =
|
if !!user do
|
||||||
ImageLoader.search_string(
|
{:ok, {watched_images, _tags}} =
|
||||||
conn,
|
ImageLoader.search_string(
|
||||||
"my:watched",
|
conn,
|
||||||
pagination: %{conn.assigns.image_pagination | page_number: 1}
|
"my:watched",
|
||||||
)
|
pagination: %{conn.assigns.image_pagination | page_number: 1}
|
||||||
|
)
|
||||||
|
|
||||||
if Enum.any?(watched_images), do: watched_images
|
if Enum.any?(watched_images), do: watched_images
|
||||||
end
|
end
|
||||||
|
|
||||||
featured_image =
|
featured_image =
|
||||||
Image
|
Image
|
||||||
|
@ -68,7 +78,7 @@ defmodule PhilomenaWeb.ActivityController do
|
||||||
|
|
||||||
streams =
|
streams =
|
||||||
Channel
|
Channel
|
||||||
|> where([c], c.nsfw == false)
|
|> where([c], c.nsfw == false)
|
||||||
|> where([c], not is_nil(c.last_fetched_at))
|
|> where([c], not is_nil(c.last_fetched_at))
|
||||||
|> order_by(desc: :is_live, asc: :title)
|
|> order_by(desc: :is_live, asc: :title)
|
||||||
|> limit(6)
|
|> limit(6)
|
||||||
|
|
|
@ -15,7 +15,11 @@ defmodule PhilomenaWeb.Admin.AdvertController do
|
||||||
|> order_by(desc: :finish_date)
|
|> order_by(desc: :finish_date)
|
||||||
|> Repo.paginate(conn.assigns.scrivener)
|
|> Repo.paginate(conn.assigns.scrivener)
|
||||||
|
|
||||||
render(conn, "index.html", title: "Admin - Adverts", layout_class: "layout--wide", adverts: adverts)
|
render(conn, "index.html",
|
||||||
|
title: "Admin - Adverts",
|
||||||
|
layout_class: "layout--wide",
|
||||||
|
adverts: adverts
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def new(conn, _params) do
|
def new(conn, _params) do
|
||||||
|
@ -62,7 +66,7 @@ defmodule PhilomenaWeb.Admin.AdvertController do
|
||||||
|
|
||||||
defp verify_authorized(conn, _opts) do
|
defp verify_authorized(conn, _opts) do
|
||||||
case Canada.Can.can?(conn.assigns.current_user, :index, Advert) do
|
case Canada.Can.can?(conn.assigns.current_user, :index, Advert) do
|
||||||
true -> conn
|
true -> conn
|
||||||
false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,7 +24,7 @@ defmodule PhilomenaWeb.Admin.Badge.UserController do
|
||||||
|
|
||||||
defp verify_authorized(conn, _opts) do
|
defp verify_authorized(conn, _opts) do
|
||||||
case Canada.Can.can?(conn.assigns.current_user, :index, Badge) do
|
case Canada.Can.can?(conn.assigns.current_user, :index, Badge) do
|
||||||
true -> conn
|
true -> conn
|
||||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -54,7 +54,7 @@ defmodule PhilomenaWeb.Admin.BadgeController do
|
||||||
|
|
||||||
defp verify_authorized(conn, _opts) do
|
defp verify_authorized(conn, _opts) do
|
||||||
case Canada.Can.can?(conn.assigns.current_user, :index, Badge) do
|
case Canada.Can.can?(conn.assigns.current_user, :index, Badge) do
|
||||||
true -> conn
|
true -> conn
|
||||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,6 +14,7 @@ defmodule PhilomenaWeb.Admin.Batch.TagController do
|
||||||
tags = Tag.parse_tag_list(tags)
|
tags = Tag.parse_tag_list(tags)
|
||||||
|
|
||||||
added_tag_names = Enum.reject(tags, &String.starts_with?(&1, "-"))
|
added_tag_names = Enum.reject(tags, &String.starts_with?(&1, "-"))
|
||||||
|
|
||||||
removed_tag_names =
|
removed_tag_names =
|
||||||
tags
|
tags
|
||||||
|> Enum.filter(&String.starts_with?(&1, "-"))
|
|> Enum.filter(&String.starts_with?(&1, "-"))
|
||||||
|
@ -24,7 +25,7 @@ defmodule PhilomenaWeb.Admin.Batch.TagController do
|
||||||
|> where([t], t.name in ^added_tag_names)
|
|> where([t], t.name in ^added_tag_names)
|
||||||
|> preload([:implied_tags, aliased_tag: :implied_tags])
|
|> preload([:implied_tags, aliased_tag: :implied_tags])
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|> Enum.map(& &1.aliased_tag || &1)
|
|> Enum.map(&(&1.aliased_tag || &1))
|
||||||
|> Enum.flat_map(&[&1 | &1.implied_tags])
|
|> Enum.flat_map(&[&1 | &1.implied_tags])
|
||||||
|
|
||||||
removed_tags =
|
removed_tags =
|
||||||
|
@ -33,6 +34,7 @@ defmodule PhilomenaWeb.Admin.Batch.TagController do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|
|
||||||
attributes = conn.assigns.attributes
|
attributes = conn.assigns.attributes
|
||||||
|
|
||||||
attributes = %{
|
attributes = %{
|
||||||
ip: attributes[:ip],
|
ip: attributes[:ip],
|
||||||
fingerprint: attributes[:fingerprint],
|
fingerprint: attributes[:fingerprint],
|
||||||
|
@ -57,7 +59,7 @@ defmodule PhilomenaWeb.Admin.Batch.TagController do
|
||||||
|
|
||||||
defp verify_authorized(conn, _opts) do
|
defp verify_authorized(conn, _opts) do
|
||||||
case Canada.Can.can?(conn.assigns.current_user, :batch_update, Tag) do
|
case Canada.Can.can?(conn.assigns.current_user, :batch_update, Tag) do
|
||||||
true -> conn
|
true -> conn
|
||||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,11 @@ defmodule PhilomenaWeb.Admin.DnpEntry.TransitionController do
|
||||||
plug :load_resource, model: DnpEntry, only: [:create], id_name: "dnp_entry_id", persisted: true
|
plug :load_resource, model: DnpEntry, only: [:create], id_name: "dnp_entry_id", persisted: true
|
||||||
|
|
||||||
def create(conn, %{"state" => new_state}) do
|
def create(conn, %{"state" => new_state}) do
|
||||||
case DnpEntries.transition_dnp_entry(conn.assigns.dnp_entry, conn.assigns.current_user, new_state) do
|
case DnpEntries.transition_dnp_entry(
|
||||||
|
conn.assigns.dnp_entry,
|
||||||
|
conn.assigns.current_user,
|
||||||
|
new_state
|
||||||
|
) do
|
||||||
{:ok, dnp_entry} ->
|
{:ok, dnp_entry} ->
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, "Successfully updated DNP entry.")
|
|> put_flash(:info, "Successfully updated DNP entry.")
|
||||||
|
@ -23,7 +27,7 @@ defmodule PhilomenaWeb.Admin.DnpEntry.TransitionController do
|
||||||
|
|
||||||
defp verify_authorized(conn, _opts) do
|
defp verify_authorized(conn, _opts) do
|
||||||
case Canada.Can.can?(conn.assigns.current_user, :index, DnpEntry) do
|
case Canada.Can.can?(conn.assigns.current_user, :index, DnpEntry) do
|
||||||
true -> conn
|
true -> conn
|
||||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,7 +21,11 @@ defmodule PhilomenaWeb.Admin.DnpEntryController do
|
||||||
DnpEntry
|
DnpEntry
|
||||||
|> join(:inner, [d], _ in assoc(d, :tag))
|
|> join(:inner, [d], _ in assoc(d, :tag))
|
||||||
|> join(:inner, [d, _t], _ in assoc(d, :requesting_user))
|
|> join(:inner, [d, _t], _ in assoc(d, :requesting_user))
|
||||||
|> where([d, t, u], ilike(u.name, ^q) or ilike(t.name, ^q) or ilike(d.reason, ^q) or ilike(d.conditions, ^q) or ilike(d.instructions, ^q))
|
|> where(
|
||||||
|
[d, t, u],
|
||||||
|
ilike(u.name, ^q) or ilike(t.name, ^q) or ilike(d.reason, ^q) or ilike(d.conditions, ^q) or
|
||||||
|
ilike(d.instructions, ^q)
|
||||||
|
)
|
||||||
|> load_entries(conn)
|
|> load_entries(conn)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,15 +47,18 @@ defmodule PhilomenaWeb.Admin.DnpEntryController do
|
||||||
|> Enum.map(&%{body: &1.conditions})
|
|> Enum.map(&%{body: &1.conditions})
|
||||||
|> Renderer.render_collection(conn)
|
|> Renderer.render_collection(conn)
|
||||||
|
|
||||||
dnp_entries =
|
dnp_entries = %{dnp_entries | entries: Enum.zip(bodies, dnp_entries.entries)}
|
||||||
%{dnp_entries | entries: Enum.zip(bodies, dnp_entries.entries)}
|
|
||||||
|
|
||||||
render(conn, "index.html", layout_class: "layout--wide", title: "Admin - DNP Entries", dnp_entries: dnp_entries)
|
render(conn, "index.html",
|
||||||
|
layout_class: "layout--wide",
|
||||||
|
title: "Admin - DNP Entries",
|
||||||
|
dnp_entries: dnp_entries
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp verify_authorized(conn, _opts) do
|
defp verify_authorized(conn, _opts) do
|
||||||
case Canada.Can.can?(conn.assigns.current_user, :index, DnpEntry) do
|
case Canada.Can.can?(conn.assigns.current_user, :index, DnpEntry) do
|
||||||
true -> conn
|
true -> conn
|
||||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,12 +11,17 @@ defmodule PhilomenaWeb.Admin.Donation.UserController do
|
||||||
def show(conn, _params) do
|
def show(conn, _params) do
|
||||||
user = conn.assigns.user
|
user = conn.assigns.user
|
||||||
changeset = Donations.change_donation(%Donation{})
|
changeset = Donations.change_donation(%Donation{})
|
||||||
render(conn, "index.html", title: "Donations for User `#{user.name}'", donations: user.donations, changeset: changeset)
|
|
||||||
|
render(conn, "index.html",
|
||||||
|
title: "Donations for User `#{user.name}'",
|
||||||
|
donations: user.donations,
|
||||||
|
changeset: changeset
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp verify_authorized(conn, _opts) do
|
defp verify_authorized(conn, _opts) do
|
||||||
case Canada.Can.can?(conn.assigns.current_user, :index, Donation) do
|
case Canada.Can.can?(conn.assigns.current_user, :index, Donation) do
|
||||||
true -> conn
|
true -> conn
|
||||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,7 @@ defmodule PhilomenaWeb.Admin.DonationController do
|
||||||
|
|
||||||
defp verify_authorized(conn, _opts) do
|
defp verify_authorized(conn, _opts) do
|
||||||
case Canada.Can.can?(conn.assigns.current_user, :index, Donation) do
|
case Canada.Can.can?(conn.assigns.current_user, :index, Donation) do
|
||||||
true -> conn
|
true -> conn
|
||||||
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue