From 34e63d5a464585d654facc01a2468a973cbc934e Mon Sep 17 00:00:00 2001 From: "byte[]" Date: Sun, 18 Aug 2019 14:14:36 -0400 Subject: [PATCH] image pages, tags --- lib/philomena/comments.ex | 104 ++++++++++++++++++ lib/philomena/comments/comment.ex | 16 +++ lib/philomena/images.ex | 6 +- lib/philomena/images/image.ex | 21 +--- lib/philomena/tags.ex | 104 ++++++++++++++++++ lib/philomena/tags/tag.ex | 22 ++++ .../controllers/comment_controller.ex | 62 +++++++++++ .../controllers/tag_controller.ex | 62 +++++++++++ lib/philomena_web/router.ex | 3 + .../templates/comment/edit.html.eex | 5 + .../templates/comment/form.html.eex | 11 ++ .../templates/comment/index.html.eex | 24 ++++ .../templates/comment/new.html.eex | 5 + .../templates/comment/show.html.eex | 8 ++ .../templates/image/_image_box.html.slime | 2 +- .../image/_image_container.html.slime | 2 +- .../templates/image/_image_meta.html.slime | 67 +++++++++++ .../templates/image/_image_page.html.slime | 13 +++ .../templates/image/show.html.eex | 8 -- .../templates/image/show.html.slime | 22 ++++ .../templates/layout/app.html.slime | 2 +- .../templates/tag/_tag.html.slime | 28 +++++ .../templates/tag/_tag_list.html.slime | 2 + lib/philomena_web/templates/tag/edit.html.eex | 5 + lib/philomena_web/templates/tag/form.html.eex | 11 ++ .../templates/tag/index.html.eex | 24 ++++ lib/philomena_web/templates/tag/new.html.eex | 5 + lib/philomena_web/templates/tag/show.html.eex | 8 ++ lib/philomena_web/views/app_view.ex | 45 ++++++++ lib/philomena_web/views/comment_view.ex | 3 + lib/philomena_web/views/image_view.ex | 31 +++++- lib/philomena_web/views/tag_view.ex | 3 + test/philomena/comments_test.exs | 62 +++++++++++ test/philomena/tags_test.exs | 62 +++++++++++ .../controllers/comment_controller_test.exs | 88 +++++++++++++++ .../controllers/tag_controller_test.exs | 88 +++++++++++++++ 36 files changed, 998 insertions(+), 36 deletions(-) create mode 100644 lib/philomena/comments.ex create mode 100644 lib/philomena/comments/comment.ex create mode 100644 lib/philomena/tags.ex create mode 100644 lib/philomena/tags/tag.ex create mode 100644 lib/philomena_web/controllers/comment_controller.ex create mode 100644 lib/philomena_web/controllers/tag_controller.ex create mode 100644 lib/philomena_web/templates/comment/edit.html.eex create mode 100644 lib/philomena_web/templates/comment/form.html.eex create mode 100644 lib/philomena_web/templates/comment/index.html.eex create mode 100644 lib/philomena_web/templates/comment/new.html.eex create mode 100644 lib/philomena_web/templates/comment/show.html.eex create mode 100644 lib/philomena_web/templates/image/_image_meta.html.slime create mode 100644 lib/philomena_web/templates/image/_image_page.html.slime delete mode 100644 lib/philomena_web/templates/image/show.html.eex create mode 100644 lib/philomena_web/templates/image/show.html.slime create mode 100644 lib/philomena_web/templates/tag/_tag.html.slime create mode 100644 lib/philomena_web/templates/tag/_tag_list.html.slime create mode 100644 lib/philomena_web/templates/tag/edit.html.eex create mode 100644 lib/philomena_web/templates/tag/form.html.eex create mode 100644 lib/philomena_web/templates/tag/index.html.eex create mode 100644 lib/philomena_web/templates/tag/new.html.eex create mode 100644 lib/philomena_web/templates/tag/show.html.eex create mode 100644 lib/philomena_web/views/app_view.ex create mode 100644 lib/philomena_web/views/comment_view.ex create mode 100644 lib/philomena_web/views/tag_view.ex create mode 100644 test/philomena/comments_test.exs create mode 100644 test/philomena/tags_test.exs create mode 100644 test/philomena_web/controllers/comment_controller_test.exs create mode 100644 test/philomena_web/controllers/tag_controller_test.exs diff --git a/lib/philomena/comments.ex b/lib/philomena/comments.ex new file mode 100644 index 00000000..45e0afcb --- /dev/null +++ b/lib/philomena/comments.ex @@ -0,0 +1,104 @@ +defmodule Philomena.Comments do + @moduledoc """ + The Comments context. + """ + + import Ecto.Query, warn: false + alias Philomena.Repo + + alias Philomena.Comments.Comment + + @doc """ + Returns the list of comments. + + ## Examples + + iex> list_comments() + [%Comment{}, ...] + + """ + def list_comments do + Repo.all(Comment) + end + + @doc """ + Gets a single comment. + + Raises `Ecto.NoResultsError` if the Comment does not exist. + + ## Examples + + iex> get_comment!(123) + %Comment{} + + iex> get_comment!(456) + ** (Ecto.NoResultsError) + + """ + def get_comment!(id), do: Repo.get!(Comment, id) + + @doc """ + Creates a comment. + + ## Examples + + iex> create_comment(%{field: value}) + {:ok, %Comment{}} + + iex> create_comment(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_comment(attrs \\ %{}) do + %Comment{} + |> Comment.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a comment. + + ## Examples + + iex> update_comment(comment, %{field: new_value}) + {:ok, %Comment{}} + + iex> update_comment(comment, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_comment(%Comment{} = comment, attrs) do + comment + |> Comment.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a Comment. + + ## Examples + + iex> delete_comment(comment) + {:ok, %Comment{}} + + iex> delete_comment(comment) + {:error, %Ecto.Changeset{}} + + """ + def delete_comment(%Comment{} = comment) do + Repo.delete(comment) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking comment changes. + + ## Examples + + iex> change_comment(comment) + %Ecto.Changeset{source: %Comment{}} + + """ + def change_comment(%Comment{} = comment) do + Comment.changeset(comment, %{}) + end +end diff --git a/lib/philomena/comments/comment.ex b/lib/philomena/comments/comment.ex new file mode 100644 index 00000000..7f07cd51 --- /dev/null +++ b/lib/philomena/comments/comment.ex @@ -0,0 +1,16 @@ +defmodule Philomena.Comments.Comment do + use Ecto.Schema + import Ecto.Changeset + + schema "comments" do + + timestamps() + end + + @doc false + def changeset(comment, attrs) do + comment + |> cast(attrs, []) + |> validate_required([]) + end +end diff --git a/lib/philomena/images.ex b/lib/philomena/images.ex index 631fb49f..e4cc1f84 100644 --- a/lib/philomena/images.ex +++ b/lib/philomena/images.ex @@ -18,7 +18,7 @@ defmodule Philomena.Images do """ def list_images do - Repo.all(Image |> where(hidden_from_users: false) |> limit(25)) + Repo.all(Image |> where(hidden_from_users: false) |> order_by(desc: :created_at) |> limit(25)) end @doc """ @@ -35,7 +35,9 @@ defmodule Philomena.Images do ** (Ecto.NoResultsError) """ - def get_image!(id), do: Repo.get!(Image, id) + def get_image!(id) do + Repo.one!(Image |> where(id: ^id) |> preload(:tags)) + end @doc """ Creates a image. diff --git a/lib/philomena/images/image.ex b/lib/philomena/images/image.ex index d725fbfd..5ffbf7a4 100644 --- a/lib/philomena/images/image.ex +++ b/lib/philomena/images/image.ex @@ -5,6 +5,7 @@ defmodule Philomena.Images.Image do schema "images" do belongs_to :user, Philomena.Users.User belongs_to :deleter, Philomena.Users.User, source: :deleted_by_id + many_to_many :tags, Philomena.Tags.Tag, join_through: "image_taggings" field :image, :string field :image_name, :string @@ -59,24 +60,4 @@ defmodule Philomena.Images.Image do |> 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/tags.ex b/lib/philomena/tags.ex new file mode 100644 index 00000000..846866c3 --- /dev/null +++ b/lib/philomena/tags.ex @@ -0,0 +1,104 @@ +defmodule Philomena.Tags do + @moduledoc """ + The Tags context. + """ + + import Ecto.Query, warn: false + alias Philomena.Repo + + alias Philomena.Tags.Tag + + @doc """ + Returns the list of tags. + + ## Examples + + iex> list_tags() + [%Tag{}, ...] + + """ + def list_tags do + Repo.all(Tag) + end + + @doc """ + Gets a single tag. + + Raises `Ecto.NoResultsError` if the Tag does not exist. + + ## Examples + + iex> get_tag!(123) + %Tag{} + + iex> get_tag!(456) + ** (Ecto.NoResultsError) + + """ + def get_tag!(id), do: Repo.get!(Tag, id) + + @doc """ + Creates a tag. + + ## Examples + + iex> create_tag(%{field: value}) + {:ok, %Tag{}} + + iex> create_tag(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_tag(attrs \\ %{}) do + %Tag{} + |> Tag.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a tag. + + ## Examples + + iex> update_tag(tag, %{field: new_value}) + {:ok, %Tag{}} + + iex> update_tag(tag, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_tag(%Tag{} = tag, attrs) do + tag + |> Tag.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a Tag. + + ## Examples + + iex> delete_tag(tag) + {:ok, %Tag{}} + + iex> delete_tag(tag) + {:error, %Ecto.Changeset{}} + + """ + def delete_tag(%Tag{} = tag) do + Repo.delete(tag) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking tag changes. + + ## Examples + + iex> change_tag(tag) + %Ecto.Changeset{source: %Tag{}} + + """ + def change_tag(%Tag{} = tag) do + Tag.changeset(tag, %{}) + end +end diff --git a/lib/philomena/tags/tag.ex b/lib/philomena/tags/tag.ex new file mode 100644 index 00000000..43ab3fac --- /dev/null +++ b/lib/philomena/tags/tag.ex @@ -0,0 +1,22 @@ +defmodule Philomena.Tags.Tag do + use Ecto.Schema + import Ecto.Changeset + + schema "tags" do + field :slug, :string + field :name, :string + field :category, :string + field :images_count, :integer + field :description, :string + field :short_description, :string + + timestamps(inserted_at: :created_at) + end + + @doc false + def changeset(tag, attrs) do + tag + |> cast(attrs, []) + |> validate_required([]) + end +end diff --git a/lib/philomena_web/controllers/comment_controller.ex b/lib/philomena_web/controllers/comment_controller.ex new file mode 100644 index 00000000..c681055a --- /dev/null +++ b/lib/philomena_web/controllers/comment_controller.ex @@ -0,0 +1,62 @@ +defmodule PhilomenaWeb.CommentController do + use PhilomenaWeb, :controller + + alias Philomena.Comments + alias Philomena.Comments.Comment + + def index(conn, _params) do + comments = Comments.list_comments() + render(conn, "index.html", comments: comments) + end + + def new(conn, _params) do + changeset = Comments.change_comment(%Comment{}) + render(conn, "new.html", changeset: changeset) + end + + def create(conn, %{"comment" => comment_params}) do + case Comments.create_comment(comment_params) do + {:ok, comment} -> + conn + |> put_flash(:info, "Comment created successfully.") + |> redirect(to: Routes.comment_path(conn, :show, comment)) + + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "new.html", changeset: changeset) + end + end + + def show(conn, %{"id" => id}) do + comment = Comments.get_comment!(id) + render(conn, "show.html", comment: comment) + end + + def edit(conn, %{"id" => id}) do + comment = Comments.get_comment!(id) + changeset = Comments.change_comment(comment) + render(conn, "edit.html", comment: comment, changeset: changeset) + end + + def update(conn, %{"id" => id, "comment" => comment_params}) do + comment = Comments.get_comment!(id) + + case Comments.update_comment(comment, comment_params) do + {:ok, comment} -> + conn + |> put_flash(:info, "Comment updated successfully.") + |> redirect(to: Routes.comment_path(conn, :show, comment)) + + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "edit.html", comment: comment, changeset: changeset) + end + end + + def delete(conn, %{"id" => id}) do + comment = Comments.get_comment!(id) + {:ok, _comment} = Comments.delete_comment(comment) + + conn + |> put_flash(:info, "Comment deleted successfully.") + |> redirect(to: Routes.comment_path(conn, :index)) + end +end diff --git a/lib/philomena_web/controllers/tag_controller.ex b/lib/philomena_web/controllers/tag_controller.ex new file mode 100644 index 00000000..350c820e --- /dev/null +++ b/lib/philomena_web/controllers/tag_controller.ex @@ -0,0 +1,62 @@ +defmodule PhilomenaWeb.TagController do + use PhilomenaWeb, :controller + + alias Philomena.Tags + alias Philomena.Tags.Tag + + def index(conn, _params) do + tags = Tags.list_tags() + render(conn, "index.html", tags: tags) + end + + def new(conn, _params) do + changeset = Tags.change_tag(%Tag{}) + render(conn, "new.html", changeset: changeset) + end + + def create(conn, %{"tag" => tag_params}) do + case Tags.create_tag(tag_params) do + {:ok, tag} -> + conn + |> put_flash(:info, "Tag created successfully.") + |> redirect(to: Routes.tag_path(conn, :show, tag)) + + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "new.html", changeset: changeset) + end + end + + def show(conn, %{"id" => id}) do + tag = Tags.get_tag!(id) + render(conn, "show.html", tag: tag) + end + + def edit(conn, %{"id" => id}) do + tag = Tags.get_tag!(id) + changeset = Tags.change_tag(tag) + render(conn, "edit.html", tag: tag, changeset: changeset) + end + + def update(conn, %{"id" => id, "tag" => tag_params}) do + tag = Tags.get_tag!(id) + + case Tags.update_tag(tag, tag_params) do + {:ok, tag} -> + conn + |> put_flash(:info, "Tag updated successfully.") + |> redirect(to: Routes.tag_path(conn, :show, tag)) + + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "edit.html", tag: tag, changeset: changeset) + end + end + + def delete(conn, %{"id" => id}) do + tag = Tags.get_tag!(id) + {:ok, _tag} = Tags.delete_tag(tag) + + conn + |> put_flash(:info, "Tag deleted successfully.") + |> redirect(to: Routes.tag_path(conn, :index)) + end +end diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index d4a953f2..831f04e4 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -26,6 +26,9 @@ defmodule PhilomenaWeb.Router do get "/", PageController, :index resources "/images", ImageController, only: [:index, :show] + resources "/tags", TagController, only: [:index, :show] + + get "/:id", ImageController, :show end # Other scopes may use custom stacks. diff --git a/lib/philomena_web/templates/comment/edit.html.eex b/lib/philomena_web/templates/comment/edit.html.eex new file mode 100644 index 00000000..cd80fb3a --- /dev/null +++ b/lib/philomena_web/templates/comment/edit.html.eex @@ -0,0 +1,5 @@ +

Edit Comment

+ +<%= render "form.html", Map.put(assigns, :action, Routes.comment_path(@conn, :update, @comment)) %> + +<%= link "Back", to: Routes.comment_path(@conn, :index) %> diff --git a/lib/philomena_web/templates/comment/form.html.eex b/lib/philomena_web/templates/comment/form.html.eex new file mode 100644 index 00000000..a8b33630 --- /dev/null +++ b/lib/philomena_web/templates/comment/form.html.eex @@ -0,0 +1,11 @@ +<%= form_for @changeset, @action, fn f -> %> + <%= if @changeset.action do %> +
+

Oops, something went wrong! Please check the errors below.

+
+ <% end %> + +
+ <%= submit "Save" %> +
+<% end %> diff --git a/lib/philomena_web/templates/comment/index.html.eex b/lib/philomena_web/templates/comment/index.html.eex new file mode 100644 index 00000000..bada0732 --- /dev/null +++ b/lib/philomena_web/templates/comment/index.html.eex @@ -0,0 +1,24 @@ +

Listing Comments

+ + + + + + + + + +<%= for comment <- @comments do %> + + + + +<% end %> + +
+ <%= link "Show", to: Routes.comment_path(@conn, :show, comment) %> + <%= link "Edit", to: Routes.comment_path(@conn, :edit, comment) %> + <%= link "Delete", to: Routes.comment_path(@conn, :delete, comment), method: :delete, data: [confirm: "Are you sure?"] %> +
+ +<%= link "New Comment", to: Routes.comment_path(@conn, :new) %> diff --git a/lib/philomena_web/templates/comment/new.html.eex b/lib/philomena_web/templates/comment/new.html.eex new file mode 100644 index 00000000..931e4cf3 --- /dev/null +++ b/lib/philomena_web/templates/comment/new.html.eex @@ -0,0 +1,5 @@ +

New Comment

+ +<%= render "form.html", Map.put(assigns, :action, Routes.comment_path(@conn, :create)) %> + +<%= link "Back", to: Routes.comment_path(@conn, :index) %> diff --git a/lib/philomena_web/templates/comment/show.html.eex b/lib/philomena_web/templates/comment/show.html.eex new file mode 100644 index 00000000..4dbf4995 --- /dev/null +++ b/lib/philomena_web/templates/comment/show.html.eex @@ -0,0 +1,8 @@ +

Show Comment

+ + + +<%= link "Edit", to: Routes.comment_path(@conn, :edit, @comment) %> +<%= link "Back", to: Routes.comment_path(@conn, :index) %> diff --git a/lib/philomena_web/templates/image/_image_box.html.slime b/lib/philomena_web/templates/image/_image_box.html.slime index f07af525..690034d7 100644 --- a/lib/philomena_web/templates/image/_image_box.html.slime +++ b/lib/philomena_web/templates/image/_image_box.html.slime @@ -15,4 +15,4 @@ 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 + = render PhilomenaWeb.ImageView, "_image_container.html", image: @image, size: :thumb \ No newline at end of file diff --git a/lib/philomena_web/templates/image/_image_container.html.slime b/lib/philomena_web/templates/image/_image_container.html.slime index cec90171..ac6fc929 100644 --- a/lib/philomena_web/templates/image/_image_container.html.slime +++ b/lib/philomena_web/templates/image/_image_container.html.slime @@ -15,6 +15,6 @@ .media-box__overlay.js-spoiler-info-overlay a href="/#{@image.id}" = if @image.thumbnails_generated do - picture: img src=thumb_url(@image, false, :thumb) + picture: img src=thumb_url(@image, false, @size) - else | Thumbnails not yet generated diff --git a/lib/philomena_web/templates/image/_image_meta.html.slime b/lib/philomena_web/templates/image/_image_meta.html.slime new file mode 100644 index 00000000..37988da4 --- /dev/null +++ b/lib/philomena_web/templates/image/_image_meta.html.slime @@ -0,0 +1,67 @@ +.block.block__header + .flex.flex--wrap.image-metabar.center--layout id="image_meta_#{@image.id}" + .stretched-mobile-links + a.js-prev href="/" title="Previous Image (j)" + i.fa.fa-chevron-left + a.js-up href="/" title="Find this image in the global image list (i)" + i.fa.fa-chevron-up + a.js-next href="/" title="Next Image (k)" + i.fa.fa-chevron-right + a.js-rand href="/" title="Random (r)" + i.fa.fa-random + .stretched-mobile-links + a.interaction--fave href="#" rel="nofollow" data-image-id=@image.id + span.favorites> title="Favorites" data-image-id=@image.id = @image.faves_count + span.fave-span title="Fave!" + i.fa.fa-star + a.interaction--upvote href="#" rel="nofollow" data-image-id=@image.id + span.upvotes> title="Upvotes" data-image-id=@image.id = @image.upvotes_count + span.upvote-span title="Yay!" + i.fa.fa-arrow-up + span.score.block__header__title data-image-id=@image.id = @image.score + a.interaction--downvote href="#" rel="nofollow" data-image-id=@image.id + span.downvote-span title="Neigh!" + i.fa.fa-arrow-down + span.downvotes< title="Downvotes" data-image-id=@image.id = @image.downvotes_count + a.interaction--comments href="#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 + span.hide-span title="Hide" + i.fa.fa-eye-slash + .stretched-mobile-links + a href="/" title="Related Images" + i.fa.fa-sitemap> + span.hide-limited-desktop.hide-mobile Related + .stretched-mobile-links + a href="#{pretty_url(@image, false, false)}" rel="nofollow" title="View (tags in filename)" + i.fa.fa-eye> + | View + a href="#{pretty_url(@image, true, false)}" rel="nofollow" title="View (no tags in filename)" + i.fa.fa-eye> + | VS + a href="#{pretty_url(@image, false, true)}" rel="nofollow" title="Download (tags in filename)" + i.fa.fa-download> + | Download + a href="#{pretty_url(@image, true, true)}" title="Download (no tags in filename)" + i.fa.fa-download> + | DS + .image-metabar.flex.flex--wrap.block__header--user-credit.center--layout#extrameta + div + | Uploaded + =<> PhilomenaWeb.AppView.pretty_time(@image.created_at) + span.image-size + |   + = @image.image_width + | x + = @image.image_height + =<> String.upcase(@image.image_format) + - size_kb = div(@image.image_size, 1024) + - size_mb = Float.round(size_kb / 1024.0, 2) + span title="#{size_kb} kB" + = if size_kb <= 1024 do + => size_kb + | kB + - else + => size_mb + | MB diff --git a/lib/philomena_web/templates/image/_image_page.html.slime b/lib/philomena_web/templates/image/_image_page.html.slime new file mode 100644 index 00000000..1cac9e23 --- /dev/null +++ b/lib/philomena_web/templates/image/_image_page.html.slime @@ -0,0 +1,13 @@ +.center--layout--flex + = if @image.thumbnails_generated do + = render PhilomenaWeb.ImageView, "_image_container.html", image: @image, size: :full + - else + #thumbnails-not-yet-generated.block.block--fixed.block--warning.layout--narrow + h3 Just a moment + p The image should appear in a few minutes; report it otherwise. + p Implications might have added tags, so check them in the meanwhile. + + = if !@image.processed and @image.thumbnails_generated do + br + #image-being-optimized.block.block--fixed.block--warning.layout--narrow + | This image is being processed to optimize the filesize. It should finish shortly. \ No newline at end of file diff --git a/lib/philomena_web/templates/image/show.html.eex b/lib/philomena_web/templates/image/show.html.eex deleted file mode 100644 index 7f42c6f8..00000000 --- a/lib/philomena_web/templates/image/show.html.eex +++ /dev/null @@ -1,8 +0,0 @@ -

Show Image

- - - -<%= link "Edit", to: Routes.image_path(@conn, :edit, @image) %> -<%= link "Back", to: Routes.image_path(@conn, :index) %> diff --git a/lib/philomena_web/templates/image/show.html.slime b/lib/philomena_web/templates/image/show.html.slime new file mode 100644 index 00000000..9d9febe0 --- /dev/null +++ b/lib/philomena_web/templates/image/show.html.slime @@ -0,0 +1,22 @@ += render PhilomenaWeb.ImageView, "_image_meta.html", image: @image += render PhilomenaWeb.ImageView, "_image_page.html", image: @image + +.layout--narrow + .image-description + div + p + | Description: + .image-description__text + = @image.description + .js-tagsauce id="image_tags_and_source_#{@image.id}" + .tagsauce + .block + | Tags: + = render PhilomenaWeb.TagView, "_tag_list.html", tags: @image.tags + .block + .flex.flex--wrap#image-source + ' Source: + = if !!@image.source_url and @image.source_url != "" do + a href=@image.source_url = @image.source_url + - else + em> not provided yet \ No newline at end of file diff --git a/lib/philomena_web/templates/layout/app.html.slime b/lib/philomena_web/templates/layout/app.html.slime index 4e61231d..440a8998 100644 --- a/lib/philomena_web/templates/layout/app.html.slime +++ b/lib/philomena_web/templates/layout/app.html.slime @@ -23,6 +23,6 @@ html lang="en" #container = render PhilomenaWeb.LayoutView, "_header.html", assigns = render PhilomenaWeb.LayoutView, "_flash_warnings.html", assigns - main#content class="layout--narrow" + main#content class="layout--wide" = render @view_module, @view_template, assigns = render PhilomenaWeb.LayoutView, "_footer.html", assigns \ No newline at end of file diff --git a/lib/philomena_web/templates/tag/_tag.html.slime b/lib/philomena_web/templates/tag/_tag.html.slime new file mode 100644 index 00000000..217783c5 --- /dev/null +++ b/lib/philomena_web/templates/tag/_tag.html.slime @@ -0,0 +1,28 @@ +span.tag.dropdown data-tag-category="#{@tag.category}" data-tag-id="#{@tag.id}" data-tag-name="#{@tag.name}" data-tag-slug="#{@tag.slug}" + a.tag__name> href="/tags/#{@tag.slug}" title="#{@tag.short_description}" = @tag.name + span.tag__count + | ( + = @tag.images_count + | ) + / The order of tag states and dropdown links is important for tags.js + span + span.tag__state.hidden title="Unwatched" + | + + span.tag__state.hidden title="Watched" + | - + span.tag__state.hidden title="Spoilered" + | S + span.tag__state.hidden title="Hidden" + | H + span.dropdown__content + a.tag__dropdown__link.hidden data-method="delete" data-remote="true" data-tag-action="unwatch" href="/tags/#{@tag.slug}/watch" Unwatch + a.tag__dropdown__link.hidden data-method="post" data-remote="true" data-tag-action="watch" href="/tags/#{@tag.slug}/watch" Watch + + a.tag__dropdown__link.hidden data-method="delete" data-remote="true" data-tag-action="unspoiler" href="/filters/spoiler?tagname=#{@tag.slug}" Unspoiler + a.tag__dropdown__link.hidden data-method="post" data-remote="true" data-tag-action="spoiler" href="/filters/spoiler?tagname=#{@tag.slug}" Spoiler + + a.tag__dropdown__link.hidden data-method="delete" data-remote="true" data-tag-action="unhide" href="/filters/hide?tagname=#{@tag.slug}" Unhide + a.tag__dropdown__link.hidden data-method="post" data-remote="true" data-tag-action="hide" href="/filters/hide?tagname=#{@tag.slug}" Hide + + a.tag__dropdown__link.hidden href="/users/sign_in" Sign in to Watch + a.tag__dropdown__link.hidden href="/filters" Filter \ No newline at end of file diff --git a/lib/philomena_web/templates/tag/_tag_list.html.slime b/lib/philomena_web/templates/tag/_tag_list.html.slime new file mode 100644 index 00000000..0798b1c3 --- /dev/null +++ b/lib/philomena_web/templates/tag/_tag_list.html.slime @@ -0,0 +1,2 @@ += for tag <- @tags do + = render PhilomenaWeb.TagView, "_tag.html", tag: tag \ No newline at end of file diff --git a/lib/philomena_web/templates/tag/edit.html.eex b/lib/philomena_web/templates/tag/edit.html.eex new file mode 100644 index 00000000..f2f4987a --- /dev/null +++ b/lib/philomena_web/templates/tag/edit.html.eex @@ -0,0 +1,5 @@ +

Edit Tag

+ +<%= render "form.html", Map.put(assigns, :action, Routes.tag_path(@conn, :update, @tag)) %> + +<%= link "Back", to: Routes.tag_path(@conn, :index) %> diff --git a/lib/philomena_web/templates/tag/form.html.eex b/lib/philomena_web/templates/tag/form.html.eex new file mode 100644 index 00000000..a8b33630 --- /dev/null +++ b/lib/philomena_web/templates/tag/form.html.eex @@ -0,0 +1,11 @@ +<%= form_for @changeset, @action, fn f -> %> + <%= if @changeset.action do %> +
+

Oops, something went wrong! Please check the errors below.

+
+ <% end %> + +
+ <%= submit "Save" %> +
+<% end %> diff --git a/lib/philomena_web/templates/tag/index.html.eex b/lib/philomena_web/templates/tag/index.html.eex new file mode 100644 index 00000000..51086582 --- /dev/null +++ b/lib/philomena_web/templates/tag/index.html.eex @@ -0,0 +1,24 @@ +

Listing Tags

+ + + + + + + + + +<%= for tag <- @tags do %> + + + + +<% end %> + +
+ <%= link "Show", to: Routes.tag_path(@conn, :show, tag) %> + <%= link "Edit", to: Routes.tag_path(@conn, :edit, tag) %> + <%= link "Delete", to: Routes.tag_path(@conn, :delete, tag), method: :delete, data: [confirm: "Are you sure?"] %> +
+ +<%= link "New Tag", to: Routes.tag_path(@conn, :new) %> diff --git a/lib/philomena_web/templates/tag/new.html.eex b/lib/philomena_web/templates/tag/new.html.eex new file mode 100644 index 00000000..51af2aa9 --- /dev/null +++ b/lib/philomena_web/templates/tag/new.html.eex @@ -0,0 +1,5 @@ +

New Tag

+ +<%= render "form.html", Map.put(assigns, :action, Routes.tag_path(@conn, :create)) %> + +<%= link "Back", to: Routes.tag_path(@conn, :index) %> diff --git a/lib/philomena_web/templates/tag/show.html.eex b/lib/philomena_web/templates/tag/show.html.eex new file mode 100644 index 00000000..fe485512 --- /dev/null +++ b/lib/philomena_web/templates/tag/show.html.eex @@ -0,0 +1,8 @@ +

Show Tag

+ + + +<%= link "Edit", to: Routes.tag_path(@conn, :edit, @tag) %> +<%= link "Back", to: Routes.tag_path(@conn, :index) %> diff --git a/lib/philomena_web/views/app_view.ex b/lib/philomena_web/views/app_view.ex new file mode 100644 index 00000000..4feb3037 --- /dev/null +++ b/lib/philomena_web/views/app_view.ex @@ -0,0 +1,45 @@ +defmodule PhilomenaWeb.AppView do + use PhilomenaWeb, :view + + @time_strings %{ + seconds: "less than a minute", + minute: "about a minute", + minutes: "%d minutes", + hour: "about an hour", + hours: "about %d hours", + day: "a day", + days: "%d days", + month: "about a month", + months: "%d months", + year: "about a year", + years: "%d years" + } + + def pretty_time(time) do + seconds = NaiveDateTime.diff(NaiveDateTime.utc_now(), time, :second) + relation = if seconds < 0, do: "from now", else: "ago" + + seconds = abs(seconds) + minutes = abs(div(seconds, 60)) + hours = abs(div(minutes, 60)) + days = abs(div(hours, 24)) + months = abs(div(days, 30)) + years = abs(div(days, 365)) + + words = cond do + seconds < 45 -> String.replace(@time_strings[:seconds], "%d", to_string(seconds)) + seconds < 90 -> String.replace(@time_strings[:minute], "%d", to_string(1)) + minutes < 45 -> String.replace(@time_strings[:minutes], "%d", to_string(minutes)) + minutes < 90 -> String.replace(@time_strings[:hour], "%d", to_string(1)) + hours < 24 -> String.replace(@time_strings[:hours], "%d", to_string(hours)) + hours < 42 -> String.replace(@time_strings[:day], "%d", to_string(1)) + days < 30 -> String.replace(@time_strings[:days], "%d", to_string(days)) + days < 45 -> String.replace(@time_strings[:month], "%d", to_string(1)) + days < 365 -> String.replace(@time_strings[:months], "%d", to_string(months)) + days < 548 -> String.replace(@time_strings[:year], "%d", to_string(1)) + true -> String.replace(@time_strings[:years], "%d", to_string(years)) + end + + content_tag(:time, "#{words} #{relation}", datetime: time |> NaiveDateTime.to_iso8601()) + end +end \ No newline at end of file diff --git a/lib/philomena_web/views/comment_view.ex b/lib/philomena_web/views/comment_view.ex new file mode 100644 index 00000000..fbe88c11 --- /dev/null +++ b/lib/philomena_web/views/comment_view.ex @@ -0,0 +1,3 @@ +defmodule PhilomenaWeb.CommentView do + use PhilomenaWeb, :view +end diff --git a/lib/philomena_web/views/image_view.ex b/lib/philomena_web/views/image_view.ex index 6708d57d..2087e78e 100644 --- a/lib/philomena_web/views/image_view.ex +++ b/lib/philomena_web/views/image_view.ex @@ -1,9 +1,34 @@ defmodule PhilomenaWeb.ImageView do use PhilomenaWeb, :view - alias Philomena.Images.Image - def thumb_url(image, show_hidden, name) do - Image.thumb_url(image, show_hidden, name) + %{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 + + def pretty_url(image, _short, download) do + %{year: year, month: month, day: day} = image.created_at + root = image_url_root() + + view = if download, do: "download", else: "view" + filename = "#{image.id}" + format = image.image_format + + "#{root}/#{view}/#{year}/#{month}/#{day}/#{filename}.#{format}" + end + + def image_url_root do + Application.get_env(:philomena, :image_url_root) end end diff --git a/lib/philomena_web/views/tag_view.ex b/lib/philomena_web/views/tag_view.ex new file mode 100644 index 00000000..08d8f02d --- /dev/null +++ b/lib/philomena_web/views/tag_view.ex @@ -0,0 +1,3 @@ +defmodule PhilomenaWeb.TagView do + use PhilomenaWeb, :view +end diff --git a/test/philomena/comments_test.exs b/test/philomena/comments_test.exs new file mode 100644 index 00000000..6f0b2fa3 --- /dev/null +++ b/test/philomena/comments_test.exs @@ -0,0 +1,62 @@ +defmodule Philomena.CommentsTest do + use Philomena.DataCase + + alias Philomena.Comments + + describe "comments" do + alias Philomena.Comments.Comment + + @valid_attrs %{} + @update_attrs %{} + @invalid_attrs %{} + + def comment_fixture(attrs \\ %{}) do + {:ok, comment} = + attrs + |> Enum.into(@valid_attrs) + |> Comments.create_comment() + + comment + end + + test "list_comments/0 returns all comments" do + comment = comment_fixture() + assert Comments.list_comments() == [comment] + end + + test "get_comment!/1 returns the comment with given id" do + comment = comment_fixture() + assert Comments.get_comment!(comment.id) == comment + end + + test "create_comment/1 with valid data creates a comment" do + assert {:ok, %Comment{} = comment} = Comments.create_comment(@valid_attrs) + end + + test "create_comment/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Comments.create_comment(@invalid_attrs) + end + + test "update_comment/2 with valid data updates the comment" do + comment = comment_fixture() + assert {:ok, %Comment{} = comment} = Comments.update_comment(comment, @update_attrs) + end + + test "update_comment/2 with invalid data returns error changeset" do + comment = comment_fixture() + assert {:error, %Ecto.Changeset{}} = Comments.update_comment(comment, @invalid_attrs) + assert comment == Comments.get_comment!(comment.id) + end + + test "delete_comment/1 deletes the comment" do + comment = comment_fixture() + assert {:ok, %Comment{}} = Comments.delete_comment(comment) + assert_raise Ecto.NoResultsError, fn -> Comments.get_comment!(comment.id) end + end + + test "change_comment/1 returns a comment changeset" do + comment = comment_fixture() + assert %Ecto.Changeset{} = Comments.change_comment(comment) + end + end +end diff --git a/test/philomena/tags_test.exs b/test/philomena/tags_test.exs new file mode 100644 index 00000000..b5b3f8b3 --- /dev/null +++ b/test/philomena/tags_test.exs @@ -0,0 +1,62 @@ +defmodule Philomena.TagsTest do + use Philomena.DataCase + + alias Philomena.Tags + + describe "tags" do + alias Philomena.Tags.Tag + + @valid_attrs %{} + @update_attrs %{} + @invalid_attrs %{} + + def tag_fixture(attrs \\ %{}) do + {:ok, tag} = + attrs + |> Enum.into(@valid_attrs) + |> Tags.create_tag() + + tag + end + + test "list_tags/0 returns all tags" do + tag = tag_fixture() + assert Tags.list_tags() == [tag] + end + + test "get_tag!/1 returns the tag with given id" do + tag = tag_fixture() + assert Tags.get_tag!(tag.id) == tag + end + + test "create_tag/1 with valid data creates a tag" do + assert {:ok, %Tag{} = tag} = Tags.create_tag(@valid_attrs) + end + + test "create_tag/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Tags.create_tag(@invalid_attrs) + end + + test "update_tag/2 with valid data updates the tag" do + tag = tag_fixture() + assert {:ok, %Tag{} = tag} = Tags.update_tag(tag, @update_attrs) + end + + test "update_tag/2 with invalid data returns error changeset" do + tag = tag_fixture() + assert {:error, %Ecto.Changeset{}} = Tags.update_tag(tag, @invalid_attrs) + assert tag == Tags.get_tag!(tag.id) + end + + test "delete_tag/1 deletes the tag" do + tag = tag_fixture() + assert {:ok, %Tag{}} = Tags.delete_tag(tag) + assert_raise Ecto.NoResultsError, fn -> Tags.get_tag!(tag.id) end + end + + test "change_tag/1 returns a tag changeset" do + tag = tag_fixture() + assert %Ecto.Changeset{} = Tags.change_tag(tag) + end + end +end diff --git a/test/philomena_web/controllers/comment_controller_test.exs b/test/philomena_web/controllers/comment_controller_test.exs new file mode 100644 index 00000000..07b901c4 --- /dev/null +++ b/test/philomena_web/controllers/comment_controller_test.exs @@ -0,0 +1,88 @@ +defmodule PhilomenaWeb.CommentControllerTest do + use PhilomenaWeb.ConnCase + + alias Philomena.Comments + + @create_attrs %{} + @update_attrs %{} + @invalid_attrs %{} + + def fixture(:comment) do + {:ok, comment} = Comments.create_comment(@create_attrs) + comment + end + + describe "index" do + test "lists all comments", %{conn: conn} do + conn = get(conn, Routes.comment_path(conn, :index)) + assert html_response(conn, 200) =~ "Listing Comments" + end + end + + describe "new comment" do + test "renders form", %{conn: conn} do + conn = get(conn, Routes.comment_path(conn, :new)) + assert html_response(conn, 200) =~ "New Comment" + end + end + + describe "create comment" do + test "redirects to show when data is valid", %{conn: conn} do + conn = post(conn, Routes.comment_path(conn, :create), comment: @create_attrs) + + assert %{id: id} = redirected_params(conn) + assert redirected_to(conn) == Routes.comment_path(conn, :show, id) + + conn = get(conn, Routes.comment_path(conn, :show, id)) + assert html_response(conn, 200) =~ "Show Comment" + end + + test "renders errors when data is invalid", %{conn: conn} do + conn = post(conn, Routes.comment_path(conn, :create), comment: @invalid_attrs) + assert html_response(conn, 200) =~ "New Comment" + end + end + + describe "edit comment" do + setup [:create_comment] + + test "renders form for editing chosen comment", %{conn: conn, comment: comment} do + conn = get(conn, Routes.comment_path(conn, :edit, comment)) + assert html_response(conn, 200) =~ "Edit Comment" + end + end + + describe "update comment" do + setup [:create_comment] + + test "redirects when data is valid", %{conn: conn, comment: comment} do + conn = put(conn, Routes.comment_path(conn, :update, comment), comment: @update_attrs) + assert redirected_to(conn) == Routes.comment_path(conn, :show, comment) + + conn = get(conn, Routes.comment_path(conn, :show, comment)) + assert html_response(conn, 200) + end + + test "renders errors when data is invalid", %{conn: conn, comment: comment} do + conn = put(conn, Routes.comment_path(conn, :update, comment), comment: @invalid_attrs) + assert html_response(conn, 200) =~ "Edit Comment" + end + end + + describe "delete comment" do + setup [:create_comment] + + test "deletes chosen comment", %{conn: conn, comment: comment} do + conn = delete(conn, Routes.comment_path(conn, :delete, comment)) + assert redirected_to(conn) == Routes.comment_path(conn, :index) + assert_error_sent 404, fn -> + get(conn, Routes.comment_path(conn, :show, comment)) + end + end + end + + defp create_comment(_) do + comment = fixture(:comment) + {:ok, comment: comment} + end +end diff --git a/test/philomena_web/controllers/tag_controller_test.exs b/test/philomena_web/controllers/tag_controller_test.exs new file mode 100644 index 00000000..6b7f0670 --- /dev/null +++ b/test/philomena_web/controllers/tag_controller_test.exs @@ -0,0 +1,88 @@ +defmodule PhilomenaWeb.TagControllerTest do + use PhilomenaWeb.ConnCase + + alias Philomena.Tags + + @create_attrs %{} + @update_attrs %{} + @invalid_attrs %{} + + def fixture(:tag) do + {:ok, tag} = Tags.create_tag(@create_attrs) + tag + end + + describe "index" do + test "lists all tags", %{conn: conn} do + conn = get(conn, Routes.tag_path(conn, :index)) + assert html_response(conn, 200) =~ "Listing Tags" + end + end + + describe "new tag" do + test "renders form", %{conn: conn} do + conn = get(conn, Routes.tag_path(conn, :new)) + assert html_response(conn, 200) =~ "New Tag" + end + end + + describe "create tag" do + test "redirects to show when data is valid", %{conn: conn} do + conn = post(conn, Routes.tag_path(conn, :create), tag: @create_attrs) + + assert %{id: id} = redirected_params(conn) + assert redirected_to(conn) == Routes.tag_path(conn, :show, id) + + conn = get(conn, Routes.tag_path(conn, :show, id)) + assert html_response(conn, 200) =~ "Show Tag" + end + + test "renders errors when data is invalid", %{conn: conn} do + conn = post(conn, Routes.tag_path(conn, :create), tag: @invalid_attrs) + assert html_response(conn, 200) =~ "New Tag" + end + end + + describe "edit tag" do + setup [:create_tag] + + test "renders form for editing chosen tag", %{conn: conn, tag: tag} do + conn = get(conn, Routes.tag_path(conn, :edit, tag)) + assert html_response(conn, 200) =~ "Edit Tag" + end + end + + describe "update tag" do + setup [:create_tag] + + test "redirects when data is valid", %{conn: conn, tag: tag} do + conn = put(conn, Routes.tag_path(conn, :update, tag), tag: @update_attrs) + assert redirected_to(conn) == Routes.tag_path(conn, :show, tag) + + conn = get(conn, Routes.tag_path(conn, :show, tag)) + assert html_response(conn, 200) + end + + test "renders errors when data is invalid", %{conn: conn, tag: tag} do + conn = put(conn, Routes.tag_path(conn, :update, tag), tag: @invalid_attrs) + assert html_response(conn, 200) =~ "Edit Tag" + end + end + + describe "delete tag" do + setup [:create_tag] + + test "deletes chosen tag", %{conn: conn, tag: tag} do + conn = delete(conn, Routes.tag_path(conn, :delete, tag)) + assert redirected_to(conn) == Routes.tag_path(conn, :index) + assert_error_sent 404, fn -> + get(conn, Routes.tag_path(conn, :show, tag)) + end + end + end + + defp create_tag(_) do + tag = fixture(:tag) + {:ok, tag: tag} + end +end