diff --git a/lib/philomena/versions.ex b/lib/philomena/versions.ex index e37e281c..28b30f3b 100644 --- a/lib/philomena/versions.ex +++ b/lib/philomena/versions.ex @@ -7,35 +7,47 @@ defmodule Philomena.Versions do alias Philomena.Repo alias Philomena.Versions.Version + alias Philomena.Users.User - @doc """ - Returns the list of versions. + def load_data_and_associations(versions, parent) do + user_ids = + versions + |> Enum.map(& &1.whodunnit) + |> Enum.reject(&is_nil/1) - ## Examples + users = + User + |> where([u], u.id in ^user_ids) + |> preload(awards: :badge) + |> Repo.all() + |> Map.new(&{to_string(&1.id), &1}) - iex> list_versions() - [%Version{}, ...] + {versions, _last_body} = + versions + |> Enum.reverse() + |> Enum.map_reduce(nil, fn version, previous_body -> + yaml = YamlElixir.read_from_string!(version.object || "") + body = yaml["body"] || "" + edit_reason = yaml["edit_reason"] - """ - def list_versions do - Repo.all(Version) + v = + %{ + version | + parent: parent, + user: users[version.whodunnit], + body: body, + edit_reason: edit_reason, + difference: difference(previous_body, body) + } + + {v, body} + end) + + Enum.reverse(versions) end - @doc """ - Gets a single version. - - Raises `Ecto.NoResultsError` if the Version does not exist. - - ## Examples - - iex> get_version!(123) - %Version{} - - iex> get_version!(456) - ** (Ecto.NoResultsError) - - """ - def get_version!(id), do: Repo.get!(Version, id) + defp difference(nil, next), do: [eq: next] + defp difference(previous, next), do: String.myers_difference(previous, next) @doc """ Creates a version. @@ -54,51 +66,4 @@ defmodule Philomena.Versions do |> Version.changeset(attrs) |> Repo.insert() end - - @doc """ - Updates a version. - - ## Examples - - iex> update_version(version, %{field: new_value}) - {:ok, %Version{}} - - iex> update_version(version, %{field: bad_value}) - {:error, %Ecto.Changeset{}} - - """ - def update_version(%Version{} = version, attrs) do - version - |> Version.changeset(attrs) - |> Repo.update() - end - - @doc """ - Deletes a Version. - - ## Examples - - iex> delete_version(version) - {:ok, %Version{}} - - iex> delete_version(version) - {:error, %Ecto.Changeset{}} - - """ - def delete_version(%Version{} = version) do - Repo.delete(version) - end - - @doc """ - Returns an `%Ecto.Changeset{}` for tracking version changes. - - ## Examples - - iex> change_version(version) - %Ecto.Changeset{source: %Version{}} - - """ - def change_version(%Version{} = version) do - Version.changeset(version, %{}) - end end diff --git a/lib/philomena/versions/attribution.ex b/lib/philomena/versions/attribution.ex new file mode 100644 index 00000000..da354094 --- /dev/null +++ b/lib/philomena/versions/attribution.ex @@ -0,0 +1,16 @@ +defimpl Philomena.Attribution, for: Philomena.Versions.Version do + def object_identifier(version) do + to_string(version.parent.id) + end + + def best_user_identifier(version) do + to_string(version.user.id) + end + + def anonymous?(version) do + same_user?(version.user, version.parent) and !!version.parent.anonymous + end + + defp same_user?(%{id: id}, %{user_id: id}), do: true + defp same_user?(_user, _parent), do: false +end \ No newline at end of file diff --git a/lib/philomena/versions/version.ex b/lib/philomena/versions/version.ex index a5e6988e..0db017e2 100644 --- a/lib/philomena/versions/version.ex +++ b/lib/philomena/versions/version.ex @@ -11,6 +11,12 @@ defmodule Philomena.Versions.Version do field :item_id, :integer field :item_type, :string + field :user, :any, virtual: true + field :parent, :any, virtual: true + field :body, :string, virtual: true + field :edit_reason, :string, virtual: true + field :difference, :any, virtual: true + timestamps(inserted_at: :created_at, updated_at: false) end diff --git a/lib/philomena_web/controllers/image/comment/history_controller.ex b/lib/philomena_web/controllers/image/comment/history_controller.ex new file mode 100644 index 00000000..6cfd37aa --- /dev/null +++ b/lib/philomena_web/controllers/image/comment/history_controller.ex @@ -0,0 +1,28 @@ +defmodule PhilomenaWeb.Image.Comment.HistoryController do + use PhilomenaWeb, :controller + + alias Philomena.Versions.Version + alias Philomena.Versions + alias Philomena.Comments.Comment + alias Philomena.Images.Image + alias Philomena.Repo + import Ecto.Query + + plug PhilomenaWeb.CanaryMapPlug, index: :show + plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true + plug :load_and_authorize_resource, model: Comment, id_name: "comment_id", persisted: true, preload: [:user, :image] + + def index(conn, _params) do + comment = conn.assigns.comment + + versions = + Version + |> where(item_type: "Comment", item_id: ^comment.id) + |> order_by(desc: :created_at) + |> limit(25) + |> Repo.all() + |> Versions.load_data_and_associations(comment) + + render(conn, "index.html", versions: versions) + end +end \ No newline at end of file diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index 257904ed..17d7b329 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -152,6 +152,7 @@ defmodule PhilomenaWeb.Router do resources "/images", ImageController, only: [:index, :show, :new, :create] do resources "/comments", Image.CommentController, only: [:index, :show, :create] do resources "/reports", Image.Comment.ReportController, only: [:new, :create] + resources "/history", Image.Comment.HistoryController, only: [:index] end resources "/tags", Image.TagController, only: [:update], singleton: true resources "/sources", Image.SourceController, only: [:update], singleton: true diff --git a/lib/philomena_web/templates/image/comment/history/index.html.slime b/lib/philomena_web/templates/image/comment/history/index.html.slime new file mode 100644 index 00000000..58fb3ecf --- /dev/null +++ b/lib/philomena_web/templates/image/comment/history/index.html.slime @@ -0,0 +1,46 @@ +h1 + ' Viewing last 25 versions of comment by + = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @comment, conn: @conn + ' on image + a href=Routes.image_path(@conn, :show, @comment.image) + | # + = @comment.image_id + += for version <- @versions do + article.block.communication + .block__content.flex.flex--no-wrap + .flex__fixed.spacing-right + = 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, awards: true, conn: @conn + br + + = render PhilomenaWeb.UserAttributionView, "_anon_user_title.html", object: @comment, conn: @conn + + .communication__body__text + = for edit <- version.difference do + = case edit do + - {:eq, value} -> + = text_to_html(value) + + - {:ins, value} -> + ins.differ = text_to_html(value) + + - {:del, value} -> + del.differ = text_to_html(value) + + .block__content.communication__options + .flex.flex--wrap.flex--spaced-out + div + = if version.edit_reason not in [nil, ""] do + ' Reason: + = version.edit_reason + - else + ' No reason given + + .flex__right + ' Edited + = pretty_time(version.created_at) + ' by + = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @version, conn: @conn \ No newline at end of file diff --git a/lib/philomena_web/views/image/comment/history_view.ex b/lib/philomena_web/views/image/comment/history_view.ex new file mode 100644 index 00000000..3d2423c0 --- /dev/null +++ b/lib/philomena_web/views/image/comment/history_view.ex @@ -0,0 +1,3 @@ +defmodule PhilomenaWeb.Image.Comment.HistoryView do + use PhilomenaWeb, :view +end diff --git a/mix.exs b/mix.exs index d30195f3..bfece2aa 100644 --- a/mix.exs +++ b/mix.exs @@ -60,7 +60,8 @@ defmodule Philomena.MixProject do {:bamboo_smtp, "~> 1.7"}, {:remote_ip, "~> 0.2.0"}, {:briefly, "~> 0.3.0"}, - {:phoenix_mtm, "~> 1.0.0"} + {:phoenix_mtm, "~> 1.0.0"}, + {:yaml_elixir, "~> 2.4.0"} ] end diff --git a/mix.lock b/mix.lock index 71ffe421..1645e02a 100644 --- a/mix.lock +++ b/mix.lock @@ -61,4 +61,6 @@ "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"}, "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, + "yamerl": {:hex, :yamerl, "0.7.0", "e51dba652dce74c20a88294130b48051ebbbb0be7d76f22de064f0f3ccf0aaf5", [:rebar3], [], "hexpm"}, + "yaml_elixir": {:hex, :yaml_elixir, "2.4.0", "2f444abc3c994c902851fde56b6a9cb82895c291c05a0490a289035c2e62ae71", [:mix], [{:yamerl, "~> 0.7", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm"}, }