diff --git a/config/config.exs b/config/config.exs index 8208ef73..67e779da 100644 --- a/config/config.exs +++ b/config/config.exs @@ -13,7 +13,8 @@ config :philomena, password_pepper: "dn2e0EpZrvBLoxUM3gfQveBhjf0bG/6/bYhrOyq3L3hV9hdo/bimJ+irbDWsuXLP", otp_secret_key: "Wn7O/8DD+qxL0X4X7bvT90wOkVGcA90bIHww4twR03Ci//zq7PnMw8ypqyyT/b/C", image_url_root: "/img", - avatar_url_root: "/avatars" + avatar_url_root: "/avatars", + badge_url_root: "/media" config :philomena, :pow, user: Philomena.Users.User, diff --git a/config/prod.secret.exs b/config/prod.secret.exs index 44dae28b..c49da0ae 100644 --- a/config/prod.secret.exs +++ b/config/prod.secret.exs @@ -20,6 +20,7 @@ config :philomena, avatar_url_root: System.get_env("AVATAR_URL_ROOT"), otp_secret_key: System.get_env("OTP_SECRET_KEY"), image_url_root: System.get_env("IMAGE_URL_ROOT"), + badge_url_root: System.get_env("BADGE_URL_ROOT"), camo_host: System.get_env("CAMO_HOST"), camo_key: System.get_env("CAMO_KEY"), cdn_host: System.get_env("CDN_HOST") diff --git a/lib/philomena/users/ability.ex b/lib/philomena/users/ability.ex index ad234af3..76e5c17f 100644 --- a/lib/philomena/users/ability.ex +++ b/lib/philomena/users/ability.ex @@ -54,6 +54,9 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do def can?(_user, :show, %Forum{access_level: "normal"}), do: true def can?(_user, :show, %Topic{hidden_from_users: false}), do: true + # View profile pages + def can?(_user, :show, %User{}), do: true + # Otherwise... def can?(_user, _action, _model), do: false end diff --git a/lib/philomena/users/user.ex b/lib/philomena/users/user.ex index 05c04e9f..2227d0ec 100644 --- a/lib/philomena/users/user.ex +++ b/lib/philomena/users/user.ex @@ -11,7 +11,15 @@ defmodule Philomena.Users.User do import Ecto.Changeset + @derive {Phoenix.Param, key: :slug} + schema "users" do + has_many :links, Philomena.Users.Link + has_many :verified_links, Philomena.Users.Link, where: [aasm_state: "verified"] + has_many :public_links, Philomena.Users.Link, where: [public: true, aasm_state: "verified"] + has_many :galleries, Philomena.Galleries.Gallery + has_many :awards, Philomena.Badges.Award + belongs_to :current_filter, Philomena.Filters.Filter belongs_to :deleted_by_user, Philomena.Users.User diff --git a/lib/philomena_web/controllers/profile_controller.ex b/lib/philomena_web/controllers/profile_controller.ex new file mode 100644 index 00000000..eb8e6d8f --- /dev/null +++ b/lib/philomena_web/controllers/profile_controller.ex @@ -0,0 +1,54 @@ +defmodule PhilomenaWeb.ProfileController do + use PhilomenaWeb, :controller + + alias Philomena.{Images, Images.Image, Comments.Comment, Posts.Post, Users.User, Users.Link} + alias Philomena.Repo + import Ecto.Query + + plug :load_and_authorize_resource, model: User, only: :show, id_field: "slug", preload: [awards: :badge, public_links: :tag] + + def show(conn, _params) do + current_user = conn.assigns.current_user + filter = conn.assigns.compiled_filter + user = conn.assigns.user + + {:ok, upload_query} = Images.Query.compile(current_user, "uploader_id:#{user.id}") + {:ok, fave_query} = Images.Query.compile(current_user, "faved_by_id:#{user.id}") + + recent_uploads = + Image.search_records( + %{ + query: %{ + bool: %{ + must_not: filter, + must: upload_query + } + } + }, + %{page_number: 1, page_size: 6}, + Image |> preload([:tags]) + ) + + recent_faves = + Image.search_records( + %{ + query: %{ + bool: %{ + must_not: filter, + must: fave_query + } + } + }, + %{page_number: 1, page_size: 6}, + Image |> preload([:tags]) + ) + + render( + conn, + "show.html", + user: user, + recent_uploads: recent_uploads, + recent_faves: recent_faves + ) + end +end diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index 324d3f14..1eca70ef 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -39,6 +39,7 @@ defmodule PhilomenaWeb.Router do resources "/current", CurrentController, only: [:update], singular: true end resources "/filters", FilterController + resources "/profiles", ProfileController, only: [:show] get "/:id", ImageController, :show end diff --git a/lib/philomena_web/templates/activity/_comment_strip.html.slime b/lib/philomena_web/templates/activity/_comment_strip.html.slime index 01fadb14..a2a3c686 100644 --- a/lib/philomena_web/templates/activity/_comment_strip.html.slime +++ b/lib/philomena_web/templates/activity/_comment_strip.html.slime @@ -7,6 +7,6 @@ => @comment.image.id ' by span.hyphenate-breaks - = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @comment + = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @comment, conn: @conn br = pretty_time(@comment.created_at) diff --git a/lib/philomena_web/templates/activity/_topic_strip.html.slime b/lib/philomena_web/templates/activity/_topic_strip.html.slime index a2326c84..a9c311e6 100644 --- a/lib/philomena_web/templates/activity/_topic_strip.html.slime +++ b/lib/philomena_web/templates/activity/_topic_strip.html.slime @@ -3,7 +3,7 @@ i.fa.fa-thumbtack> = if @topic.last_post do span.hyphenate-breaks - = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @topic.last_post + = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @topic.last_post, conn: @conn ' replied to /=> link_to 'replied to', short_topic_post_path(topic.forum, topic, topic.last_post, anchor: "post_#{topic.last_post.id}") TODO => @topic.title diff --git a/lib/philomena_web/templates/activity/index.html.slime b/lib/philomena_web/templates/activity/index.html.slime index 326a2555..59f516a9 100644 --- a/lib/philomena_web/templates/activity/index.html.slime +++ b/lib/philomena_web/templates/activity/index.html.slime @@ -3,7 +3,7 @@ = if @featured_image do .center h4.remove-top-margin Featured Image - = render PhilomenaWeb.ImageView, "_image_box.html", image: @featured_image, size: :medium + = render PhilomenaWeb.ImageView, "_image_box.html", image: @featured_image, size: :medium, conn: @conn .block.block--fixed.block--fixed--sub.block--success.center.hide-mobile ' Enjoy the site? a href="/pages/donations" @@ -16,24 +16,24 @@ ' Trending Images .block__content.flex.flex--centered.flex--wrap.image-flex-grid = for image <- @top_scoring do - = render PhilomenaWeb.ImageView, "_image_box.html", image: image, size: :thumb_small + = render PhilomenaWeb.ImageView, "_image_box.html", image: image, size: :thumb_small, conn: @conn a.block__header--single-item.center href="/search?q=*&sf=score&sd=desc" ' All Time Top Scoring .block.hide-mobile a.block__header--single-item.center href="/channels" ' Streams = for channel <- @streams do - = render PhilomenaWeb.ActivityView, "_channel_strip.html", channel: channel + = render PhilomenaWeb.ActivityView, "_channel_strip.html", channel: channel, conn: @conn .block.hide-mobile a.block__header--single-item.center href="/forums" ' Forum Activity = for topic <- @topics do - = render PhilomenaWeb.ActivityView, "_topic_strip.html", topic: topic + = render PhilomenaWeb.ActivityView, "_topic_strip.html", topic: topic, conn: @conn .block.hide-mobile a.block__header--single-item.center href="/lists/recent_comments" ' Recent Comments = for comment <- @comments do - = render PhilomenaWeb.ActivityView, "_comment_strip.html", comment: comment + = render PhilomenaWeb.ActivityView, "_comment_strip.html", comment: comment, conn: @conn a.block__header--single-item.center href="/search?q=first_seen_at.gt:3 days ago&sf=comments&sd=desc" ' Most Commented-on Images diff --git a/lib/philomena_web/templates/comment/_comment.html.slime b/lib/philomena_web/templates/comment/_comment.html.slime index 2c9a0c66..75ac176a 100644 --- a/lib/philomena_web/templates/comment/_comment.html.slime +++ b/lib/philomena_web/templates/comment/_comment.html.slime @@ -1,9 +1,9 @@ article.block.communication id="comment_#{@comment.id}" .block__content.flex.flex--no-wrap .flex__fixed.spacing-right - = render PhilomenaWeb.UserAttributionView, "_anon_user_avatar.html", object: @comment + = render PhilomenaWeb.UserAttributionView, "_anon_user_avatar.html", object: @comment, conn: @conn .flex__grow.communication__body - span.communication__body__sender-name = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @comment + span.communication__body__sender-name = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @comment, conn: @conn .communication__body__text /- if comment.hidden_from_users / strong.comment_deleted @@ -23,7 +23,7 @@ article.block.communication id="comment_#{@comment.id}" .block__content.communication__options .flex.flex--wrap.flex--spaced-out - = render PhilomenaWeb.CommentView, "_comment_options.html", comment: @comment + = render PhilomenaWeb.CommentView, "_comment_options.html", comment: @comment, conn: @conn /- if can?(:hide, Comment) / .js-staff-action / - if !comment.hidden_from_users && !comment.destroyed_content diff --git a/lib/philomena_web/templates/filter/_filter.html.slime b/lib/philomena_web/templates/filter/_filter.html.slime index 13cf0f93..e807f8e6 100644 --- a/lib/philomena_web/templates/filter/_filter.html.slime +++ b/lib/philomena_web/templates/filter/_filter.html.slime @@ -5,7 +5,7 @@ = if @filter.user do p ' Maintained by - = render PhilomenaWeb.UserAttributionView, "_user.html", object: @filter + = render PhilomenaWeb.UserAttributionView, "_user.html", object: @filter, conn: @conn ' . .filter-options diff --git a/lib/philomena_web/templates/filter/show.html.slime b/lib/philomena_web/templates/filter/show.html.slime index 278ced72..4633010a 100644 --- a/lib/philomena_web/templates/filter/show.html.slime +++ b/lib/philomena_web/templates/filter/show.html.slime @@ -30,7 +30,7 @@ h1 = if @filter.user do p.filter-maintainer ' This filter is maintained by - = render PhilomenaWeb.UserAttributionView, "_user.html", object: @filter + = render PhilomenaWeb.UserAttributionView, "_user.html", object: @filter, conn: @conn ' . p.filter-description diff --git a/lib/philomena_web/templates/forum/index.html.slime b/lib/philomena_web/templates/forum/index.html.slime index 00e1dba7..10315e49 100644 --- a/lib/philomena_web/templates/forum/index.html.slime +++ b/lib/philomena_web/templates/forum/index.html.slime @@ -28,6 +28,6 @@ h1 Discussion Forums br => link("Go to post", to: Routes.forum_topic_path(@conn, :show, forum, forum.last_post.topic, post_id: forum.last_post.id) <> "#post_#{forum.last_post.id}") ' by - => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: forum.last_post + => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: forum.last_post, conn: @conn br => pretty_time(forum.last_post.created_at) diff --git a/lib/philomena_web/templates/forum/show.html.slime b/lib/philomena_web/templates/forum/show.html.slime index 391eb759..a5f86add 100644 --- a/lib/philomena_web/templates/forum/show.html.slime +++ b/lib/philomena_web/templates/forum/show.html.slime @@ -41,14 +41,14 @@ h1 = @forum.name ' Posted => pretty_time(topic.created_at) ' by - = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: topic + = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: topic, conn: @conn td.table--communication-list__stats.hide-mobile = topic.view_count td.table--communication-list__stats.hide-mobile = topic.post_count td.table--communication-list__last-post = if topic.last_post do => link("Go to post", to: Routes.forum_topic_path(@conn, :show, @forum, topic, post_id: topic.last_post) <> "#post_#{topic.last_post.id}") ' by - = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: topic.last_post + = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: topic.last_post, conn: @conn br => pretty_time(topic.last_post.created_at) .block__header.block__header--light diff --git a/lib/philomena_web/templates/image/_image_meta.html.slime b/lib/philomena_web/templates/image/_image_meta.html.slime index 70401bb9..e68230ff 100644 --- a/lib/philomena_web/templates/image/_image_meta.html.slime +++ b/lib/philomena_web/templates/image/_image_meta.html.slime @@ -51,7 +51,7 @@ ' Uploaded => pretty_time(@image.created_at) ' by - => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @image + => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @image, conn: @conn span.image-size |   = @image.image_width diff --git a/lib/philomena_web/templates/image/show.html.slime b/lib/philomena_web/templates/image/show.html.slime index 084dcaae..cb5d95ca 100644 --- a/lib/philomena_web/templates/image/show.html.slime +++ b/lib/philomena_web/templates/image/show.html.slime @@ -1,5 +1,5 @@ -= render PhilomenaWeb.ImageView, "_image_meta.html", image: @image -= render PhilomenaWeb.ImageView, "_image_page.html", image: @image += render PhilomenaWeb.ImageView, "_image_meta.html", image: @image, conn: @conn += render PhilomenaWeb.ImageView, "_image_page.html", image: @image, conn: @conn .layout--narrow .image-description @@ -10,7 +10,7 @@ == @description .js-tagsauce id="image_tags_and_source_#{@image.id}" .tagsauce - = render PhilomenaWeb.TagView, "_tag_list.html", tags: display_order(@image.tags) + = render PhilomenaWeb.TagView, "_tag_list.html", tags: display_order(@image.tags), conn: @conn .block .flex.flex--wrap#image-source = if !!@image.source_url and @image.source_url != "" do @@ -21,4 +21,4 @@ h4 Comments #comments data-current-url="" data-loaded="true" = for {comment, body} <- @comments do - = render PhilomenaWeb.CommentView, "_comment.html", comment: comment, body: body + = render PhilomenaWeb.CommentView, "_comment.html", comment: comment, body: body, conn: @conn diff --git a/lib/philomena_web/templates/post/_post.html.slime b/lib/philomena_web/templates/post/_post.html.slime index 85ab97d7..5f91b27e 100644 --- a/lib/philomena_web/templates/post/_post.html.slime +++ b/lib/philomena_web/templates/post/_post.html.slime @@ -1,9 +1,9 @@ article.block.communication id="post_#{@post.id}" .block__content.flex.flex--no-wrap .flex__fixed.spacing-right - = render PhilomenaWeb.UserAttributionView, "_anon_user_avatar.html", object: @post + = render PhilomenaWeb.UserAttributionView, "_anon_user_avatar.html", object: @post, conn: @conn .flex__grow.communication__body - span.communication__body__sender-name = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @post + span.communication__body__sender-name = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @post, conn: @conn .communication__body__text = if !@post.hidden_from_users do ==<> @body diff --git a/lib/philomena_web/templates/profile/_recent_images.html.slime b/lib/philomena_web/templates/profile/_recent_images.html.slime new file mode 100644 index 00000000..c1c99e70 --- /dev/null +++ b/lib/philomena_web/templates/profile/_recent_images.html.slime @@ -0,0 +1,9 @@ +.block + .block__header + span.block__header__title = @title + = if assigns[:view_all_path] do + = link("View all", to: assigns[:view_all_path]) + + .block__content.js-resizable-media-container + = for image <- @images do + = render PhilomenaWeb.ImageView, "_image_box.html", image: image, size: :thumb \ No newline at end of file diff --git a/lib/philomena_web/templates/profile/show.html.slime b/lib/philomena_web/templates/profile/show.html.slime new file mode 100644 index 00000000..619dcb37 --- /dev/null +++ b/lib/philomena_web/templates/profile/show.html.slime @@ -0,0 +1,55 @@ +.profile-top + .profile-top__avatar + = render PhilomenaWeb.UserAttributionView, "_user_avatar.html", object: %{user: @user}, class: "avatar--125px" + .profile-top__name-and-links + div + h1.profile-top__name-header + = @user.name + | 's profile + span + ' Member since + = pretty_time(@user.created_at) + + .profile-top__options + ul.profile-top__options__column + li = link("Send message", to: "#") + li = link("Our conversations", to: "#") + li = link("Report this user", to: "#") + + ul.profile-top__options__column + li = link("Uploads", to: Routes.search_path(@conn, :index, q: "uploader_id:#{@user.id}")) + li = link("Comments", to: "#") + li = link("Posts", to: "#") + + ul.profile-top__options__column + li = link("Favorites", to: Routes.search_path(@conn, :index, q: "faved_by_id:#{@user.id}")) + li = link("Tag changes", to: "#") + li = link("Source changes", to: "#") + +.column-layout + .column-layout__left + .block + .block__header + span.block__header__title User Links + = for link <- @user.public_links do + .block__content.alternating-color.break-word + .center + = if link.tag do + .tag_list = render PhilomenaWeb.TagView, "_tag.html", tag: link.tag + = link(link.uri, to: link.uri) + + .block + .block__header + span.block__header__title Badges + = for award <- award_order(@user.awards) do + .block__content.flex.flex--centered.flex--center-distributed.alternating-color.no-overflow title=award.label + .flex__grow.center + .badge = badge_image(award.badge, alt: award.label, size: "32") + br + = award.badge_name || badge.title + .flex__grow.center + = pretty_time(award.awarded_on) + + .column-layout__main + = render PhilomenaWeb.ProfileView, "_recent_images.html", title: "Recent Uploads", images: @recent_uploads, view_all_path: Routes.search_path(@conn, :index, q: "uploader_id:#{@user.id}") + = render PhilomenaWeb.ProfileView, "_recent_images.html", title: "Recent Favorites", images: @recent_faves, view_all_path: Routes.search_path(@conn, :index, q: "faved_by_id:#{@user.id}") \ No newline at end of file diff --git a/lib/philomena_web/templates/topic/show.html.slime b/lib/philomena_web/templates/topic/show.html.slime index 9158fb9e..f2349d4c 100644 --- a/lib/philomena_web/templates/topic/show.html.slime +++ b/lib/philomena_web/templates/topic/show.html.slime @@ -17,7 +17,7 @@ h1 = @topic.title = pagination .flex--fixed.block__header__item ' Started by - => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @topic + => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @topic, conn: @conn .flex--fixed.block__header__item ' Posted =< pretty_time(@topic.created_at) diff --git a/lib/philomena_web/templates/user_attribution/_anon_user.html.slime b/lib/philomena_web/templates/user_attribution/_anon_user.html.slime index 56521a19..69b0e6da 100644 --- a/lib/philomena_web/templates/user_attribution/_anon_user.html.slime +++ b/lib/philomena_web/templates/user_attribution/_anon_user.html.slime @@ -1,5 +1,6 @@ = if !!@object.user and !@object.anonymous do - strong<> = @object.user.name + strong<> + = link(@object.user.name, to: Routes.profile_path(@conn, :show, @object.user)) - else strong<> | Background Pony # diff --git a/lib/philomena_web/templates/user_attribution/_anon_user_avatar.html.slime b/lib/philomena_web/templates/user_attribution/_anon_user_avatar.html.slime index b28207b4..7ba3d1c5 100644 --- a/lib/philomena_web/templates/user_attribution/_anon_user_avatar.html.slime +++ b/lib/philomena_web/templates/user_attribution/_anon_user_avatar.html.slime @@ -1,4 +1,4 @@ = if !!@object.user and !@object.anonymous do - = user_avatar(@object) + = user_avatar(@object, assigns[:class] || "avatar--100px") - else - = anonymous_avatar(@object) \ No newline at end of file + = anonymous_avatar(@object, assigns[:class] || "avatar--100px") \ No newline at end of file diff --git a/lib/philomena_web/templates/user_attribution/_user.html.slime b/lib/philomena_web/templates/user_attribution/_user.html.slime index 875af5fe..7ae0802e 100644 --- a/lib/philomena_web/templates/user_attribution/_user.html.slime +++ b/lib/philomena_web/templates/user_attribution/_user.html.slime @@ -1,2 +1,3 @@ = if !!@object.user do - strong<>= @object.user.name \ No newline at end of file + strong<> + = link(@object.user.name, to: Routes.profile_path(@conn, :show, @object.user)) \ No newline at end of file diff --git a/lib/philomena_web/templates/user_attribution/_user_avatar.html.slime b/lib/philomena_web/templates/user_attribution/_user_avatar.html.slime new file mode 100644 index 00000000..1e58fdf4 --- /dev/null +++ b/lib/philomena_web/templates/user_attribution/_user_avatar.html.slime @@ -0,0 +1,4 @@ += if !!@object.user do + = user_avatar(@object, assigns[:class] || "avatar--100px") +- else + = anonymous_avatar(assigns[:class] || "avatar--100px") \ No newline at end of file diff --git a/lib/philomena_web/views/profile_view.ex b/lib/philomena_web/views/profile_view.ex new file mode 100644 index 00000000..7d37514d --- /dev/null +++ b/lib/philomena_web/views/profile_view.ex @@ -0,0 +1,16 @@ +defmodule PhilomenaWeb.ProfileView do + use PhilomenaWeb, :view + + def award_order(awards) do + awards + |> Enum.sort_by(&{!&1.badge.priority, &1.awarded_on}) + end + + def badge_image(badge, options \\ []) do + img_tag(badge_url_root() <> "/" <> badge.image, options) + end + + defp badge_url_root do + Application.get_env(:philomena, :badge_url_root) + end +end \ No newline at end of file