diff --git a/lib/philomena/mod_notes.ex b/lib/philomena/mod_notes.ex index f74c9d8f..9ae9313b 100644 --- a/lib/philomena/mod_notes.ex +++ b/lib/philomena/mod_notes.ex @@ -49,8 +49,8 @@ defmodule Philomena.ModNotes do {:error, %Ecto.Changeset{}} """ - def create_mod_note(attrs \\ %{}) do - %ModNote{} + def create_mod_note(creator, attrs \\ %{}) do + %ModNote{moderator_id: creator.id} |> ModNote.changeset(attrs) |> Repo.insert() end diff --git a/lib/philomena/mod_notes/mod_note.ex b/lib/philomena/mod_notes/mod_note.ex index bf1674cb..30c468d4 100644 --- a/lib/philomena/mod_notes/mod_note.ex +++ b/lib/philomena/mod_notes/mod_note.ex @@ -12,7 +12,8 @@ defmodule Philomena.ModNotes.ModNote do field :notable_type, :string field :body, :string - field :deleted, :boolean, default: false + + field :notable, :any, virtual: true timestamps(inserted_at: :created_at) end @@ -20,7 +21,8 @@ defmodule Philomena.ModNotes.ModNote do @doc false def changeset(mod_note, attrs) do mod_note - |> cast(attrs, []) - |> validate_required([]) + |> cast(attrs, [:notable_id, :notable_type, :body]) + |> validate_required([:notable_id, :notable_type, :body]) + |> validate_inclusion(:notable_type, ["User", "Report", "DnpEntry"]) end end diff --git a/lib/philomena/polymorphic.ex b/lib/philomena/polymorphic.ex index e7c48aea..1d604601 100644 --- a/lib/philomena/polymorphic.ex +++ b/lib/philomena/polymorphic.ex @@ -7,12 +7,14 @@ defmodule Philomena.Polymorphic do "Comment" => Philomena.Comments.Comment, "Commission" => Philomena.Commissions.Commission, "Conversation" => Philomena.Conversations.Conversation, + "DnpEntry" => Philomena.DnpEntries.DnpEntry, "Filter" => Philomena.Filters.Filter, "Forum" => Philomena.Forums.Forum, "Gallery" => Philomena.Galleries.Gallery, "Image" => Philomena.Images.Image, "LivestreamChannel" => Philomena.Channels.Channel, "Post" => Philomena.Posts.Post, + "Report" => Philomena.Reports.Report, "Topic" => Philomena.Topics.Topic, "User" => Philomena.Users.User } @@ -21,10 +23,12 @@ defmodule Philomena.Polymorphic do "Comment" => [:user, image: :tags], "Commission" => [:user], "Conversation" => [:from, :to], + "DnpEntry" => [:requesting_user], "Gallery" => [:creator], "Image" => [:user, :tags], "Post" => [:user, topic: :forum], - "Topic" => [:forum, :user] + "Topic" => [:forum, :user], + "Report" => [:user] } # Deal with Rails polymorphism BS @@ -64,4 +68,4 @@ defmodule Philomena.Polymorphic do %{struct | name => row} end) end -end \ No newline at end of file +end diff --git a/lib/philomena/users/ability.ex b/lib/philomena/users/ability.ex index 2d9199a5..0d767e38 100644 --- a/lib/philomena/users/ability.ex +++ b/lib/philomena/users/ability.ex @@ -9,6 +9,7 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do alias Philomena.Images.Image alias Philomena.Forums.Forum alias Philomena.Topics.Topic + alias Philomena.ModNotes.ModNote alias Philomena.Posts.Post alias Philomena.Filters.Filter alias Philomena.Galleries.Gallery @@ -97,6 +98,9 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do # Award badges def can?(%User{role: "moderator"}, :create, %Award{}), do: true + # Create mod notes + def can?(%User{role: "moderator"}, :index, ModNote), do: true + # # Assistants can... # diff --git a/lib/philomena_web/controllers/admin/mod_note_controller.ex b/lib/philomena_web/controllers/admin/mod_note_controller.ex new file mode 100644 index 00000000..48a9c9bb --- /dev/null +++ b/lib/philomena_web/controllers/admin/mod_note_controller.ex @@ -0,0 +1,92 @@ +defmodule PhilomenaWeb.Admin.ModNoteController do + use PhilomenaWeb, :controller + + alias Philomena.Textile.Renderer + alias Philomena.ModNotes.ModNote + alias Philomena.Polymorphic + alias Philomena.ModNotes + alias Philomena.Repo + import Ecto.Query + + plug :verify_authorized + plug :load_resource, model: ModNote, only: [:edit, :update, :delete] + plug :preload_association when action in [:edit, :update, :delete] + + def index(conn, %{"q" => q}) do + ModNote + |> where([m], ilike(m.body, ^"%#{q}%")) + |> load_mod_notes(conn) + end + + def index(conn, _params) do + load_mod_notes(ModNote, conn) + end + + defp load_mod_notes(queryable, conn) do + mod_notes = + queryable + |> preload(:moderator) + |> Repo.paginate(conn.assigns.scrivener) + + bodies = Renderer.render_collection(mod_notes, conn) + preloaded = Polymorphic.load_polymorphic(mod_notes, notable: [notable_id: :notable_type]) + mod_notes = %{mod_notes | entries: Enum.zip(bodies, preloaded)} + + render(conn, "index.html", mod_notes: mod_notes) + end + + def new(conn, %{"notable_type" => type, "notable_id" => id}) do + changeset = ModNotes.change_mod_note(%ModNote{notable_type: type, notable_id: id}) + render(conn, "new.html", changeset: changeset) + end + + def create(conn, %{"mod_note" => mod_note_params}) do + case ModNotes.create_mod_note(conn.assigns.current_user, mod_note_params) do + {:ok, _mod_note} -> + conn + |> put_flash(:info, "Successfully created mod note.") + |> redirect(to: Routes.admin_mod_note_path(conn, :index)) + + {:error, changeset} -> + render(conn, "new.html", changeset: changeset) + end + end + + def edit(conn, _params) do + changeset = ModNotes.change_mod_note(conn.assigns.mod_note) + render(conn, "edit.html", changeset: changeset) + end + + def update(conn, %{"mod_note" => mod_note_params}) do + case ModNotes.update_mod_note(conn.assigns.mod_note, mod_note_params) do + {:ok, _mod_note} -> + conn + |> put_flash(:info, "Successfully updated mod note.") + |> redirect(to: Routes.admin_mod_note_path(conn, :index)) + + {:error, changeset} -> + render(conn, "edit.html", changeset: changeset) + end + end + + def delete(conn, _params) do + {:ok, _mod_note} = ModNotes.delete_mod_note(conn.assigns.mod_note) + + conn + |> put_flash(:info, "Successfully deleted mod note.") + |> redirect(to: Routes.admin_mod_note_path(conn, :index)) + end + + defp verify_authorized(conn, _opts) do + case Canada.Can.can?(conn.assigns.current_user, :index, ModNote) do + true -> conn + _false -> PhilomenaWeb.NotAuthorizedPlug.call(conn) + end + end + + def preload_association(%{assigns: %{mod_note: mod_note}} = conn, _opts) do + [mod_note] = Polymorphic.load_polymorphic([mod_note], notable: [notable_id: :notable_type]) + + assign(conn, :mod_note, mod_note) + end +end diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index 08c81493..ebb34805 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -205,6 +205,7 @@ defmodule PhilomenaWeb.Router do resources "/forums", ForumController, except: [:show, :delete] resources "/badges", BadgeController, except: [:show, :delete] + resources "/mod_notes", ModNoteController, except: [:show] end resources "/duplicate_reports", DuplicateReportController, only: [] do diff --git a/lib/philomena_web/templates/admin/mod_note/_form.html.slime b/lib/philomena_web/templates/admin/mod_note/_form.html.slime new file mode 100644 index 00000000..494735ef --- /dev/null +++ b/lib/philomena_web/templates/admin/mod_note/_form.html.slime @@ -0,0 +1,15 @@ += form_for @changeset, @action, fn f -> + = if @changeset.action do + .alert.alert-danger + p Oops, something went wrong! Please check the errors below. + + .field + => label f, :body, "Note" + = text_input f, :body, class: "input input--wide", required: true + = error_tag f, :body + + = hidden_input f, :notable_id + = hidden_input f, :notable_type + + .field + = submit "Save", class: "button" diff --git a/lib/philomena_web/templates/admin/mod_note/edit.html.slime b/lib/philomena_web/templates/admin/mod_note/edit.html.slime new file mode 100644 index 00000000..e9df5556 --- /dev/null +++ b/lib/philomena_web/templates/admin/mod_note/edit.html.slime @@ -0,0 +1,6 @@ +h2 + ' Editing mod note for + => @mod_note.notable_type + => @mod_note.notable_id + += render PhilomenaWeb.Admin.ModNoteView, "_form.html", changeset: @changeset, action: Routes.admin_mod_note_path(@conn, :update, @mod_note), conn: @conn diff --git a/lib/philomena_web/templates/admin/mod_note/index.html.slime b/lib/philomena_web/templates/admin/mod_note/index.html.slime new file mode 100644 index 00000000..80caba1b --- /dev/null +++ b/lib/philomena_web/templates/admin/mod_note/index.html.slime @@ -0,0 +1,38 @@ +- route = fn p -> Routes.admin_mod_note_path(@conn, :index, p) end +- pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @mod_notes, route: route, conn: @conn + +h2 Mod Notes + +.block + .block__header + span.block__header__title Mod Notes + = pagination + + .block__content + table.table + thead + tr + td Object + td Note + td Time + td Moderator + td Actions + tbody + = for {body, note} <- @mod_notes do + tr + td + = link_to_noted_thing(@conn, note.notable) + + td + == body + + td + = pretty_time note.created_at + + td + = link note.moderator.name, to: Routes.profile_path(@conn, :show, note.moderator) + + td + => link "Edit", to: Routes.admin_mod_note_path(@conn, :edit, note) + ' • + => link "Delete", to: Routes.admin_mod_note_path(@conn, :delete, note), data: [confirm: "Are you really, really sure?", method: "delete"] diff --git a/lib/philomena_web/templates/admin/mod_note/new.html.slime b/lib/philomena_web/templates/admin/mod_note/new.html.slime new file mode 100644 index 00000000..1cb13aea --- /dev/null +++ b/lib/philomena_web/templates/admin/mod_note/new.html.slime @@ -0,0 +1,6 @@ +h2 + ' New mod note for + => @conn.params["notable_type"] + => @conn.params["notable_id"] + += render PhilomenaWeb.Admin.ModNoteView, "_form.html", changeset: @changeset, action: Routes.admin_mod_note_path(@conn, :create), conn: @conn diff --git a/lib/philomena_web/templates/layout/_header_staff_links.html.slime b/lib/philomena_web/templates/layout/_header_staff_links.html.slime index 8d7e697a..b428f646 100644 --- a/lib/philomena_web/templates/layout/_header_staff_links.html.slime +++ b/lib/philomena_web/templates/layout/_header_staff_links.html.slime @@ -37,7 +37,7 @@ ' Pages = if manages_mod_notes?(@conn) do - = link to: "#", class: "header__link" do + = link to: Routes.admin_mod_note_path(@conn, :index), class: "header__link" do i.fa.fa-fw.fa-sticky-note> ' Mod Notes diff --git a/lib/philomena_web/views/admin/mod_note_view.ex b/lib/philomena_web/views/admin/mod_note_view.ex new file mode 100644 index 00000000..7e3b3fcd --- /dev/null +++ b/lib/philomena_web/views/admin/mod_note_view.ex @@ -0,0 +1,21 @@ +defmodule PhilomenaWeb.Admin.ModNoteView do + use PhilomenaWeb, :view + + alias Philomena.Users.User + alias Philomena.Reports.Report + alias Philomena.DnpEntries.DnpEntry + + def link_to_noted_thing(conn, %DnpEntry{requesting_user: user} = dnp_entry), + do: link("#{user.name}'s DNP entry", to: Routes.dnp_entry_path(conn, :show, dnp_entry)) + + def link_to_noted_thing(conn, %Report{user: nil} = report), + do: link("Report #{report.id}", to: Routes.admin_report_path(conn, :show, report)) + + def link_to_noted_thing(conn, %Report{user: user} = report), + do: link("Report #{report.id} by #{user.name}", to: Routes.admin_report_path(conn, :show, report)) + + def link_to_noted_thing(conn, %User{} = user), + do: link("User #{user.name}", to: Routes.profile_path(conn, :show, user)) + + def link_to_noted_thing(_conn, notable), do: "Item permanently deleted" +end