From 375a5c6de1ffd1c4f32e05fa6615c7f501bbae1d Mon Sep 17 00:00:00 2001 From: Luna D Date: Fri, 13 Dec 2019 18:31:42 -0500 Subject: [PATCH 1/5] topic stick, lock, total shitcode --- lib/philomena/topics.ex | 20 +++++++ lib/philomena/topics/topic.ex | 29 ++++++++++- .../controllers/topic/lock_controller.ex | 52 +++++++++++++++++++ .../controllers/topic/move_controller.ex | 26 ++++++++++ .../controllers/topic/post_controller.ex | 2 +- .../controllers/topic/stick_controller.ex | 51 ++++++++++++++++++ lib/philomena_web/router.ex | 3 ++ .../templates/topic/show.html.slime | 30 ++++++++++- 8 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 lib/philomena_web/controllers/topic/lock_controller.ex create mode 100644 lib/philomena_web/controllers/topic/move_controller.ex create mode 100644 lib/philomena_web/controllers/topic/stick_controller.ex diff --git a/lib/philomena/topics.ex b/lib/philomena/topics.ex index 16806840..67c38bcb 100644 --- a/lib/philomena/topics.ex +++ b/lib/philomena/topics.ex @@ -190,6 +190,26 @@ defmodule Philomena.Topics do |> Repo.delete() end + def stick_topic(topic) do + Topic.stick_changeset(topic) + |> Repo.update() + end + + def unstick_topic(topic) do + Topic.unstick_changeset(topic) + |> Repo.update() + end + + def lock_topic(%Topic{} = topic, attrs, user) do + Topic.lock_changeset(topic, attrs, user) + |> Repo.update() + end + + def unlock_topic(%Topic{} = topic) do + Topic.unlock_changeset(topic) + |> Repo.update() + end + def clear_notification(_topic, nil), do: nil def clear_notification(topic, user) do Notifications.delete_unread_notification("Topic", topic.id, user) diff --git a/lib/philomena/topics/topic.ex b/lib/philomena/topics/topic.ex index 333edd39..fea9af3f 100644 --- a/lib/philomena/topics/topic.ex +++ b/lib/philomena/topics/topic.ex @@ -25,7 +25,7 @@ defmodule Philomena.Topics.Topic do field :view_count, :integer, default: 0 field :sticky, :boolean, default: false field :last_replied_to_at, :naive_datetime - field :locked_at, :naive_datetime + field :locked_at, :utc_datetime field :deletion_reason, :string field :lock_reason, :string field :slug, :string @@ -64,6 +64,33 @@ defmodule Philomena.Topics.Topic do |> unique_constraint(:slug, name: :index_topics_on_forum_id_and_slug) end + def stick_changeset(topic) do + change(topic) + |> put_change(:sticky, true) + end + + def unstick_changeset(topic) do + change(topic) + |> put_change(:sticky, false) + end + + def lock_changeset(topic, attrs, user) do + now = DateTime.utc_now() |> DateTime.truncate(:second) + + change(topic) + |> cast(attrs, [:lock_reason]) + |> put_change(:locked_at, now) + |> put_change(:locked_by_id, user.id) + |> validate_required([:lock_reason]) + end + + def unlock_changeset(topic) do + change(topic) + |> put_change(:locked_at, nil) + |> put_change(:locked_by_id, nil) + |> put_change(:lock_reason, "") + end + def put_slug(changeset) do slug = changeset diff --git a/lib/philomena_web/controllers/topic/lock_controller.ex b/lib/philomena_web/controllers/topic/lock_controller.ex new file mode 100644 index 00000000..6d983045 --- /dev/null +++ b/lib/philomena_web/controllers/topic/lock_controller.ex @@ -0,0 +1,52 @@ +defmodule PhilomenaWeb.Topic.LockController do + import Plug.Conn + use PhilomenaWeb, :controller + + alias Philomena.Topics.Topic + alias Philomena.Topics + alias Philomena.Repo + import Ecto.Query + + plug :load_topic + plug PhilomenaWeb.CanaryMapPlug, create: :lock, delete: :unlock + plug :authorize_resource, model: Topic, id_name: "topic_id", persisted: true + + def create(conn, %{"topic" => topic_params}) do + topic = conn.assigns.topic + user = conn.assigns.current_user + + case Topics.lock_topic(topic, topic_params, user) do + {:ok, topic} -> + conn + |> put_flash(:info, "Topic successfully locked!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + {:error, _changeset} -> + conn + |> put_flash(:error, "Unable to lock the topic!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + end + end + + def delete(conn, _opts) do + topic = conn.assigns.topic + + case Topics.unlock_topic(topic) do + {:ok, topic} -> + conn + |> put_flash(:info, "Topic successfully unlocked!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + {:error, _changeset} -> + conn + |> put_flash(:error, "Unable to unlock the topic!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + end + end + + defp load_topic(conn, _opts) do + topic = Topic + |> where(forum_id: ^conn.params["forum_id"], slug: ^conn.params["topic_id"]) + |> Repo.one() + + Plug.Conn.assign(conn, :topic, topic) + end +end diff --git a/lib/philomena_web/controllers/topic/move_controller.ex b/lib/philomena_web/controllers/topic/move_controller.ex new file mode 100644 index 00000000..b8a8e83c --- /dev/null +++ b/lib/philomena_web/controllers/topic/move_controller.ex @@ -0,0 +1,26 @@ +defmodule PhilomenaWeb.Topic.MoveController do + import Plug.Conn + use PhilomenaWeb, :controller + + alias Philomena.Topics.Topic + alias Philomena.Topics + alias Philomena.Repo + import Ecto.Query + + plug :load_topic + plug PhilomenaWeb.CanaryMapPlug, create: :stick, delete: :stick + plug :authorize_resource, model: Topic, id_name: "topic_id", persisted: true + + # intentionally blank + # todo: moving + def create(conn, _opts) do + end + + defp load_topic(conn, _opts) do + topic = Topic + |> where(forum_id: ^conn.params["forum_id"], slug: ^conn.params["topic_id"]) + |> Repo.one() + + Plug.Conn.assign(conn, :topic, topic) + end +end diff --git a/lib/philomena_web/controllers/topic/post_controller.ex b/lib/philomena_web/controllers/topic/post_controller.ex index 8e620d77..7e11f389 100644 --- a/lib/philomena_web/controllers/topic/post_controller.ex +++ b/lib/philomena_web/controllers/topic/post_controller.ex @@ -83,4 +83,4 @@ defmodule PhilomenaWeb.Topic.PostController do Plug.Conn.assign(conn, :topic, topic) end end -end \ No newline at end of file +end diff --git a/lib/philomena_web/controllers/topic/stick_controller.ex b/lib/philomena_web/controllers/topic/stick_controller.ex new file mode 100644 index 00000000..ed63af04 --- /dev/null +++ b/lib/philomena_web/controllers/topic/stick_controller.ex @@ -0,0 +1,51 @@ +defmodule PhilomenaWeb.Topic.StickController do + import Plug.Conn + use PhilomenaWeb, :controller + + alias Philomena.Topics.Topic + alias Philomena.Topics + alias Philomena.Repo + import Ecto.Query + + plug :load_topic + plug PhilomenaWeb.CanaryMapPlug, create: :stick, delete: :stick + plug :authorize_resource, model: Topic, id_name: "topic_id", persisted: true + + def create(conn, _opts) do + topic = conn.assigns.topic + + case Topics.stick_topic(topic) do + {:ok, topic} -> + conn + |> put_flash(:info, "Topic successfully stickied!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + {:error, _changeset} -> + conn + |> put_flash(:error, "Unable to stick the topic!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + end + end + + def delete(conn, _opts) do + topic = conn.assigns.topic + + case Topics.unstick_topic(topic) do + {:ok, topic} -> + conn + |> put_flash(:info, "Topic successfully unstickied!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + {:error, _changeset} -> + conn + |> put_flash(:error, "Unable to unstick the topic!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + end + end + + defp load_topic(conn, _opts) do + topic = Topic + |> where(forum_id: ^conn.params["forum_id"], slug: ^conn.params["topic_id"]) + |> Repo.one() + + Plug.Conn.assign(conn, :topic, topic) + end +end diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index ca7918d4..45ddb223 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -126,6 +126,9 @@ defmodule PhilomenaWeb.Router do resources "/topics", TopicController, only: [:new, :create] do resources "/subscription", Topic.SubscriptionController, only: [:create, :delete], singleton: true resources "/read", Topic.ReadController, only: [:create], singleton: true + resources "/move", Topic.MoveController, only: [:create], singleton: true + resources "/stick", Topic.StickController, only: [:create, :delete], singleton: true + resources "/lock", Topic.LockController, only: [:create, :delete], singleton: true resources "/posts", Topic.PostController, only: [:edit, :update] do resources "/hide", Topic.Post.HideController, only: [:create, :delete], singleton: true resources "/delete", Topic.Post.DeleteController, only: [:create], singleton: true diff --git a/lib/philomena_web/templates/topic/show.html.slime b/lib/philomena_web/templates/topic/show.html.slime index b9811987..7b6f4d27 100644 --- a/lib/philomena_web/templates/topic/show.html.slime +++ b/lib/philomena_web/templates/topic/show.html.slime @@ -74,7 +74,35 @@ h1 = @topic.title p For various reasons, we'd like to ask you to start a new topic. - true -> - + = if can?(@conn, :assist, Topic) do + .block__content + input.toggle-box id="administrator_tools" type="checkbox" checked=false + label for="administrator_tools" Manage Topic + .toggle-box-container + .toggle-box-container__content + p + = cond do + - can?(@conn, :lock, @topic) and is_nil(@topic.locked_at) -> + = form_for :topic, Routes.forum_topic_lock_path(@conn, :create, @topic.forum_id, @topic), [method: :post, class: "hform"], fn f -> + = text_input f, :lock_reason, class: "input hform__text", placeholder: "Lock reason" + = submit class: "hform__button button" do + i.fas.fa-lock> + ' Lock + - can?(@conn, :unlock, @topic) and not is_nil(@topic.locked_at) -> + = link(to: Routes.forum_topic_lock_path(@conn, :delete, @topic.forum_id, @topic), method: :delete, class: "button") do + i.fas.fa-unlock> + ' Unlock + - true -> + = cond do + - can?(@conn, :stick, @topic) and @topic.sticky -> + = link(to: Routes.forum_topic_stick_path(@conn, :delete, @topic.forum_id, @topic), method: :delete, class: "button") do + i.fas.fa-thumbtack> + ' Unstick + - can?(@conn, :stick, @topic) and not @topic.sticky -> + = link(to: Routes.forum_topic_stick_path(@conn, :create, @topic.forum_id, @topic), method: :post, class: "button") do + i.fas.fa-thumbtack> + ' Stick + - true -> / Mod tools /- if can? :assist, Topic / .block__content From ffed45269b5b62e3568240154825a4dfe09e4557 Mon Sep 17 00:00:00 2001 From: Luna D Date: Sat, 14 Dec 2019 11:10:33 -0500 Subject: [PATCH 2/5] unfinished topic moving --- lib/philomena/topics.ex | 6 +++++ lib/philomena/topics/topic.ex | 5 ++++ .../controllers/topic/move_controller.ex | 16 ++++++++++-- .../templates/topic/show.html.slime | 26 +++++-------------- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/lib/philomena/topics.ex b/lib/philomena/topics.ex index 67c38bcb..c18cf37c 100644 --- a/lib/philomena/topics.ex +++ b/lib/philomena/topics.ex @@ -209,7 +209,13 @@ defmodule Philomena.Topics do Topic.unlock_changeset(topic) |> Repo.update() end + + def move_topic(%Topic{} = topic, attrs) do + Topic.move_changeset(topic, attrs) + |> Repo.update() + end + def clear_notification(nil, _user), do: nil def clear_notification(_topic, nil), do: nil def clear_notification(topic, user) do Notifications.delete_unread_notification("Topic", topic.id, user) diff --git a/lib/philomena/topics/topic.ex b/lib/philomena/topics/topic.ex index fea9af3f..6ad54980 100644 --- a/lib/philomena/topics/topic.ex +++ b/lib/philomena/topics/topic.ex @@ -91,6 +91,11 @@ defmodule Philomena.Topics.Topic do |> put_change(:lock_reason, "") end + def move_changeset(topic, attrs) do + change(topic) + |> put_change(:forum_id, attrs["target_forum_id"] |> String.to_integer()) + end + def put_slug(changeset) do slug = changeset diff --git a/lib/philomena_web/controllers/topic/move_controller.ex b/lib/philomena_web/controllers/topic/move_controller.ex index b8a8e83c..759c8f68 100644 --- a/lib/philomena_web/controllers/topic/move_controller.ex +++ b/lib/philomena_web/controllers/topic/move_controller.ex @@ -8,12 +8,24 @@ defmodule PhilomenaWeb.Topic.MoveController do import Ecto.Query plug :load_topic - plug PhilomenaWeb.CanaryMapPlug, create: :stick, delete: :stick + plug PhilomenaWeb.CanaryMapPlug, create: :move plug :authorize_resource, model: Topic, id_name: "topic_id", persisted: true # intentionally blank # todo: moving - def create(conn, _opts) do + def create(conn, %{"topic" => topic_params}) do + topic = conn.assigns.topic + + case Topics.move_topic(topic, topic_params) do + {:ok, topic} -> + conn + |> put_flash(:info, "Topic successfully moved!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic_params["target_forum_id"] |> String.to_integer(), topic.id)) + {:error, _changeset} -> + conn + |> put_flash(:error, "Unable to move the topic!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + end end defp load_topic(conn, _opts) do diff --git a/lib/philomena_web/templates/topic/show.html.slime b/lib/philomena_web/templates/topic/show.html.slime index 7b6f4d27..cf905ab3 100644 --- a/lib/philomena_web/templates/topic/show.html.slime +++ b/lib/philomena_web/templates/topic/show.html.slime @@ -103,6 +103,13 @@ h1 = @topic.title i.fas.fa-thumbtack> ' Stick - true -> + = if can?(@conn, :move, @topic) do + = form_for :topic, Routes.forum_topic_move_path(@conn, :create, @topic.forum_id, @topic), [method: :post, class: "hform"], fn f -> + .field + = select f, :target_forum_id, @conn.assigns.forums |> Enum.map(fn forum -> {forum.name, forum.id} end), class: "input hform__text" + = submit class: "hform__button button" do + i.fas.fa-truck> + ' Move / Mod tools /- if can? :assist, Topic / .block__content @@ -111,25 +118,6 @@ h1 = @topic.title / .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 From 37be32f2b2c29d89b38557a5a1d60230418ebdac Mon Sep 17 00:00:00 2001 From: Luna D Date: Sat, 14 Dec 2019 11:43:26 -0500 Subject: [PATCH 3/5] finish topic moving --- lib/philomena/topics.ex | 26 ++++++++++++++++--- lib/philomena/topics/topic.ex | 4 +-- .../controllers/topic/lock_controller.ex | 9 ++++--- .../controllers/topic/move_controller.ex | 13 +++++++--- .../topic/post/delete_controller.ex | 6 ++--- .../controllers/topic/post/hide_controller.ex | 10 +++---- .../controllers/topic/stick_controller.ex | 9 ++++--- .../templates/topic/show.html.slime | 7 ----- 8 files changed, 52 insertions(+), 32 deletions(-) diff --git a/lib/philomena/topics.ex b/lib/philomena/topics.ex index c18cf37c..35576a59 100644 --- a/lib/philomena/topics.ex +++ b/lib/philomena/topics.ex @@ -210,9 +210,29 @@ defmodule Philomena.Topics do |> Repo.update() end - def move_topic(%Topic{} = topic, attrs) do - Topic.move_changeset(topic, attrs) - |> Repo.update() + def move_topic(topic, new_forum_id) do + old_forum_id = topic.forum_id + topic_changes = Topic.move_changeset(topic, new_forum_id) + + Multi.new + |> Multi.update(:topic, topic_changes) + |> Multi.run(:update_old_forum, fn repo, %{topic: topic} -> + {count, nil} = + Forum + |> where(id: ^old_forum_id) + |> repo.update_all(inc: [post_count: -topic.post_count, topic_count: -1]) + + {:ok, count} + end) + |> Multi.run(:update_new_forum, fn repo, %{topic: topic} -> + {count, nil} = + Forum + |> where(id: ^topic.forum_id) + |> repo.update_all(inc: [post_count: topic.post_count, topic_count: 1]) + + {:ok, count} + end) + |> Repo.isolated_transaction(:serializable) end def clear_notification(nil, _user), do: nil diff --git a/lib/philomena/topics/topic.ex b/lib/philomena/topics/topic.ex index 6ad54980..d2403c7b 100644 --- a/lib/philomena/topics/topic.ex +++ b/lib/philomena/topics/topic.ex @@ -91,9 +91,9 @@ defmodule Philomena.Topics.Topic do |> put_change(:lock_reason, "") end - def move_changeset(topic, attrs) do + def move_changeset(topic, new_forum_id) do change(topic) - |> put_change(:forum_id, attrs["target_forum_id"] |> String.to_integer()) + |> put_change(:forum_id, new_forum_id) end def put_slug(changeset) do diff --git a/lib/philomena_web/controllers/topic/lock_controller.ex b/lib/philomena_web/controllers/topic/lock_controller.ex index 6d983045..6a50ddcc 100644 --- a/lib/philomena_web/controllers/topic/lock_controller.ex +++ b/lib/philomena_web/controllers/topic/lock_controller.ex @@ -19,11 +19,11 @@ defmodule PhilomenaWeb.Topic.LockController do {:ok, topic} -> conn |> put_flash(:info, "Topic successfully locked!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) {:error, _changeset} -> conn |> put_flash(:error, "Unable to lock the topic!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) end end @@ -34,17 +34,18 @@ defmodule PhilomenaWeb.Topic.LockController do {:ok, topic} -> conn |> put_flash(:info, "Topic successfully unlocked!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) {:error, _changeset} -> conn |> put_flash(:error, "Unable to unlock the topic!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) end end defp load_topic(conn, _opts) do topic = Topic |> where(forum_id: ^conn.params["forum_id"], slug: ^conn.params["topic_id"]) + |> preload([:forum]) |> Repo.one() Plug.Conn.assign(conn, :topic, topic) diff --git a/lib/philomena_web/controllers/topic/move_controller.ex b/lib/philomena_web/controllers/topic/move_controller.ex index 759c8f68..86767640 100644 --- a/lib/philomena_web/controllers/topic/move_controller.ex +++ b/lib/philomena_web/controllers/topic/move_controller.ex @@ -15,16 +15,21 @@ defmodule PhilomenaWeb.Topic.MoveController do # todo: moving def create(conn, %{"topic" => topic_params}) do topic = conn.assigns.topic + target_forum_id = String.to_integer(topic_params["target_forum_id"]) + + case Topics.move_topic(topic, target_forum_id) do + {:ok, %{topic: topic}} -> + topic = Repo.preload(topic, :forum) - case Topics.move_topic(topic, topic_params) do - {:ok, topic} -> conn |> put_flash(:info, "Topic successfully moved!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic_params["target_forum_id"] |> String.to_integer(), topic.id)) + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) {:error, _changeset} -> + topic = Repo.preload(topic, :forum) + conn |> put_flash(:error, "Unable to move the topic!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) end end diff --git a/lib/philomena_web/controllers/topic/post/delete_controller.ex b/lib/philomena_web/controllers/topic/post/delete_controller.ex index 90e9942e..a59d726b 100644 --- a/lib/philomena_web/controllers/topic/post/delete_controller.ex +++ b/lib/philomena_web/controllers/topic/post/delete_controller.ex @@ -5,7 +5,7 @@ defmodule PhilomenaWeb.Topic.Post.DeleteController do alias Philomena.Posts plug PhilomenaWeb.CanaryMapPlug, create: :hide - plug :load_and_authorize_resource, model: Post, id_name: "post_id", persisted: true, preload: [:topic] + plug :load_and_authorize_resource, model: Post, id_name: "post_id", persisted: true, preload: [:topic, topic: :forum] def create(conn, _params) do post = conn.assigns.post @@ -16,12 +16,12 @@ defmodule PhilomenaWeb.Topic.Post.DeleteController do conn |> put_flash(:info, "Post successfully destroyed!") - |> redirect(to: Routes.forum_topic_path(conn, :show, post.topic.forum_id, post.topic_id) <> "#post_#{post.id}") + |> redirect(to: Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic) <> "#post_#{post.id}") {:error, _changeset} -> conn |> put_flash(:error, "Unable to destroy post!") - |> redirect(to: Routes.forum_topic_path(conn, :show, post.topic.forum_id, post.topic_id) <> "#post_#{post.id}") + |> redirect(to: Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic) <> "#post_#{post.id}") end end end diff --git a/lib/philomena_web/controllers/topic/post/hide_controller.ex b/lib/philomena_web/controllers/topic/post/hide_controller.ex index 11b9b193..c659b91b 100644 --- a/lib/philomena_web/controllers/topic/post/hide_controller.ex +++ b/lib/philomena_web/controllers/topic/post/hide_controller.ex @@ -5,7 +5,7 @@ defmodule PhilomenaWeb.Topic.Post.HideController do alias Philomena.Posts plug PhilomenaWeb.CanaryMapPlug, create: :hide, delete: :hide - plug :load_and_authorize_resource, model: Post, id_name: "post_id", persisted: true, preload: [:topic] + plug :load_and_authorize_resource, model: Post, id_name: "post_id", persisted: true, preload: [:topic, topic: :forum] def create(conn, %{"post" => post_params}) do post = conn.assigns.post @@ -17,11 +17,11 @@ defmodule PhilomenaWeb.Topic.Post.HideController do conn |> put_flash(:info, "Post successfully hidden!") - |> redirect(to: Routes.forum_topic_path(conn, :show, post.topic.forum_id, post.topic_id) <> "#post_#{post.id}") + |> redirect(to: Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic) <> "#post_#{post.id}") {:error, _changeset} -> conn |> put_flash(:error, "Unable to hide post!") - |> redirect(to: Routes.forum_topic_path(conn, :show, post.topic.forum_id, post.topic_id) <> "#post_#{post.id}") + |> redirect(to: Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic) <> "#post_#{post.id}") end end @@ -34,11 +34,11 @@ defmodule PhilomenaWeb.Topic.Post.HideController do conn |> put_flash(:info, "Post successfully unhidden!") - |> redirect(to: Routes.forum_topic_path(conn, :show, post.topic.forum_id, post.topic_id) <> "#post_#{post.id}") + |> redirect(to: Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic) <> "#post_#{post.id}") {:error, _changeset} -> conn |> put_flash(:error, "Unable to unhide post!") - |> redirect(to: Routes.forum_topic_path(conn, :show, post.topic.forum_id, post.topic_id) <> "#post_#{post.id}") + |> redirect(to: Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic) <> "#post_#{post.id}") end end end diff --git a/lib/philomena_web/controllers/topic/stick_controller.ex b/lib/philomena_web/controllers/topic/stick_controller.ex index ed63af04..e6d4ecc0 100644 --- a/lib/philomena_web/controllers/topic/stick_controller.ex +++ b/lib/philomena_web/controllers/topic/stick_controller.ex @@ -18,11 +18,11 @@ defmodule PhilomenaWeb.Topic.StickController do {:ok, topic} -> conn |> put_flash(:info, "Topic successfully stickied!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) {:error, _changeset} -> conn |> put_flash(:error, "Unable to stick the topic!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) end end @@ -33,17 +33,18 @@ defmodule PhilomenaWeb.Topic.StickController do {:ok, topic} -> conn |> put_flash(:info, "Topic successfully unstickied!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) {:error, _changeset} -> conn |> put_flash(:error, "Unable to unstick the topic!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum_id, topic.id)) + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) end end defp load_topic(conn, _opts) do topic = Topic |> where(forum_id: ^conn.params["forum_id"], slug: ^conn.params["topic_id"]) + |> preload([:forum]) |> Repo.one() Plug.Conn.assign(conn, :topic, topic) diff --git a/lib/philomena_web/templates/topic/show.html.slime b/lib/philomena_web/templates/topic/show.html.slime index cf905ab3..b0ce6a61 100644 --- a/lib/philomena_web/templates/topic/show.html.slime +++ b/lib/philomena_web/templates/topic/show.html.slime @@ -118,13 +118,6 @@ h1 = @topic.title / .toggle-box-container / .toggle-box-container__content / p - / - 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 From 782baba3241511d4da32cfb3f7f85b0dca2e33b5 Mon Sep 17 00:00:00 2001 From: Luna D Date: Sat, 14 Dec 2019 13:03:11 -0500 Subject: [PATCH 4/5] topic hiding / restoring --- lib/philomena/topics.ex | 10 + lib/philomena/topics/topic.ex | 15 ++ .../controllers/topic/hide_controller.ex | 54 +++++ .../controllers/topic_controller.ex | 4 +- lib/philomena_web/router.ex | 1 + .../templates/topic/show.html.slime | 190 ++++++++++-------- 6 files changed, 186 insertions(+), 88 deletions(-) create mode 100644 lib/philomena_web/controllers/topic/hide_controller.ex diff --git a/lib/philomena/topics.ex b/lib/philomena/topics.ex index 35576a59..1753b0fb 100644 --- a/lib/philomena/topics.ex +++ b/lib/philomena/topics.ex @@ -235,6 +235,16 @@ defmodule Philomena.Topics do |> Repo.isolated_transaction(:serializable) end + def hide_topic(topic, deletion_reason, user) do + Topic.hide_changeset(topic, deletion_reason, user) + |> Repo.update() + end + + def unhide_topic(topic) do + Topic.unhide_changeset(topic) + |> Repo.update() + end + def clear_notification(nil, _user), do: nil def clear_notification(_topic, nil), do: nil def clear_notification(topic, user) do diff --git a/lib/philomena/topics/topic.ex b/lib/philomena/topics/topic.ex index d2403c7b..90f64332 100644 --- a/lib/philomena/topics/topic.ex +++ b/lib/philomena/topics/topic.ex @@ -96,6 +96,21 @@ defmodule Philomena.Topics.Topic do |> put_change(:forum_id, new_forum_id) end + def hide_changeset(topic, deletion_reason, user) do + change(topic) + |> put_change(:hidden_from_users, true) + |> put_change(:deleted_by_id, user.id) + |> put_change(:deletion_reason, deletion_reason) + |> validate_required([:deletion_reason]) + end + + def unhide_changeset(topic) do + change(topic) + |> put_change(:hidden_from_users, false) + |> put_change(:deleted_by_id, nil) + |> put_change(:deletion_reason, "") + end + def put_slug(changeset) do slug = changeset diff --git a/lib/philomena_web/controllers/topic/hide_controller.ex b/lib/philomena_web/controllers/topic/hide_controller.ex new file mode 100644 index 00000000..5eec402c --- /dev/null +++ b/lib/philomena_web/controllers/topic/hide_controller.ex @@ -0,0 +1,54 @@ +defmodule PhilomenaWeb.Topic.HideController do + import Plug.Conn + use PhilomenaWeb, :controller + + alias Philomena.Topics.Topic + alias Philomena.Topics + alias Philomena.Repo + import Ecto.Query + + plug :load_topic + plug PhilomenaWeb.CanaryMapPlug, create: :hide, delete: :hide + plug :authorize_resource, model: Topic, id_name: "topic_id", persisted: true + + def create(conn, %{"topic" => topic_params}) do + topic = conn.assigns.topic + deletion_reason = topic_params["deletion_reason"] + user = conn.assigns.current_user + + case Topics.hide_topic(topic, deletion_reason, user) do + {:ok, topic} -> + conn + |> put_flash(:info, "Topic successfully hidden!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + {:error, _changeset} -> + conn + |> put_flash(:error, "Unable to hide the topic!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + end + end + + def delete(conn, _opts) do + topic = conn.assigns.topic + + case Topics.unhide_topic(topic) do + {:ok, topic} -> + conn + |> put_flash(:info, "Topic successfully restored!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + {:error, _changeset} -> + conn + |> put_flash(:error, "Unable to restore the topic!") + |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + end + end + + defp load_topic(conn, _opts) do + topic = Topic + |> where(forum_id: ^conn.params["forum_id"], slug: ^conn.params["topic_id"]) + |> preload([:forum]) + |> Repo.one() + + Plug.Conn.assign(conn, :topic, topic) + end +end diff --git a/lib/philomena_web/controllers/topic_controller.ex b/lib/philomena_web/controllers/topic_controller.ex index 99083c6a..e557026f 100644 --- a/lib/philomena_web/controllers/topic_controller.ex +++ b/lib/philomena_web/controllers/topic_controller.ex @@ -19,8 +19,8 @@ defmodule PhilomenaWeb.TopicController do forum = conn.assigns.forum topic = Topic - |> where(forum_id: ^forum.id, slug: ^slug, hidden_from_users: false) - |> preload([:user, poll: :options]) + |> where(forum_id: ^forum.id, slug: ^slug) + |> preload([:deleted_by, :user, poll: :options]) |> Repo.one() user = conn.assigns.current_user diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index 45ddb223..6de0b52e 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -129,6 +129,7 @@ defmodule PhilomenaWeb.Router do resources "/move", Topic.MoveController, only: [:create], singleton: true resources "/stick", Topic.StickController, only: [:create, :delete], singleton: true resources "/lock", Topic.LockController, only: [:create, :delete], singleton: true + resources "/hide", Topic.HideController, only: [:create, :delete], singleton: true resources "/posts", Topic.PostController, only: [:edit, :update] do resources "/hide", Topic.Post.HideController, only: [:create, :delete], singleton: true resources "/delete", Topic.Post.DeleteController, only: [:create], singleton: true diff --git a/lib/philomena_web/templates/topic/show.html.slime b/lib/philomena_web/templates/topic/show.html.slime index b0ce6a61..574cb5a4 100644 --- a/lib/philomena_web/templates/topic/show.html.slime +++ b/lib/philomena_web/templates/topic/show.html.slime @@ -3,6 +3,23 @@ elixir: pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @posts, route: route, last: true h1 = @topic.title += if @topic.hidden_from_users do + .block.block--fixed.block--danger + h4 This topic has been deleted by a moderator. + p + strong> Reason: + em + = @topic.deletion_reason + = if can?(@conn, :unhide, @topic) do + p + strong> Deleted by: + em + strong + = link(@topic.deleted_by.name, to: Routes.profile_path(@conn, :show, @topic.deleted_by)) + p + = link(to: Routes.forum_topic_hide_path(@conn, :delete, @forum.id, @topic), method: :delete, class: "button") do + i.fas.fa-check> + ' Restore / Header section .block .block__header @@ -11,60 +28,66 @@ h1 = @topic.title => link(@forum.name, to: Routes.forum_path(@conn, :show, @forum)) ' » => link(@topic.title, to: Routes.forum_topic_path(@conn, :show, @forum, @topic)) - a href=Routes.post_path(@conn, :index, pq: "topic_id:#{@topic.id}") - i.fa.fa-fw.fa-search> - ' Search Posts - .flex.flex--wrap.block__header.block__header--light - .flex--fixed - = pagination - .flex--fixed.block__header__item - ' Started by - => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @topic, conn: @conn - .flex--fixed.block__header__item - ' Posted - =< pretty_time(@topic.created_at) - .flex--fixed.block__header__item - => @topic.post_count - 1 - ' replies - = render PhilomenaWeb.Topic.SubscriptionView, "_subscription.html", forum: @forum, topic: @topic, watching: @watching, conn: @conn + = if not @topic.hidden_from_users or can?(@conn, :hide, @topic) do + a href=Routes.post_path(@conn, :index, pq: "topic_id:#{@topic.id}") + i.fa.fa-fw.fa-search> + ' Search Posts + .flex.flex--wrap.block__header.block__header--light + .flex--fixed + = pagination + .flex--fixed.block__header__item + ' Started by + => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @topic, conn: @conn + .flex--fixed.block__header__item + ' Posted + =< pretty_time(@topic.created_at) + .flex--fixed.block__header__item + => @topic.post_count - 1 + ' replies + = if not @topic.hidden_from_users do + = render PhilomenaWeb.Topic.SubscriptionView, "_subscription.html", forum: @forum, topic: @topic, watching: @watching, conn: @conn -/ Display the poll, if any -= if @topic.poll do - = render PhilomenaWeb.Topic.PollView, "_display.html", poll: @topic.poll, conn: @conn += if not @topic.hidden_from_users or can?(@conn, :hide, @topic) do + / Display the poll, if any + = if @topic.poll do + = render PhilomenaWeb.Topic.PollView, "_display.html", poll: @topic.poll, conn: @conn -/ The actual posts -.posts-area - .post-list - = for {post, body} <- @posts, (!post.destroyed_content or can?(@conn, :hide, post)) do - = render PhilomenaWeb.PostView, "_post.html", conn: @conn, post: post, body: body + / The actual posts + .posts-area + .post-list + = for {post, body} <- @posts, (!post.destroyed_content or can?(@conn, :hide, post)) do + = render PhilomenaWeb.PostView, "_post.html", conn: @conn, post: post, body: body - = if @conn.assigns.advert do - = render PhilomenaWeb.AdvertView, "_box.html", advert: @conn.assigns.advert, conn: @conn + = if @conn.assigns.advert do + = render PhilomenaWeb.AdvertView, "_box.html", advert: @conn.assigns.advert, conn: @conn - / Post editability data for JS - /.js-editable-posts data-editable=editable_communications(@posts).to_json + / Post editability data for JS + /.js-editable-posts data-editable=editable_communications(@posts).to_json - / Footer section - .block - .block__header.block__header--light - = pagination + / Footer section + .block + .block__header.block__header--light + = pagination - = if @topic.locked_at do - .block.block--fixed.block--warning - h4 This topic has been locked to new posts from non-moderators. - p - ' Locked - => pretty_time(@topic.locked_at) - - p - ' Lock reason: - em = @topic.lock_reason + = if @topic.locked_at do + .block.block--fixed.block--warning + h4 This topic has been locked to new posts from non-moderators. + p + ' Locked + => pretty_time(@topic.locked_at) + p + ' Lock reason: + em = @topic.lock_reason / Post form = cond do - @conn.assigns.current_ban -> = render PhilomenaWeb.BanView, "_ban_reason.html", conn: @conn + - @topic.hidden_from_users -> + .block.block--fixed.block--danger + h4 Cannot reply to a deleted topic. + - @topic.post_count < 200_000 and can?(@conn, :create_post, @topic) -> = render PhilomenaWeb.Topic.PostView, "_form.html", conn: @conn, forum: @forum, topic: @topic, changeset: @changeset @@ -81,47 +104,42 @@ h1 = @topic.title .toggle-box-container .toggle-box-container__content p - = cond do - - can?(@conn, :lock, @topic) and is_nil(@topic.locked_at) -> - = form_for :topic, Routes.forum_topic_lock_path(@conn, :create, @topic.forum_id, @topic), [method: :post, class: "hform"], fn f -> - = text_input f, :lock_reason, class: "input hform__text", placeholder: "Lock reason" - = submit class: "hform__button button" do - i.fas.fa-lock> - ' Lock - - can?(@conn, :unlock, @topic) and not is_nil(@topic.locked_at) -> - = link(to: Routes.forum_topic_lock_path(@conn, :delete, @topic.forum_id, @topic), method: :delete, class: "button") do - i.fas.fa-unlock> - ' Unlock - - true -> - = cond do - - can?(@conn, :stick, @topic) and @topic.sticky -> - = link(to: Routes.forum_topic_stick_path(@conn, :delete, @topic.forum_id, @topic), method: :delete, class: "button") do - i.fas.fa-thumbtack> - ' Unstick - - can?(@conn, :stick, @topic) and not @topic.sticky -> - = link(to: Routes.forum_topic_stick_path(@conn, :create, @topic.forum_id, @topic), method: :post, class: "button") do - i.fas.fa-thumbtack> - ' Stick - - true -> - = if can?(@conn, :move, @topic) do - = form_for :topic, Routes.forum_topic_move_path(@conn, :create, @topic.forum_id, @topic), [method: :post, class: "hform"], fn f -> - .field - = select f, :target_forum_id, @conn.assigns.forums |> Enum.map(fn forum -> {forum.name, forum.id} end), class: "input hform__text" - = submit class: "hform__button button" do - i.fas.fa-truck> - ' Move - / 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?(: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 + = if not @topic.hidden_from_users do + = cond do + - can?(@conn, :lock, @topic) and is_nil(@topic.locked_at) -> + = form_for :topic, Routes.forum_topic_lock_path(@conn, :create, @forum.id, @topic), [method: :post, class: "hform"], fn f -> + = text_input f, :lock_reason, class: "input hform__text", placeholder: "Lock reason", required: true + = submit class: "hform__button button" do + i.fas.fa-lock> + ' Lock + - can?(@conn, :unlock, @topic) and not is_nil(@topic.locked_at) -> + = link(to: Routes.forum_topic_lock_path(@conn, :delete, @forum.id, @topic), method: :delete, class: "button") do + i.fas.fa-unlock> + ' Unlock + - true -> + = cond do + - can?(@conn, :stick, @topic) and @topic.sticky -> + = link(to: Routes.forum_topic_stick_path(@conn, :delete, @forum.id, @topic), method: :delete, class: "button") do + i.fas.fa-thumbtack> + ' Unstick + - can?(@conn, :stick, @topic) and not @topic.sticky -> + = link(to: Routes.forum_topic_stick_path(@conn, :create, @forum.id, @topic), method: :post, class: "button") do + i.fas.fa-thumbtack> + ' Stick + - true -> + = if can?(@conn, :move, @topic) do + = form_for :topic, Routes.forum_topic_move_path(@conn, :create, @forum.id, @topic), [method: :post, class: "hform"], fn f -> + .field + = select f, :target_forum_id, @conn.assigns.forums |> Enum.map(fn forum -> {forum.name, forum.id} end), class: "input hform__text" + = submit class: "hform__button button" do + i.fas.fa-truck> + ' Move + = if can?(@conn, :hide, @topic) do + = form_for :topic, Routes.forum_topic_hide_path(@conn, :create, @forum.id, @topic), [method: :post, class: "hform"], fn f -> + .field + = text_input f, :deletion_reason, class: "input hform__text", placeholder: "Deletion reason", required: true + = submit class: "hform__button button" do + i.fas.fa-trash> + ' Delete + - else + | Moderation tools unavailable for deleted topics. From 8cdbb9a07df8aa48b754f479925417107cc10024 Mon Sep 17 00:00:00 2001 From: Luna D Date: Sat, 14 Dec 2019 13:10:27 -0500 Subject: [PATCH 5/5] remove accidental dot --- lib/philomena_web/templates/topic/show.html.slime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/philomena_web/templates/topic/show.html.slime b/lib/philomena_web/templates/topic/show.html.slime index 574cb5a4..b5de8509 100644 --- a/lib/philomena_web/templates/topic/show.html.slime +++ b/lib/philomena_web/templates/topic/show.html.slime @@ -5,7 +5,7 @@ elixir: h1 = @topic.title = if @topic.hidden_from_users do .block.block--fixed.block--danger - h4 This topic has been deleted by a moderator. + h4 This topic has been deleted by a moderator p strong> Reason: em