From 02f30391f690499091daeae3f0f21a599dd86f58 Mon Sep 17 00:00:00 2001 From: "byte[]" Date: Sun, 6 Oct 2019 17:31:48 -0400 Subject: [PATCH] add forums visibility --- lib/philomena/forums/forum.ex | 3 +- lib/philomena/topics/topic.ex | 2 + .../controllers/forum_controller.ex | 35 +++++++ .../controllers/topic_controller.ex | 22 +++++ lib/philomena_web/router.ex | 3 + .../templates/forum/index.html.slime | 33 +++++++ .../templates/forum/show.html.slime | 58 +++++++++++ .../templates/post/_post.html.slime | 36 +++++++ .../templates/post/_post_options.html.slime | 31 ++++++ .../templates/topic/show.html.slime | 95 +++++++++++++++++++ lib/philomena_web/views/forum_view.ex | 3 + lib/philomena_web/views/post_view.ex | 3 + lib/philomena_web/views/topic_view.ex | 3 + 13 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 lib/philomena_web/controllers/forum_controller.ex create mode 100644 lib/philomena_web/controllers/topic_controller.ex create mode 100644 lib/philomena_web/templates/forum/index.html.slime create mode 100644 lib/philomena_web/templates/forum/show.html.slime create mode 100644 lib/philomena_web/templates/post/_post.html.slime create mode 100644 lib/philomena_web/templates/post/_post_options.html.slime create mode 100644 lib/philomena_web/templates/topic/show.html.slime create mode 100644 lib/philomena_web/views/forum_view.ex create mode 100644 lib/philomena_web/views/post_view.ex create mode 100644 lib/philomena_web/views/topic_view.ex diff --git a/lib/philomena/forums/forum.ex b/lib/philomena/forums/forum.ex index 2c1d0cfd..35fb84f4 100644 --- a/lib/philomena/forums/forum.ex +++ b/lib/philomena/forums/forum.ex @@ -2,6 +2,7 @@ defmodule Philomena.Forums.Forum do use Ecto.Schema import Ecto.Changeset + @derive {Phoenix.Param, key: :short_name} schema "forums" do belongs_to :last_post, Philomena.Posts.Post belongs_to :last_topic, Philomena.Topics.Topic @@ -22,4 +23,4 @@ defmodule Philomena.Forums.Forum do |> cast(attrs, []) |> validate_required([]) end -end +end \ No newline at end of file diff --git a/lib/philomena/topics/topic.ex b/lib/philomena/topics/topic.ex index b82bba19..77dd3ddb 100644 --- a/lib/philomena/topics/topic.ex +++ b/lib/philomena/topics/topic.ex @@ -2,12 +2,14 @@ defmodule Philomena.Topics.Topic do use Ecto.Schema import Ecto.Changeset + @derive {Phoenix.Param, key: :slug} schema "topics" do belongs_to :user, Philomena.Users.User belongs_to :deleted_by, Philomena.Users.User belongs_to :locked_by, Philomena.Users.User belongs_to :last_post, Philomena.Posts.Post belongs_to :forum, Philomena.Forums.Forum + has_one :poll, Philomena.Polls.Poll field :title, :string field :post_count, :integer, default: 0 diff --git a/lib/philomena_web/controllers/forum_controller.ex b/lib/philomena_web/controllers/forum_controller.ex new file mode 100644 index 00000000..a11e60e9 --- /dev/null +++ b/lib/philomena_web/controllers/forum_controller.ex @@ -0,0 +1,35 @@ +defmodule PhilomenaWeb.ForumController do + use PhilomenaWeb, :controller + + alias Philomena.{Forums.Forum, Topics.Topic} + alias Philomena.Repo + import Ecto.Query + + plug :load_and_authorize_resource, model: Forum, id_field: "short_name" + + def index(conn, _params) do + user = conn.assigns.current_user + forums = + Forum + |> preload([last_post: [:topic, :user]]) + |> Repo.all() + |> Enum.filter(&Canada.Can.can?(user, :show, &1)) + + topic_count = Repo.aggregate(Forum, :sum, :topic_count) + + render(conn, "index.html", forums: forums, topic_count: topic_count) + end + + def show(conn, %{"id" => _id}) do + topics = + Topic + |> where(forum_id: ^conn.assigns.forum.id) + |> where(hidden_from_users: false) + |> order_by(desc: :sticky, desc: :last_replied_to_at) + |> preload([:poll, :forum, :user, last_post: :user]) + |> limit(25) + |> Repo.all() + + render(conn, "show.html", forum: conn.assigns.forum, topics: topics) + end +end diff --git a/lib/philomena_web/controllers/topic_controller.ex b/lib/philomena_web/controllers/topic_controller.ex new file mode 100644 index 00000000..6682f9b6 --- /dev/null +++ b/lib/philomena_web/controllers/topic_controller.ex @@ -0,0 +1,22 @@ +defmodule PhilomenaWeb.TopicController do + use PhilomenaWeb, :controller + + alias Philomena.{Forums.Forum, Topics.Topic, Posts.Post} + alias Philomena.Repo + import Ecto.Query + + plug :load_and_authorize_resource, model: Forum, id_name: "forum_id", id_field: "short_name", persisted: true + plug :load_and_authorize_resource, model: Topic, id_name: "id", id_field: "slug", preload: :user + + def show(conn, %{"id" => _id}) do + posts = + Post + |> where(topic_id: ^conn.assigns.topic.id) + |> order_by(asc: :created_at, asc: :id) + |> preload([:user, topic: :forum]) + |> limit(25) + |> Repo.all() + + render(conn, "show.html", posts: posts) + end +end diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index 3b5a1cf2..fe052f7d 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -29,6 +29,9 @@ defmodule PhilomenaWeb.Router do resources "/images", ImageController, only: [:index, :show] resources "/tags", TagController, only: [:index, :show] resources "/search", SearchController, only: [:index] + resources "/forums", ForumController, only: [:index, :show] do + resources "/topics", TopicController, only: [:show] + end get "/:id", ImageController, :show end diff --git a/lib/philomena_web/templates/forum/index.html.slime b/lib/philomena_web/templates/forum/index.html.slime new file mode 100644 index 00000000..fbc47f51 --- /dev/null +++ b/lib/philomena_web/templates/forum/index.html.slime @@ -0,0 +1,33 @@ +h1 Discussion Forums +.block + .block__header + /= icon_link 'Search Posts', 'fa fa-fw fa-search', posts_path + span.block__header__item + => @topic_count + ' topics + .block__content + table.table.table--communication-list + thead + tr + th.table--communication-list__name Forum + th.table--communication-list__stats.hide-mobile Topics + th.table--communication-list__stats.hide-mobile Posts + th.table--communication-list__last-post Last Post + tbody + = for forum <- @forums do + tr + td.table--communication-list__name + => link(forum.name, to: Routes.forum_path(@conn, :show, forum)) + .small-text = forum.description + td.table--communication-list__stats.hide-mobile = forum.topic_count + td.table--communication-list__stats.hide-mobile = forum.post_count + td.table--communication-list__last-post + = if forum.last_post do + strong + => link(forum.last_post.topic.title, to: Routes.forum_topic_path(@conn, :show, forum, forum.last_post.topic)) + br + => link("Go to post", to: Routes.forum_topic_path(@conn, :show, forum, forum.last_post.topic, post_id: forum.last_post.id)) + ' by + => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: forum.last_post + 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 new file mode 100644 index 00000000..3de8d63b --- /dev/null +++ b/lib/philomena_web/templates/forum/show.html.slime @@ -0,0 +1,58 @@ +h1 = @forum.name +.block + .block__header + => link("Forums", to: Routes.forum_path(@conn, :index)) + ' » + => link(@forum.name, to: Routes.forum_path(@conn, :show, @forum)) + /= icon_link 'New Topic', 'fas fa-fw fa-pen-square', new_forum_topic_path(@forum) + /= icon_link 'Search Posts', 'fa fa-fw fa-search', posts_path(forum_id: @forum.id) + span.spacing-left + => @forum.topic_count + ' topics + .block__header--sub.block__header--light + /- if @forum.topic_count >= Topic.topics_per_page do + / = paginate @topics + span.block__header__title = @forum.description + /- if current_user + / = subscription_link(@forum, current_user) + /- else + / | Login to subscribe to new threads + .block__content + table.table.table--communication-list + thead + tr + th.table--communication-list__name Topic + th.table--communication-list__stats.hide-mobile Views + th.table--communication-list__stats.hide-mobile Posts + th.table--communication-list__last-post Last Post + tbody + = for topic <- @topics do + tr + td.table--communication-list__name + = if topic.sticky do + i.fa.fa-thumbtack + = if topic.locked_at do + i.fa.fa-lock + = if topic.poll do + i.fas.fa-poll-h + =< link(topic.title, to: Routes.forum_topic_path(@conn, :show, @forum, topic)) + .small-text + ' Posted + => pretty_time(topic.created_at) + ' by + = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: topic + 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 + br + => pretty_time(topic.last_post.created_at) + /= if @forum.topic_count >= Topic.topics_per_page do + / .block__header.block__header--light + / = paginate @topics + +/- if current_user +/ = render partial: 'topics/form' diff --git a/lib/philomena_web/templates/post/_post.html.slime b/lib/philomena_web/templates/post/_post.html.slime new file mode 100644 index 00000000..4476c953 --- /dev/null +++ b/lib/philomena_web/templates/post/_post.html.slime @@ -0,0 +1,36 @@ +article.block.communication id="post_#{@post.id}" + .block__content.flex.flex--no-wrap + .flex__fixed.spacing-right + /=<> user_avatar((post.user if post.user_visible?), 'avatar--100px', post.author) + .flex__grow.communication__body + span.communication__body__sender-name = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @post + .communication__body__text + = if !@post.hidden_from_users do + =<> @post.body + + .block__content.communication__options + .flex.flex--wrap.flex--spaced-out + = render PhilomenaWeb.PostView, "_post_options.html", conn: @conn, post: @post + + /- if can?(:hide, Post) + / .js-staff-action + / - if !post.hidden_from_users && !post.destroyed_content + / =<> link_to '#', class: 'communication__interaction togglable-delete-form-link', 'data-click-toggle': "#inline-del-form-post-#{post.id}" do + / i.fa.fa-times + / =<> 'Delete' + / - elsif post.hidden_from_users && !post.destroyed_content + / =<> link_to forum_topic_post_hide_path(post.topic.forum, post.topic, post), data: { confirm: t('are_you_sure') }, method: :delete, class: 'communication__interaction' do + / i.fa.fa-check + / =<> 'Restore' + / - if can?(:manage, Post) + / =<> link_to forum_topic_post_path(post.topic.forum, post.topic, post, deletion_reason: post.deletion_reason), method: :delete, data: { confirm: t('are_you_sure') }, class: 'communication__interaction' do + / i.fa.fa-times + / =<> 'Delete Contents' + / - if can?(:manage, Post) + / .communication__info + / =<> link_to_ip(post.ip) + / .communication__info + / =<> link_to_fingerprint(post.fingerprint, post.user_agent) + / = form_tag forum_topic_post_hide_path(post.topic.forum, post.topic, post), class: 'togglable-delete-form hidden flex', id: "inline-del-form-post-#{post.id}" + / = text_field_tag :deletion_reason, nil, class: 'input input--wide', placeholder: 'Deletion Reason', id: "inline-del-reason-post-#{post.id}", required: true + / = submit_tag 'Delete', class: 'button' diff --git a/lib/philomena_web/templates/post/_post_options.html.slime b/lib/philomena_web/templates/post/_post_options.html.slime new file mode 100644 index 00000000..ff6befba --- /dev/null +++ b/lib/philomena_web/templates/post/_post_options.html.slime @@ -0,0 +1,31 @@ +div + ' Posted + => pretty_time(@post.created_at) + /=<> link_to new_report_path(reportable_class: 'Post', reportable_id: post.id), class: 'communication__interaction' do + / i.fa.fa-flag + / =<> 'Report' + /- if post.edited_at && can?(:read, post) + / br + / a href=forum_topic_post_history_path(post.topic.forum, post.topic, post) + / | Edited + / =<> friendly_time(post.edited_at) + / - if post.edit_reason.present? + / | because: + / =<> post.edit_reason +div + - link_path = Routes.forum_topic_path(@conn, :show, @post.topic.forum, @post.topic, post_id: @post.id) <> "post_#{@post.id}" + a.communication__interaction title="Link to post" href=link_path + i.fa.fa-link> + ' Link + /=<> link_to link_path, 'data-author': safe_author(post), 'data-reply-url': link_path, 'data-post': (post.hidden_from_users ? '' : post.body), class: 'communication__interaction post-reply post-reply-quote' do + a.communication__interaction.post-reply.post-reply-quote href=link_path data-reply-url=link_path data-author="" data-post="" + i.fa.fa-quote-right> + ' Quote + /=<> link_to link_path, 'data-author': safe_author(post), 'data-reply-url': link_path, class: 'communication__interaction post-reply' do + a.communication__interaction.post-reply href=link_path data-reply-url=link_path data-author="" + i.fa.fa-reply + ' Reply + /span.owner-options.hidden + / strong =<> link_to edit_forum_topic_post_path(post.topic.forum, post.topic, post), class: 'communication__interaction' do + / i.fas.fa-edit + / ' Edit diff --git a/lib/philomena_web/templates/topic/show.html.slime b/lib/philomena_web/templates/topic/show.html.slime new file mode 100644 index 00000000..d57143dd --- /dev/null +++ b/lib/philomena_web/templates/topic/show.html.slime @@ -0,0 +1,95 @@ +h1 = @topic.title +/ Header section +.block + .block__header + => link("Forums", to: Routes.forum_path(@conn, :index)) + ' » + => link(@forum.name, to: Routes.forum_path(@conn, :show, @forum)) + ' » + => link(@topic.title, to: Routes.forum_topic_path(@conn, :show, @forum, @topic)) + /a href=posts_path(forum_id: @forum.id, subject: @topic.title) + / i.fa.fa-fw.fa-search> + / | Search Posts + .flex.flex--wrap.block__header.block__header--light + /.flex--fixed + / = paginate @posts + .flex--fixed.block__header__item + ' Started by + => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @topic + .flex--fixed.block__header__item + ' Posted + =< pretty_time(@topic.created_at) + .flex--fixed.block__header__item + => @topic.post_count - 1 + ' replies + /= if current_user + / = subscription_link(@topic, current_user) + /- else + ' Login to subscribe to responses + + / Display the poll, if any + /= render partial: 'polls/display', locals: { poll: @topic.poll } + + / The actual posts + .posts-area + .post-list + = for post <- @posts, !post.destroyed_content do + = render PhilomenaWeb.PostView, "_post.html", conn: @conn, post: post + + /include ../adverts/_box.html.slim + + / Post editability data for JS + /.js-editable-posts data-editable=editable_communications(@posts).to_json + + / Footer section + /.block: .block__header.block__header--light = paginate @posts + + / Post form + = if @topic.post_count < 200_000 do + /= render partial: 'topics/posts/form' + - else + /h3 Okay, we're impressed + /p You're looking at a thread with over 200,000 posts in it! + /p For various reasons, we'd like to ask you to start a new topic. + + / Mod tools + /- if can? :assist, Topic + / .block__content + / input.toggle-box id="administrator_tools" type="checkbox" checked=false + / label for="administrator_tools" Topic Admin Tools + / .toggle-box-container + / .toggle-box-container__content + / p + / - if can?(:unlock, @topic) && !@topic.locked_at.nil? + / => button_to forum_topic_lock_path(@forum, @topic), method: :delete, class: 'button' do + / i.fa.fa-lock> + / | Unlock + / - if can?(:stick, @topic) && !@topic.sticky + / => button_to forum_topic_stick_path(@forum, @topic), method: :post, class: 'button' do + / i.fa.fa-thumbtack> + / | Stick + / - if can?(:stick, @topic) && @topic.sticky + / => button_to forum_topic_stick_path(@forum, @topic), method: :delete, class: 'button' do + / i.fa.fa-thumbtack> + / | Unstick + / - if can?(:lock, @topic) && @topic.locked_at.nil? + / = form_tag forum_topic_lock_path(@forum, @topic), method: :post, class: 'hform' do + / .field + / => text_field_tag :lock_reason, '', placeholder: 'Lock reason', class: 'input hform__text' + / => button_tag class: 'hform__button button' do + / i.fa.fa-unlock> + / | Lock + / - if can? :move, @topic + / = form_tag forum_topic_move_path(@forum, @topic), method: :post, class: 'hform' do + / .field + / => select_tag :target_forum_id, options_from_collection_for_select(Forum.where.not(id: @forum.id).all, :short_name, :name), class: 'input hform__text' + / => button_tag class: 'hform__button button' do + / i.fa.fa-truck> + / | Move + / - if can?(:destroy, @topic) && !@topic.hidden_from_users + / = form_tag forum_topic_hide_path(@forum, @topic), method: :post, class: 'hform' do + / .field + / => text_field_tag :deletion_reason, '', placeholder: 'Deletion reason', required: true, class: 'input hform__text' + / => button_tag class: 'hform__button button' do + / i.fa.fa-trash> + / | Delete diff --git a/lib/philomena_web/views/forum_view.ex b/lib/philomena_web/views/forum_view.ex new file mode 100644 index 00000000..e871d326 --- /dev/null +++ b/lib/philomena_web/views/forum_view.ex @@ -0,0 +1,3 @@ +defmodule PhilomenaWeb.ForumView do + use PhilomenaWeb, :view +end diff --git a/lib/philomena_web/views/post_view.ex b/lib/philomena_web/views/post_view.ex new file mode 100644 index 00000000..8ef17fe2 --- /dev/null +++ b/lib/philomena_web/views/post_view.ex @@ -0,0 +1,3 @@ +defmodule PhilomenaWeb.PostView do + use PhilomenaWeb, :view +end diff --git a/lib/philomena_web/views/topic_view.ex b/lib/philomena_web/views/topic_view.ex new file mode 100644 index 00000000..ee4e0ae0 --- /dev/null +++ b/lib/philomena_web/views/topic_view.ex @@ -0,0 +1,3 @@ +defmodule PhilomenaWeb.TopicView do + use PhilomenaWeb, :view +end