diff --git a/config/config.exs b/config/config.exs index 8a431b66..d3a3bd7e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -8,10 +8,9 @@ use Mix.Config config :philomena, - ecto_repos: [Philomena.Repo] - -config :philomena, - password_pepper: "dn2e0EpZrvBLoxUM3gfQveBhjf0bG/6/bYhrOyq3L3hV9hdo/bimJ+irbDWsuXLP" + ecto_repos: [Philomena.Repo], + password_pepper: "dn2e0EpZrvBLoxUM3gfQveBhjf0bG/6/bYhrOyq3L3hV9hdo/bimJ+irbDWsuXLP", + image_url_root: "/img" config :philomena, :pow, user: Philomena.Users.User, diff --git a/config/prod.secret.exs b/config/prod.secret.exs index 76b24d67..0e250c91 100644 --- a/config/prod.secret.exs +++ b/config/prod.secret.exs @@ -15,12 +15,8 @@ config :bcrypt_elixir, log_rounds: String.to_integer(System.get_env("BCRYPT_ROUNDS") || "12") config :philomena, - password_pepper: - System.get_env("PASSWORD_PEPPER") || - raise(""" - environment variable PASSWORD_PEPPER is missing. - You can generate one by calling: mix phx.gen.secret - """) + password_pepper: System.get_env("PASSWORD_PEPPER"), + image_url_root: System.get_env("IMAGE_URL_ROOT") config :philomena, Philomena.Repo, # ssl: true, diff --git a/lib/philomena/images.ex b/lib/philomena/images.ex new file mode 100644 index 00000000..631fb49f --- /dev/null +++ b/lib/philomena/images.ex @@ -0,0 +1,104 @@ +defmodule Philomena.Images do + @moduledoc """ + The Images context. + """ + + import Ecto.Query, warn: false + alias Philomena.Repo + + alias Philomena.Images.Image + + @doc """ + Returns the list of images. + + ## Examples + + iex> list_images() + [%Image{}, ...] + + """ + def list_images do + Repo.all(Image |> where(hidden_from_users: false) |> limit(25)) + end + + @doc """ + Gets a single image. + + Raises `Ecto.NoResultsError` if the Image does not exist. + + ## Examples + + iex> get_image!(123) + %Image{} + + iex> get_image!(456) + ** (Ecto.NoResultsError) + + """ + def get_image!(id), do: Repo.get!(Image, id) + + @doc """ + Creates a image. + + ## Examples + + iex> create_image(%{field: value}) + {:ok, %Image{}} + + iex> create_image(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_image(attrs \\ %{}) do + %Image{} + |> Image.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a image. + + ## Examples + + iex> update_image(image, %{field: new_value}) + {:ok, %Image{}} + + iex> update_image(image, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_image(%Image{} = image, attrs) do + image + |> Image.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a Image. + + ## Examples + + iex> delete_image(image) + {:ok, %Image{}} + + iex> delete_image(image) + {:error, %Ecto.Changeset{}} + + """ + def delete_image(%Image{} = image) do + Repo.delete(image) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking image changes. + + ## Examples + + iex> change_image(image) + %Ecto.Changeset{source: %Image{}} + + """ + def change_image(%Image{} = image) do + Image.changeset(image, %{}) + end +end diff --git a/lib/philomena/images/image.ex b/lib/philomena/images/image.ex new file mode 100644 index 00000000..d725fbfd --- /dev/null +++ b/lib/philomena/images/image.ex @@ -0,0 +1,82 @@ +defmodule Philomena.Images.Image do + use Ecto.Schema + import Ecto.Changeset + + schema "images" do + belongs_to :user, Philomena.Users.User + belongs_to :deleter, Philomena.Users.User, source: :deleted_by_id + + field :image, :string + field :image_name, :string + field :image_width, :integer + field :image_height, :integer + field :image_size, :integer + field :image_format, :string + field :image_mime_type, :string + field :image_aspect_ratio, :float + field :ip, EctoNetwork.INET + field :fingerprint, :string + field :user_agent, :string, default: "" + field :referrer, :string, default: "" + field :anonymous, :boolean, default: false + field :score, :integer, default: 0 + field :faves_count, :integer, default: 0 + field :upvotes_count, :integer, default: 0 + field :downvotes_count, :integer, default: 0 + field :votes_count, :integer, default: 0 + field :source_url, :string + field :description, :string, default: "" + field :image_sha512_hash, :string + field :image_orig_sha512_hash, :string + field :deletion_reason, :string + field :duplicate_id, :integer + field :comments_count, :integer, default: 0 + field :processed, :boolean, default: false + field :thumbnails_generated, :boolean, default: false + field :duplication_checked, :boolean, default: false + field :hidden_from_users, :boolean, default: false + field :tag_editing_allowed, :boolean, default: true + field :description_editing_allowed, :boolean, default: true + field :commenting_allowed, :boolean, default: true + field :is_animated, :boolean + field :first_seen_at, :naive_datetime + field :destroyed_content, :boolean + field :hidden_image_key, :string + field :scratchpad, :string + field :hides_count, :integer, default: 0 + + # todo: can probably remove these now + # field :tag_list_cache, :string + # field :tag_list_plus_alias_cache, :string + # field :file_name_cache, :string + + timestamps(inserted_at: :created_at) + end + + @doc false + def changeset(image, attrs) do + image + |> cast(attrs, []) + |> validate_required([]) + end + + def thumb_url(image, show_hidden, name) do + %{year: year, month: month, day: day} = image.created_at + deleted = image.hidden_from_users + format = image.image_format + root = image_url_root() + + id_fragment = + if deleted and show_hidden do + "#{image.id}-#{image.hidden_image_Key}" + else + "#{image.id}" + end + + "#{root}/#{year}/#{month}/#{day}/#{id_fragment}/#{name}.#{format}" + end + + defp image_url_root do + Application.get_env(:philomena, :image_url_root) + end +end diff --git a/lib/philomena_web/controllers/image_controller.ex b/lib/philomena_web/controllers/image_controller.ex new file mode 100644 index 00000000..acba3904 --- /dev/null +++ b/lib/philomena_web/controllers/image_controller.ex @@ -0,0 +1,62 @@ +defmodule PhilomenaWeb.ImageController do + use PhilomenaWeb, :controller + + alias Philomena.Images + alias Philomena.Images.Image + + def index(conn, _params) do + images = Images.list_images() + render(conn, "index.html", images: images) + end + + def new(conn, _params) do + changeset = Images.change_image(%Image{}) + render(conn, "new.html", changeset: changeset) + end + + def create(conn, %{"image" => image_params}) do + case Images.create_image(image_params) do + {:ok, image} -> + conn + |> put_flash(:info, "Image created successfully.") + |> redirect(to: Routes.image_path(conn, :show, image)) + + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "new.html", changeset: changeset) + end + end + + def show(conn, %{"id" => id}) do + image = Images.get_image!(id) + render(conn, "show.html", image: image) + end + + def edit(conn, %{"id" => id}) do + image = Images.get_image!(id) + changeset = Images.change_image(image) + render(conn, "edit.html", image: image, changeset: changeset) + end + + def update(conn, %{"id" => id, "image" => image_params}) do + image = Images.get_image!(id) + + case Images.update_image(image, image_params) do + {:ok, image} -> + conn + |> put_flash(:info, "Image updated successfully.") + |> redirect(to: Routes.image_path(conn, :show, image)) + + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "edit.html", image: image, changeset: changeset) + end + end + + def delete(conn, %{"id" => id}) do + image = Images.get_image!(id) + {:ok, _image} = Images.delete_image(image) + + conn + |> put_flash(:info, "Image deleted successfully.") + |> redirect(to: Routes.image_path(conn, :index)) + end +end diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index 59a8ac87..d4a953f2 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -24,6 +24,8 @@ defmodule PhilomenaWeb.Router do pipe_through :browser get "/", PageController, :index + + resources "/images", ImageController, only: [:index, :show] end # Other scopes may use custom stacks. diff --git a/lib/philomena_web/templates/image/_image_box.html.slime b/lib/philomena_web/templates/image/_image_box.html.slime new file mode 100644 index 00000000..f07af525 --- /dev/null +++ b/lib/philomena_web/templates/image/_image_box.html.slime @@ -0,0 +1,18 @@ +.media-box data-image-id=@image.id + .media-box__header.media-box__header--link-row data-image-id=@image.id + a.interaction--fave href="#" rel="nofollow" data-image-id=@image.id + span.fave-span title='Fave!' + i.fa.fa-star + span.favorites title='Favorites' data-image-id=@image.id = @image.faves_count + a.interaction--upvote href="#" rel="nofollow" data-image-id=@image.id + i.fa.fa-arrow-up title='Yay!' + span.score title='Score' data-image-id=@image.id = @image.score + a.interaction--downvote href="#" rel="nofollow" data-image-id=@image.id + i.fa.fa-arrow-down title='Neigh!' + a.interaction--comments href="/#{@image.id}#comments" title='Comments' + i.fa.fa-comments + span.comments_count data-image-id=@image.id = @image.comments_count + a.interaction--hide href="#" rel="nofollow" data-image-id=@image.id + i.fa.fa-eye-slash title='Hide' + .media-box__content.media-box__content--large.flex.flex--centered.flex--center-distributed + = render PhilomenaWeb.ImageView, "_image_container.html", image: @image diff --git a/lib/philomena_web/templates/image/_image_container.html.slime b/lib/philomena_web/templates/image/_image_container.html.slime new file mode 100644 index 00000000..cec90171 --- /dev/null +++ b/lib/philomena_web/templates/image/_image_container.html.slime @@ -0,0 +1,20 @@ +.image-container.thumb + = cond do + - @image.duplicate_id -> + .media-box__overlay + strong Marked Duplicate + - @image.destroyed_content -> + .media-box__overlay + strong Destroyed Content + - @image.hidden_from_users -> + .media-box__overlay + strong Deleted: + =< @image.deletion_reason + - true -> + + .media-box__overlay.js-spoiler-info-overlay + a href="/#{@image.id}" + = if @image.thumbnails_generated do + picture: img src=thumb_url(@image, false, :thumb) + - else + | Thumbnails not yet generated diff --git a/lib/philomena_web/templates/image/edit.html.eex b/lib/philomena_web/templates/image/edit.html.eex new file mode 100644 index 00000000..595c1aac --- /dev/null +++ b/lib/philomena_web/templates/image/edit.html.eex @@ -0,0 +1,5 @@ +
Oops, something went wrong! Please check the errors below.
+