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