philomena/lib/philomena_web/image_loader.ex

161 lines
4.3 KiB
Elixir
Raw Normal View History

2019-12-01 03:22:05 +01:00
defmodule PhilomenaWeb.ImageLoader do
2020-05-29 01:43:17 +02:00
alias PhilomenaWeb.ImageSorter
alias Philomena.Elasticsearch
2019-12-01 03:22:05 +01:00
alias Philomena.Images.{Image, Query}
2020-05-08 04:19:08 +02:00
alias PhilomenaWeb.TextileRenderer
2019-12-05 22:57:59 +01:00
alias Philomena.Tags.Tag
alias Philomena.Repo
2019-12-01 03:22:05 +01:00
import Ecto.Query
def search_string(conn, search_string, options \\ []) do
user = conn.assigns.current_user
with {:ok, tree} <- Query.compile(user, search_string) do
{:ok, query(conn, tree, options)}
else
error ->
error
end
end
def query(conn, body, options \\ []) do
2020-01-11 05:20:19 +01:00
pagination = Keyword.get(options, :pagination, conn.assigns.image_pagination)
2020-08-16 12:03:33 +02:00
queryable = Keyword.get(options, :queryable, Image)
2020-05-29 01:43:17 +02:00
sorts = Keyword.get(options, :sorts, &ImageSorter.parse_sort(conn.params, &1))
2019-12-01 03:22:05 +01:00
2019-12-27 00:58:52 +01:00
tags =
body
|> search_tag_names()
|> load_tags()
|> render_bodies(conn)
2020-01-11 05:20:19 +01:00
user = conn.assigns.current_user
filter = conn.assigns.compiled_filter
filters = create_filters(conn, user, filter)
2020-05-29 01:43:17 +02:00
%{query: query, sorts: sort} = sorts.(body)
2020-01-11 05:20:19 +01:00
2019-12-05 22:57:59 +01:00
records =
search_function(options).(
Image,
2019-12-05 22:57:59 +01:00
%{
query: %{
bool: %{
2020-05-29 01:43:17 +02:00
must: query,
2019-12-05 22:57:59 +01:00
must_not: filters
}
},
2020-05-29 01:43:17 +02:00
sort: sort
2019-12-01 03:22:05 +01:00
},
2019-12-05 22:57:59 +01:00
pagination,
queryable
)
{records, tags}
2019-12-01 03:22:05 +01:00
end
defp create_filters(conn, user, filter) do
show_hidden? = Canada.Can.can?(user, :hide, %Image{})
del = conn.params["del"]
hidden = conn.params["hidden"]
2019-12-01 03:22:05 +01:00
[
filter
2019-12-01 03:22:05 +01:00
]
|> maybe_show_deleted(show_hidden?, del)
|> maybe_custom_hide(user, hidden)
2019-12-01 03:22:05 +01:00
end
# Allow moderators to index hidden images
2020-02-01 17:04:11 +01:00
defp maybe_show_deleted(filters, _show_hidden?, "1"),
do: filters
defp maybe_show_deleted(filters, false, _param),
do: [%{term: %{hidden_from_users: true}} | filters]
defp maybe_show_deleted(filters, true, "only"),
do: [%{term: %{hidden_from_users: false}} | filters]
defp maybe_show_deleted(filters, true, "deleted"),
2020-01-11 05:20:19 +01:00
do: [%{term: %{hidden_from_users: false}}, %{exists: %{field: :duplicate_id}} | filters]
defp maybe_show_deleted(filters, true, _param),
do: [%{term: %{hidden_from_users: true}} | filters]
# Allow users to reverse the effect of hiding images,
# if desired
2019-12-18 01:44:26 +01:00
defp maybe_custom_hide(filters, %{id: _id}, "1"),
do: filters
defp maybe_custom_hide(filters, %{id: id}, _param),
2019-12-01 03:22:05 +01:00
do: [%{term: %{hidden_by_user_ids: id}} | filters]
defp maybe_custom_hide(filters, _user, _param),
2019-12-01 03:22:05 +01:00
do: filters
2019-12-05 22:57:59 +01:00
# Allow callers to choose if they want inner hit objects returned;
# primarily useful for allowing client navigation through images
@spec search_function(Keyword.t()) :: function()
defp search_function(options) do
case Keyword.get(options, :include_hits) do
true -> &Elasticsearch.search_records_with_hits/4
_false -> &Elasticsearch.search_records/4
end
end
2019-12-05 22:57:59 +01:00
# TODO: the search parser should try to optimize queries
defp search_tag_name(%{term: %{"namespaced_tags.name" => tag_name}}), do: [tag_name]
defp search_tag_name(_other_query), do: []
defp search_tag_names(%{bool: %{must: musts}}), do: Enum.flat_map(musts, &search_tag_name(&1))
2020-01-11 05:20:19 +01:00
defp search_tag_names(%{bool: %{should: shoulds}}),
do: Enum.flat_map(shoulds, &search_tag_name(&1))
2019-12-05 22:57:59 +01:00
defp search_tag_names(%{term: %{"namespaced_tags.name" => tag_name}}), do: [tag_name]
defp search_tag_names(_other_query), do: []
defp load_tags([]), do: []
2020-01-11 05:20:19 +01:00
2019-12-05 22:57:59 +01:00
defp load_tags(tags) do
Tag
|> join(:left, [t], at in Tag, on: t.id == at.aliased_tag_id)
|> where([t, at], t.name in ^tags or at.name in ^tags)
2020-01-11 05:20:19 +01:00
|> preload([
:aliases,
:aliased_tag,
:implied_tags,
:implied_by_tags,
:dnp_entries,
:channels,
2020-01-11 05:20:19 +01:00
public_links: :user,
hidden_links: :user
])
2019-12-05 22:57:59 +01:00
|> Repo.all()
2019-12-08 02:56:08 +01:00
|> Enum.uniq_by(& &1.id)
2019-12-08 03:00:36 +01:00
|> Enum.filter(&is_nil(&1.aliased_tag))
2019-12-08 19:04:06 +01:00
|> Tag.display_order()
2019-12-05 22:57:59 +01:00
end
2019-12-05 23:01:09 +01:00
defp render_bodies([], _conn), do: []
2020-01-11 05:20:19 +01:00
2019-12-05 23:01:09 +01:00
defp render_bodies([tag], conn) do
2019-12-05 22:57:59 +01:00
dnp_bodies =
2020-05-20 20:23:57 +02:00
TextileRenderer.render_collection(
Enum.map(tag.dnp_entries, &%{body: &1.conditions || ""}),
conn
)
2019-12-05 22:57:59 +01:00
2020-01-11 05:20:19 +01:00
dnp_entries = Enum.zip(dnp_bodies, tag.dnp_entries)
2019-12-05 22:57:59 +01:00
2020-05-08 04:19:08 +02:00
description = TextileRenderer.render_one(%{body: tag.description || ""}, conn)
2019-12-05 23:01:09 +01:00
[{tag, description, dnp_entries}]
2019-12-05 22:57:59 +01:00
end
2020-01-11 05:20:19 +01:00
2019-12-05 23:01:09 +01:00
defp render_bodies(tags, _conn), do: tags
end