diff --git a/lib/philomena/elasticsearch.ex b/lib/philomena/elasticsearch.ex index a326597b..f55a1dd7 100644 --- a/lib/philomena/elasticsearch.ex +++ b/lib/philomena/elasticsearch.ex @@ -35,6 +35,15 @@ defmodule Philomena.Elasticsearch do ) end + def delete_document(id) do + Elastix.Document.delete( + unquote(elastic_url), + unquote(index_name), + unquote(doc_type), + id + ) + end + def reindex(ecto_query, batch_size \\ 1000) do ids = ecto_query diff --git a/lib/philomena/galleries.ex b/lib/philomena/galleries.ex index 97bc9c39..f6ec80cf 100644 --- a/lib/philomena/galleries.ex +++ b/lib/philomena/galleries.ex @@ -49,9 +49,9 @@ defmodule Philomena.Galleries do {:error, %Ecto.Changeset{}} """ - def create_gallery(attrs \\ %{}) do + def create_gallery(user, attrs \\ %{}) do %Gallery{} - |> Gallery.changeset(attrs) + |> Gallery.creation_changeset(attrs, user) |> Repo.insert() end @@ -102,6 +102,30 @@ defmodule Philomena.Galleries do Gallery.changeset(gallery, %{}) end + def reindex_gallery(%Gallery{} = gallery) do + spawn fn -> + gallery + |> preload(^indexing_preloads()) + |> where(id: ^gallery.id) + |> Repo.one() + |> Gallery.index_document() + end + + gallery + end + + def unindex_gallery(%Gallery{} = gallery) do + spawn fn -> + Gallery.delete_document(gallery.id) + end + + gallery + end + + def indexing_preloads do + [:subscribers, :creator, :interactions] + end + alias Philomena.Galleries.Subscription @doc """ diff --git a/lib/philomena/galleries/gallery.ex b/lib/philomena/galleries/gallery.ex index bbe9390b..41fe7333 100644 --- a/lib/philomena/galleries/gallery.ex +++ b/lib/philomena/galleries/gallery.ex @@ -9,10 +9,12 @@ defmodule Philomena.Galleries.Gallery do alias Philomena.Images.Image alias Philomena.Users.User + alias Philomena.Galleries.Interaction schema "galleries" do belongs_to :thumbnail, Image, source: :thumbnail_id belongs_to :creator, User, source: :creator_id + has_many :interactions, Interaction field :title, :string field :spoiler_warning, :string @@ -26,7 +28,18 @@ defmodule Philomena.Galleries.Gallery do @doc false def changeset(gallery, attrs) do gallery - |> cast(attrs, []) - |> validate_required([]) + |> cast(attrs, [:thumbnail_id, :title, :spoiler_warning, :description, :order_position_asc]) + |> validate_required([:title]) + |> validate_length(:title, max: 100, count: :bytes) + |> validate_length(:spoiler_warning, max: 20, count: :bytes) + |> validate_length(:description, max: 10_000, count: :bytes) + |> foreign_key_constraint(:thumbnail_id, name: :fk_rails_792181eb40) + end + + @doc false + def creation_changeset(gallery, attrs, user) do + changeset(gallery, attrs) + |> change(creator: user) + |> cast_assoc(:interactions, with: &Interaction.changeset/2) end end diff --git a/lib/philomena/galleries/interaction.ex b/lib/philomena/galleries/interaction.ex index b50983ee..889f0c56 100644 --- a/lib/philomena/galleries/interaction.ex +++ b/lib/philomena/galleries/interaction.ex @@ -17,7 +17,15 @@ defmodule Philomena.Galleries.Interaction do @doc false def changeset(interaction, attrs) do interaction - |> cast(attrs, []) - |> validate_required([]) + |> cast(attrs, [:image_id, :position]) + |> validate_required([:image_id, :position]) + |> foreign_key_constraint(:image_id, name: :fk_rails_bb5ebe2a77) + |> case do + %{valid?: false, changes: changes} = changeset when changes == %{} -> + %{changeset | action: :ignore} + + changeset -> + changeset + end end end diff --git a/lib/philomena/users/ability.ex b/lib/philomena/users/ability.ex index b8b2f601..b7e4b389 100644 --- a/lib/philomena/users/ability.ex +++ b/lib/philomena/users/ability.ex @@ -8,6 +8,7 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do alias Philomena.Topics.Topic alias Philomena.Posts.Post alias Philomena.Filters.Filter + alias Philomena.Galleries.Gallery alias Philomena.DnpEntries.DnpEntry alias Philomena.UserLinks.UserLink @@ -108,6 +109,11 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do def can?(_user, :show, %DnpEntry{aasm_state: "listed"}), do: true def can?(_user, :show_reason, %DnpEntry{aasm_state: "listed", hide_reason: false}), do: true + # Create and edit galleries + def can?(_user, :show, %Gallery{}), 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 + # Otherwise... def can?(_user, _action, _model), do: false end diff --git a/lib/philomena_web/controllers/gallery_controller.ex b/lib/philomena_web/controllers/gallery_controller.ex index bdac0aa7..ad3a14f6 100644 --- a/lib/philomena_web/controllers/gallery_controller.ex +++ b/lib/philomena_web/controllers/gallery_controller.ex @@ -1,10 +1,15 @@ defmodule PhilomenaWeb.GalleryController do use PhilomenaWeb, :controller + alias PhilomenaWeb.ImageLoader + alias Philomena.ImageSorter + alias Philomena.Interactions alias Philomena.Galleries.Gallery + alias Philomena.Galleries import Ecto.Query - plug :load_resource, model: Gallery, only: [:show] + plug PhilomenaWeb.FilterBannedUsersPlug when action in [:new, :create, :edit, :update, :delete] + plug :load_and_authorize_resource, model: Gallery, except: [:index] def index(conn, params) do galleries = @@ -25,9 +30,74 @@ defmodule PhilomenaWeb.GalleryController do end def show(conn, _params) do - # stub + gallery = conn.assigns.gallery + query = "gallery_id:#{gallery.id}" + params = Map.put(conn.params, "q", query) + sort = ImageSorter.parse_sort(%{"sf" => "gallery_id:#{gallery.id}", "sd" => position_order(gallery)}) + + {:ok, images} = ImageLoader.search_string(conn, query, queries: sort.queries, sorts: sort.sorts) + interactions = Interactions.user_interactions(images, conn.assigns.current_user) + conn - |> redirect(to: Routes.search_path(conn, :index, q: "gallery_id:#{conn.assigns.gallery_id}")) + |> Map.put(:params, params) + |> render("show.html", gallery: gallery, images: images, interactions: interactions) + end + + def new(conn, _params) do + changeset = Galleries.change_gallery(%Gallery{}) + render(conn, "new.html", changeset: changeset) + end + + def create(conn, %{"gallery" => gallery_params}) do + user = conn.assigns.current_user + + case Galleries.create_gallery(user, gallery_params) do + {:ok, gallery} -> + Galleries.reindex_gallery(gallery) + + conn + |> put_flash(:info, "Gallery successfully created.") + |> redirect(to: Routes.gallery_path(conn, :show, gallery)) + + {:error, changeset} -> + conn + |> render("new.html", changeset: changeset) + end + end + + def edit(conn, _params) do + gallery = conn.assigns.gallery + changeset = Galleries.change_gallery(gallery) + + render(conn, "edit.html", gallery: gallery, changeset: changeset) + end + + def update(conn, %{"gallery" => gallery_params}) do + gallery = conn.assigns.gallery + + case Galleries.update_gallery(gallery, gallery_params) do + {:ok, gallery} -> + Galleries.reindex_gallery(gallery) + + conn + |> put_flash(:info, "Gallery successfully updated.") + |> redirect(to: Routes.gallery_path(conn, :show, gallery)) + + {:error, changeset} -> + conn + |> render("edit.html", gallery: gallery, changeset: changeset) + end + end + + def delete(conn, _params) do + gallery = conn.assigns.gallery + + {:ok, _gallery} = Galleries.delete_gallery(gallery) + Galleries.unindex_gallery(gallery) + + conn + |> put_flash(:info, "Gallery successfully destroyed.") + |> redirect(to: Routes.gallery_path(conn, :index)) end defp parse_search(%{"gallery" => gallery_params}) do @@ -69,4 +139,7 @@ defmodule PhilomenaWeb.GalleryController do defp parse_sort(_params) do %{created_at: :desc} end + + defp position_order(%{order_position_asc: true}), do: "asc" + defp position_order(_gallery), do: "desc" end \ No newline at end of file diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index 719c5085..1b09481b 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -125,6 +125,7 @@ defmodule PhilomenaWeb.Router do resources "/reports", ReportController, only: [:index] resources "/user_links", UserLinkController, only: [:index, :new, :create, :show] + resources "/galleries", GalleryController, only: [:new, :create, :edit, :update, :delete] end scope "/", PhilomenaWeb do diff --git a/lib/philomena_web/templates/gallery/_form.html.slime b/lib/philomena_web/templates/gallery/_form.html.slime new file mode 100644 index 00000000..814e5d9b --- /dev/null +++ b/lib/philomena_web/templates/gallery/_form.html.slime @@ -0,0 +1,24 @@ += form_for @changeset, @action, fn f -> + = if @changeset.action do + .alert.alert-danger + p Oops, something went wrong! Please check the errors below. + + .field + = label f, :title, "Choose a title displayed in gallery preview" + = text_input f, :title, class: "input input--wide", placeholder: "Title", required: true + .field + = label f, :spoiler_warning, "If you are going to have NSFW images in the gallery, leave a warning for users who may not want to see it" + = text_input f, :spoiler_warning, class: "input input--wide", placeholder: "Spoiler warning" + .field + = label :description, "Describe the gallery in a few words" + = textarea f, :description, class: "input input--wide", placeholder: "Description" + .field + = label f, :thumbnail_id, "Set an existing image as the gallery cover" + br + = number_input f, :thumbnail_id, class: "input", placeholder: "Image ID (e.g. 100)", min: 0 + .field + = label f, :order_position_asc, "Reverse gallery order (images will be inserted at the end of the gallery)" + = checkbox f, :order_position_asc, class: "checkbox" + .field + + = submit "Save Gallery", class: "button" diff --git a/lib/philomena_web/templates/gallery/new.html.slime b/lib/philomena_web/templates/gallery/new.html.slime new file mode 100644 index 00000000..9eb3dadc --- /dev/null +++ b/lib/philomena_web/templates/gallery/new.html.slime @@ -0,0 +1,2 @@ +h1 Create a Gallery += render PhilomenaWeb.GalleryView, "_form.html", conn: @conn, changeset: @changeset, action: Routes.gallery_path(@conn, :create) \ No newline at end of file