mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-23 20:18:00 +01:00
galleries index
This commit is contained in:
parent
de48c06f99
commit
1ad4f8f076
12 changed files with 186 additions and 10 deletions
2
.iex.exs
2
.iex.exs
|
@ -1,3 +1,3 @@
|
||||||
alias Philomena.{Repo, Comments.Comment, Posts.Post, Images.Image, 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
|
||||||
|
|
46
lib/philomena/galleries/elasticsearch.ex
Normal file
46
lib/philomena/galleries/elasticsearch.ex
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
defmodule Philomena.Galleries.Elasticsearch do
|
||||||
|
def mapping do
|
||||||
|
%{
|
||||||
|
settings: %{
|
||||||
|
index: %{
|
||||||
|
number_of_shards: 5,
|
||||||
|
max_result_window: 10_000_000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mappings: %{
|
||||||
|
gallery: %{
|
||||||
|
_all: %{enabled: false},
|
||||||
|
dynamic: false,
|
||||||
|
properties: %{
|
||||||
|
id: %{type: "integer"}, # keyword
|
||||||
|
image_count: %{type: "integer"},
|
||||||
|
watcher_count: %{type: "integer"},
|
||||||
|
updated_at: %{type: "date"},
|
||||||
|
created_at: %{type: "date"},
|
||||||
|
title: %{type: "keyword"},
|
||||||
|
creator: %{type: "keyword"}, # missing creator_id
|
||||||
|
image_ids: %{type: "keyword"},
|
||||||
|
watcher_ids: %{type: "keyword"}, # ???
|
||||||
|
description: %{type: "text", analyzer: "snowball"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# [:subscribers, :creator, :interactions]
|
||||||
|
def as_json(gallery) do
|
||||||
|
%{
|
||||||
|
id: gallery.id,
|
||||||
|
image_count: gallery.image_count,
|
||||||
|
watcher_count: length(gallery.subscribers),
|
||||||
|
watcher_ids: Enum.map(gallery.subscribers, & &1.id),
|
||||||
|
updated_at: gallery.updated_at,
|
||||||
|
created_at: gallery.created_at,
|
||||||
|
title: String.downcase(gallery.title),
|
||||||
|
creator: String.downcase(gallery.creator.name),
|
||||||
|
image_ids: Enum.map(gallery.interactions, & &1.image_id),
|
||||||
|
description: gallery.description
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,11 @@ defmodule Philomena.Galleries.Gallery do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
use Philomena.Elasticsearch,
|
||||||
|
definition: Philomena.Galleries.Elasticsearch,
|
||||||
|
index_name: "galleries",
|
||||||
|
doc_type: "gallery"
|
||||||
|
|
||||||
alias Philomena.Images.Image
|
alias Philomena.Images.Image
|
||||||
alias Philomena.Users.User
|
alias Philomena.Users.User
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,7 @@ defmodule Philomena.Images do
|
||||||
{:error, %Ecto.Changeset{}}
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
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(%{})
|
||||||
|
|
|
@ -123,7 +123,7 @@ defmodule Philomena.Topics do
|
||||||
{:error, %Ecto.Changeset{}}
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
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(%{})
|
||||||
|
|
|
@ -38,7 +38,7 @@ defmodule PhilomenaWeb.CommentController do
|
||||||
end
|
end
|
||||||
defp parse_search(_conn, _params), do: [%{match_all: %{}}]
|
defp parse_search(_conn, _params), do: [%{match_all: %{}}]
|
||||||
|
|
||||||
defp parse_author(%{"author" => author}) when author not in [nil, ""] do
|
defp parse_author(%{"author" => author}) when is_binary(author) and author not in [nil, ""] do
|
||||||
case String.contains?(author, ["*", "?"]) do
|
case String.contains?(author, ["*", "?"]) do
|
||||||
true ->
|
true ->
|
||||||
[
|
[
|
||||||
|
@ -55,7 +55,7 @@ defmodule PhilomenaWeb.CommentController do
|
||||||
end
|
end
|
||||||
defp parse_author(_params), do: []
|
defp parse_author(_params), do: []
|
||||||
|
|
||||||
defp parse_image_id(conn, %{"image_id" => image_id}) when image_id not in [nil, ""] do
|
defp parse_image_id(conn, %{"image_id" => image_id}) when is_binary(image_id) and image_id not in [nil, ""] do
|
||||||
with {image_id, _rest} <- Integer.parse(image_id),
|
with {image_id, _rest} <- Integer.parse(image_id),
|
||||||
true <- valid_image?(conn.assigns.current_user, image_id)
|
true <- valid_image?(conn.assigns.current_user, image_id)
|
||||||
do
|
do
|
||||||
|
@ -67,7 +67,7 @@ defmodule PhilomenaWeb.CommentController do
|
||||||
end
|
end
|
||||||
defp parse_image_id(_conn, _params), do: []
|
defp parse_image_id(_conn, _params), do: []
|
||||||
|
|
||||||
defp parse_body(%{"body" => body}) when body not in [nil, ""],
|
defp parse_body(%{"body" => body}) when is_binary(body) and body not in [nil, ""],
|
||||||
do: [%{match: %{body: body}}]
|
do: [%{match: %{body: body}}]
|
||||||
defp parse_body(_params), do: []
|
defp parse_body(_params), do: []
|
||||||
|
|
||||||
|
|
69
lib/philomena_web/controllers/gallery_controller.ex
Normal file
69
lib/philomena_web/controllers/gallery_controller.ex
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
defmodule PhilomenaWeb.GalleryController do
|
||||||
|
use PhilomenaWeb, :controller
|
||||||
|
|
||||||
|
alias Philomena.Galleries.Gallery
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
plug :load_resource, model: Gallery, preload: [:thumbnail, :creator]
|
||||||
|
|
||||||
|
def index(conn, params) do
|
||||||
|
galleries =
|
||||||
|
Gallery.search_records(
|
||||||
|
%{
|
||||||
|
query: %{
|
||||||
|
bool: %{
|
||||||
|
must: parse_search(params)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sort: parse_sort(params),
|
||||||
|
},
|
||||||
|
conn.assigns.pagination,
|
||||||
|
Gallery |> preload([:thumbnail, :creator])
|
||||||
|
)
|
||||||
|
|
||||||
|
render(conn, "index.html", galleries: galleries, layout_class: "layout--wide")
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(_conn, _params) do
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_search(%{"gallery" => gallery_params}) do
|
||||||
|
parse_title(gallery_params) ++
|
||||||
|
parse_creator(gallery_params) ++
|
||||||
|
parse_included_image(gallery_params) ++
|
||||||
|
parse_description(gallery_params)
|
||||||
|
end
|
||||||
|
defp parse_search(_params), do: [%{match_all: %{}}]
|
||||||
|
|
||||||
|
defp parse_title(%{"title" => title}) when is_binary(title) and title not in [nil, ""],
|
||||||
|
do: [%{wildcard: %{title: "*" <> String.downcase(title) <> "*"}}]
|
||||||
|
defp parse_title(_params), do: []
|
||||||
|
|
||||||
|
defp parse_creator(%{"creator" => creator}) when is_binary(creator) and creator not in [nil, ""],
|
||||||
|
do: [%{term: %{creator: String.downcase(creator)}}]
|
||||||
|
defp parse_creator(_params), do: []
|
||||||
|
|
||||||
|
defp parse_included_image(%{"include_image" => image_id}) when is_binary(image_id) and image_id not in [nil, ""] do
|
||||||
|
with {image_id, _rest} <- Integer.parse(image_id) do
|
||||||
|
[%{term: %{image_id: image_id}}]
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
defp parse_included_image(_params), do: []
|
||||||
|
|
||||||
|
defp parse_description(%{"description" => description}) when is_binary(description) and description not in [nil, ""],
|
||||||
|
do: [%{match: %{description: %{query: description, operator: :and}}}]
|
||||||
|
defp parse_description(_params), do: []
|
||||||
|
|
||||||
|
defp parse_sort(%{"gallery" => %{"sf" => sf, "sd" => sd}})
|
||||||
|
when sf in ["created_at", "updated_at", "image_count", "_score"]
|
||||||
|
and sd in ["desc", "asc"]
|
||||||
|
do
|
||||||
|
%{sf => sd}
|
||||||
|
end
|
||||||
|
defp parse_sort(_params) do
|
||||||
|
%{created_at: :desc}
|
||||||
|
end
|
||||||
|
end
|
|
@ -49,7 +49,7 @@ defmodule PhilomenaWeb.PostController do
|
||||||
end
|
end
|
||||||
defp parse_search(_conn, _params), do: [%{match_all: %{}}]
|
defp parse_search(_conn, _params), do: [%{match_all: %{}}]
|
||||||
|
|
||||||
defp parse_author(%{"author" => author}) when author not in [nil, ""] do
|
defp parse_author(%{"author" => author}) when is_binary(author) and author not in [nil, ""] do
|
||||||
case String.contains?(author, ["*", "?"]) do
|
case String.contains?(author, ["*", "?"]) do
|
||||||
true ->
|
true ->
|
||||||
[
|
[
|
||||||
|
@ -66,12 +66,12 @@ defmodule PhilomenaWeb.PostController do
|
||||||
end
|
end
|
||||||
defp parse_author(_params), do: []
|
defp parse_author(_params), do: []
|
||||||
|
|
||||||
defp parse_subject(%{"subject" => subject}) when subject not in [nil, ""] do
|
defp parse_subject(%{"subject" => subject}) when is_binary(subject) and subject not in [nil, ""] do
|
||||||
[%{match: %{subject: %{query: subject, operator: "and"}}}]
|
[%{match: %{subject: %{query: subject, operator: "and"}}}]
|
||||||
end
|
end
|
||||||
defp parse_subject(_params), do: []
|
defp parse_subject(_params), do: []
|
||||||
|
|
||||||
defp parse_forum_id(conn, %{"forum_id" => forum_id}) when forum_id not in [nil, ""] do
|
defp parse_forum_id(conn, %{"forum_id" => forum_id}) when is_binary(forum_id) and forum_id not in [nil, ""] do
|
||||||
with {forum_id, _rest} <- Integer.parse(forum_id),
|
with {forum_id, _rest} <- Integer.parse(forum_id),
|
||||||
true <- valid_forum?(conn.assigns.current_user, forum_id)
|
true <- valid_forum?(conn.assigns.current_user, forum_id)
|
||||||
do
|
do
|
||||||
|
@ -83,7 +83,7 @@ defmodule PhilomenaWeb.PostController do
|
||||||
end
|
end
|
||||||
defp parse_forum_id(_conn, _params), do: []
|
defp parse_forum_id(_conn, _params), do: []
|
||||||
|
|
||||||
defp parse_body(%{"body" => body}) when body not in [nil, ""],
|
defp parse_body(%{"body" => body}) when is_binary(body) and body not in [nil, ""],
|
||||||
do: [%{match: %{body: body}}]
|
do: [%{match: %{body: body}}]
|
||||||
defp parse_body(_params), do: []
|
defp parse_body(_params), do: []
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,7 @@ defmodule PhilomenaWeb.Router do
|
||||||
resources "/captchas", CaptchaController, only: [:create]
|
resources "/captchas", CaptchaController, only: [:create]
|
||||||
resources "/posts", PostController, only: [:index]
|
resources "/posts", PostController, only: [:index]
|
||||||
resources "/commissions", CommissionController, only: [:index, :show]
|
resources "/commissions", CommissionController, only: [:index, :show]
|
||||||
|
resources "/galleries", GalleryController, only: [:index, :show]
|
||||||
|
|
||||||
get "/:id", ImageController, :show
|
get "/:id", ImageController, :show
|
||||||
end
|
end
|
||||||
|
|
10
lib/philomena_web/templates/gallery/_gallery.html.slime
Normal file
10
lib/philomena_web/templates/gallery/_gallery.html.slime
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
.media-box
|
||||||
|
a.media-box__header.media-box__header--link href=Routes.gallery_path(@conn, :show, @gallery) title=@gallery.title
|
||||||
|
= @gallery.title
|
||||||
|
|
||||||
|
.media-box__overlay
|
||||||
|
= @gallery.spoiler_warning
|
||||||
|
|
||||||
|
.media-box__content.media-box__content--large
|
||||||
|
a href=Routes.gallery_path(@conn, :show, @gallery)
|
||||||
|
= render PhilomenaWeb.ImageView, "_image_container.html", image: @gallery.thumbnail, size: :thumb, conn: @conn
|
42
lib/philomena_web/templates/gallery/index.html.slime
Normal file
42
lib/philomena_web/templates/gallery/index.html.slime
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
elixir:
|
||||||
|
route = fn p -> Routes.gallery_path(@conn, :index, p) end
|
||||||
|
pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @galleries, route: route
|
||||||
|
|
||||||
|
.column-layout
|
||||||
|
.column-layout__left
|
||||||
|
.block
|
||||||
|
.block__content
|
||||||
|
h3 Search Galleries
|
||||||
|
|
||||||
|
= form_for @conn, Routes.gallery_path(@conn, :index), [as: :gallery, method: "get", class: "hform"], fn f ->
|
||||||
|
.field = label f, :title, "Title"
|
||||||
|
.field = text_input f, :title, class: "input hform__text", placeholder: "Gallery title (* as wildcard)"
|
||||||
|
|
||||||
|
.field = label f, :description, "Description"
|
||||||
|
.field = textarea f, :description, class: "input hform__text", placeholder: "Gallery description"
|
||||||
|
|
||||||
|
.field = label f, :creator, "Creator"
|
||||||
|
.field = text_input f, :creator, class: "input hform__text", placeholder: "Gallery creator (exact match)"
|
||||||
|
|
||||||
|
.field = label f, :include_image, "Include image"
|
||||||
|
.field = number_input f, :include_image, class: "input hform__text", placeholder: "Image ID (e.g. 100)"
|
||||||
|
|
||||||
|
.field = label f, :sf, "Sort by"
|
||||||
|
.field
|
||||||
|
=> select f, :sf, ["Creation Date": "created_at", "Update Date": "updated_at", "Image Count": "image_count", "Relevance": "_score"], class: "input"
|
||||||
|
=> select f, :sd, ["Descending": "desc", "Ascending": "asc"], class: "input"
|
||||||
|
|
||||||
|
.field
|
||||||
|
= submit "Search", class: "button button--state-primary"
|
||||||
|
|
||||||
|
.column-layout__main
|
||||||
|
.block
|
||||||
|
.block__header
|
||||||
|
= pagination
|
||||||
|
|
||||||
|
.block__content.js-resizable-media-container
|
||||||
|
= for gallery <- @galleries do
|
||||||
|
= render PhilomenaWeb.GalleryView, "_gallery.html", gallery: gallery, conn: @conn
|
||||||
|
|
||||||
|
.block__header.block__header--light
|
||||||
|
= pagination
|
3
lib/philomena_web/views/gallery_view.ex
Normal file
3
lib/philomena_web/views/gallery_view.ex
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
defmodule PhilomenaWeb.GalleryView do
|
||||||
|
use PhilomenaWeb, :view
|
||||||
|
end
|
Loading…
Reference in a new issue