mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-27 05:37:59 +01:00
wip
This commit is contained in:
parent
f91d9f2143
commit
4d7f229641
17 changed files with 474 additions and 204 deletions
|
@ -10,13 +10,13 @@ defmodule Philomena.Comments do
|
|||
alias PhilomenaQuery.Search
|
||||
alias Philomena.UserStatistics
|
||||
alias Philomena.Comments.Comment
|
||||
alias Philomena.Comments.Version
|
||||
alias Philomena.Comments.SearchIndex, as: CommentIndex
|
||||
alias Philomena.IndexWorker
|
||||
alias Philomena.Images.Image
|
||||
alias Philomena.Images
|
||||
alias Philomena.Notifications
|
||||
alias Philomena.NotificationWorker
|
||||
alias Philomena.Versions
|
||||
alias Philomena.Reports
|
||||
|
||||
@doc """
|
||||
|
@ -93,21 +93,46 @@ defmodule Philomena.Comments do
|
|||
|
||||
"""
|
||||
def update_comment(%Comment{} = comment, editor, attrs) do
|
||||
now = DateTime.utc_now(:second)
|
||||
current_body = comment.body
|
||||
current_reason = comment.edit_reason
|
||||
version_changeset =
|
||||
Version.changeset(%Version{}, comment, editor, %{
|
||||
body: comment.body,
|
||||
edit_reason: comment.edit_reason,
|
||||
created_at: comment.edited_at || comment.created_at
|
||||
})
|
||||
|
||||
comment_changes = Comment.changeset(comment, attrs, now)
|
||||
now = DateTime.utc_now(:second)
|
||||
comment_changeset = Comment.changeset(comment, attrs, now)
|
||||
|
||||
Multi.new()
|
||||
|> Multi.update(:comment, comment_changes)
|
||||
|> Multi.run(:version, fn _repo, _changes ->
|
||||
Versions.create_version("Comment", comment.id, editor.id, %{
|
||||
"body" => current_body,
|
||||
"edit_reason" => current_reason
|
||||
})
|
||||
end)
|
||||
|> Multi.insert(:version, version_changeset)
|
||||
|> Multi.update(:comment, comment_changeset)
|
||||
|> Repo.transaction()
|
||||
|> case do
|
||||
{:ok, %{comment: comment}} ->
|
||||
if not comment.approved do
|
||||
report_non_approved(comment)
|
||||
end
|
||||
|
||||
reindex_comment(comment)
|
||||
|
||||
{:ok, comment}
|
||||
|
||||
_ ->
|
||||
{:error, comment_changeset}
|
||||
end
|
||||
end
|
||||
|
||||
def list_comment_versions(%Comment{} = comment, collection_renderer, pagination) do
|
||||
versions =
|
||||
Version
|
||||
|> where(comment_id: ^comment.id)
|
||||
|> order_by(desc: :created_at, desc: :id)
|
||||
|> select([v], %{v | index: over(row_number(), order_by: [asc: :created_at, asc: :id])})
|
||||
|> preload(:user)
|
||||
|> Repo.paginate(pagination)
|
||||
|
||||
bodies = collection_renderer.(versions)
|
||||
put_in(versions.entries, Enum.zip(versions, bodies))
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
25
lib/philomena/comments/version.ex
Normal file
25
lib/philomena/comments/version.ex
Normal file
|
@ -0,0 +1,25 @@
|
|||
defmodule Philomena.Comments.Version do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Philomena.Comments.Comment
|
||||
alias Philomena.Users.User
|
||||
|
||||
schema "comment_versions" do
|
||||
belongs_to :comment, Comment
|
||||
belongs_to :user, User
|
||||
field :body, :string
|
||||
field :edit_reason, :string
|
||||
field :index, :integer, virtual: true
|
||||
timestamps(inserted_at: :created_at, updated_at: false, type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(comment_version, comment, user, attrs) do
|
||||
comment_version
|
||||
|> cast(attrs, [:body, :edit_reason, :created_at])
|
||||
|> put_assoc(:comment, comment)
|
||||
|> put_assoc(:user, user)
|
||||
|> validate_required([:comment, :user, :body, :created_at])
|
||||
end
|
||||
end
|
|
@ -12,12 +12,12 @@ defmodule Philomena.Posts do
|
|||
alias Philomena.Topics
|
||||
alias Philomena.UserStatistics
|
||||
alias Philomena.Posts.Post
|
||||
alias Philomena.Posts.Version
|
||||
alias Philomena.Posts.SearchIndex, as: PostIndex
|
||||
alias Philomena.IndexWorker
|
||||
alias Philomena.Forums.Forum
|
||||
alias Philomena.Notifications
|
||||
alias Philomena.NotificationWorker
|
||||
alias Philomena.Versions
|
||||
alias Philomena.Reports
|
||||
|
||||
@doc """
|
||||
|
@ -142,32 +142,46 @@ defmodule Philomena.Posts do
|
|||
|
||||
"""
|
||||
def update_post(%Post{} = post, editor, attrs) do
|
||||
now = DateTime.utc_now(:second)
|
||||
current_body = post.body
|
||||
current_reason = post.edit_reason
|
||||
version_changeset =
|
||||
Version.changeset(%Version{}, post, editor, %{
|
||||
body: post.body,
|
||||
edit_reason: post.edit_reason,
|
||||
created_at: post.edited_at || post.created_at
|
||||
})
|
||||
|
||||
post_changes = Post.changeset(post, attrs, now)
|
||||
now = DateTime.utc_now(:second)
|
||||
post_changeset = Post.changeset(post, attrs, now)
|
||||
|
||||
Multi.new()
|
||||
|> Multi.update(:post, post_changes)
|
||||
|> Multi.run(:version, fn _repo, _changes ->
|
||||
Versions.create_version("Post", post.id, editor.id, %{
|
||||
"body" => current_body,
|
||||
"edit_reason" => current_reason
|
||||
})
|
||||
end)
|
||||
|> Multi.insert(:version, version_changeset)
|
||||
|> Multi.update(:post, post_changeset)
|
||||
|> Repo.transaction()
|
||||
|> case do
|
||||
{:ok, %{post: post}} = result ->
|
||||
{:ok, %{post: post}} ->
|
||||
if not post.approved do
|
||||
report_non_approved(post)
|
||||
end
|
||||
|
||||
reindex_post(post)
|
||||
|
||||
result
|
||||
{:ok, post}
|
||||
|
||||
error ->
|
||||
error
|
||||
_ ->
|
||||
{:error, post_changeset}
|
||||
end
|
||||
end
|
||||
|
||||
def list_post_versions(%Post{} = post, collection_renderer, pagination) do
|
||||
versions =
|
||||
Version
|
||||
|> where(post_id: ^post.id)
|
||||
|> order_by(desc: :created_at, desc: :id)
|
||||
|> Repo.paginate(pagination)
|
||||
|
||||
bodies = collection_renderer.(versions)
|
||||
put_in(versions.entries, Enum.zip(versions, bodies))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a Post.
|
||||
|
||||
|
|
24
lib/philomena/posts/version.ex
Normal file
24
lib/philomena/posts/version.ex
Normal file
|
@ -0,0 +1,24 @@
|
|||
defmodule Philomena.Posts.Version do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Philomena.Posts.Post
|
||||
alias Philomena.Users.User
|
||||
|
||||
schema "post_versions" do
|
||||
belongs_to :post, Post
|
||||
belongs_to :user, User
|
||||
field :body, :string
|
||||
field :edit_reason, :string
|
||||
timestamps(inserted_at: :created_at, updated_at: false, type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(post_version, post, user, attrs) do
|
||||
post_version
|
||||
|> cast(attrs, [:body, :edit_reason, :created_at])
|
||||
|> put_assoc(:post, post)
|
||||
|> put_assoc(:user, user)
|
||||
|> validate_required([:post, :user, :body, :created_at])
|
||||
end
|
||||
end
|
|
@ -5,75 +5,77 @@ defmodule Philomena.Versions do
|
|||
|
||||
import Ecto.Query, warn: false
|
||||
alias Philomena.Repo
|
||||
|
||||
alias Philomena.Versions.Version
|
||||
alias Philomena.Users.User
|
||||
|
||||
def load_data_and_associations(versions, parent) do
|
||||
user_ids =
|
||||
versions
|
||||
|> Enum.map(& &1.whodunnit)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|
||||
users =
|
||||
User
|
||||
|> where([u], u.id in ^user_ids)
|
||||
|> preload(awards: :badge)
|
||||
|> Repo.all()
|
||||
|> Map.new(&{to_string(&1.id), &1})
|
||||
|
||||
{versions, _last_body} =
|
||||
versions
|
||||
|> Enum.map_reduce(
|
||||
{parent.body, parent.edit_reason},
|
||||
fn version, {previous_body, previous_reason} ->
|
||||
json = Jason.decode!(version.object || "{}")
|
||||
body = json["body"] || ""
|
||||
edit_reason = json["edit_reason"]
|
||||
|
||||
v = %{
|
||||
version
|
||||
| parent: parent,
|
||||
user: users[version.whodunnit],
|
||||
body: body,
|
||||
edit_reason: previous_reason,
|
||||
difference: difference(body, previous_body)
|
||||
}
|
||||
|
||||
{v, {body, edit_reason}}
|
||||
end
|
||||
)
|
||||
|
||||
versions
|
||||
end
|
||||
|
||||
defp difference(previous, nil), do: [eq: previous]
|
||||
defp difference(previous, next), do: String.myers_difference(previous, next)
|
||||
alias Philomena.Versions.Difference
|
||||
|
||||
@doc """
|
||||
Creates a version.
|
||||
Calculate a list of `m:Philomena.Versions.Difference` structs that represent
|
||||
paginated differences between different versions of the object.
|
||||
|
||||
When expanded, the list of differences may look like:
|
||||
|
||||
[
|
||||
%Difference{
|
||||
previous_version: %CommentVersion{},
|
||||
parent: %Comment{},
|
||||
user: %User{},
|
||||
difference: [del: "goodbye ", ins: "hello ", eq: "world"],
|
||||
}
|
||||
]
|
||||
|
||||
## Examples
|
||||
|
||||
iex> create_version(%{field: value})
|
||||
{:ok, %Version{}}
|
||||
|
||||
iex> create_version(%{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
iex> compute_text_differences(CommentVersion, %Comment{}, :body, page: 1, page_size: 25)
|
||||
%Scrivener.Page{}
|
||||
|
||||
"""
|
||||
def create_version(item_type, item_id, whodunnit, attrs \\ %{}) do
|
||||
%Version{
|
||||
item_type: item_type,
|
||||
item_id: item_id,
|
||||
event: "update",
|
||||
whodunnit: whodunnit(whodunnit)
|
||||
}
|
||||
|> Version.changeset(attrs, item_id)
|
||||
|> Repo.insert()
|
||||
def compute_text_differences(query, parent, name, pagination) do
|
||||
page = Repo.paginate(preload_and_order(query), pagination)
|
||||
initial = get_comparison_version(query, parent, page)
|
||||
|
||||
{differences, _prev} =
|
||||
Enum.map_reduce(page, initial, fn older, newer ->
|
||||
d = %Difference{
|
||||
previous_version: newer,
|
||||
created_at: older.created_at,
|
||||
parent: parent,
|
||||
user: older.user,
|
||||
difference: difference(older, newer, name)
|
||||
}
|
||||
|
||||
{d, older}
|
||||
end)
|
||||
|
||||
%{page | entries: differences}
|
||||
end
|
||||
|
||||
# revolver ocelot
|
||||
defp whodunnit(user_id) when is_integer(user_id), do: to_string(user_id)
|
||||
defp whodunnit(nil), do: nil
|
||||
#
|
||||
# Get the first version to use when reducing the list of differences.
|
||||
#
|
||||
defp get_comparison_version(query, parent, page) do
|
||||
curr = Enum.at(page, 0)
|
||||
|
||||
prev =
|
||||
if curr do
|
||||
query
|
||||
|> where([v], v.created_at > ^curr.created_at and v.id > ^curr.id)
|
||||
|> order_by(asc: :created_at, asc: :id)
|
||||
|> limit(1)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
prev || parent
|
||||
end
|
||||
|
||||
defp preload_and_order(query) do
|
||||
query
|
||||
|> preload(:user)
|
||||
|> order_by(desc: :created_at, desc: :id)
|
||||
end
|
||||
|
||||
defp difference(curr, prev, name) do
|
||||
curr_body = Map.fetch!(curr, name)
|
||||
prev_body = Map.fetch!(prev, name)
|
||||
|
||||
String.myers_difference(curr_body, prev_body)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
defimpl Philomena.Attribution, for: Philomena.Versions.Version do
|
||||
def object_identifier(version) do
|
||||
Philomena.Attribution.object_identifier(version.parent)
|
||||
defimpl Philomena.Attribution, for: Philomena.Versions.Difference do
|
||||
def object_identifier(difference) do
|
||||
Philomena.Attribution.object_identifier(difference.parent)
|
||||
end
|
||||
|
||||
def best_user_identifier(version) do
|
||||
Philomena.Attribution.best_user_identifier(version.parent)
|
||||
def best_user_identifier(difference) do
|
||||
Philomena.Attribution.best_user_identifier(difference.parent)
|
||||
end
|
||||
|
||||
def anonymous?(version) do
|
||||
same_user?(version.user, version.parent) and !!version.parent.anonymous
|
||||
def anonymous?(difference) do
|
||||
same_user?(difference.user, difference.parent) and !!difference.parent.anonymous
|
||||
end
|
||||
|
||||
defp same_user?(%{id: id}, %{user_id: id}), do: true
|
||||
|
|
8
lib/philomena/versions/difference.ex
Normal file
8
lib/philomena/versions/difference.ex
Normal file
|
@ -0,0 +1,8 @@
|
|||
defmodule Philomena.Versions.Difference do
|
||||
defstruct previous_version: nil,
|
||||
created_at: nil,
|
||||
parent: nil,
|
||||
user: nil,
|
||||
edit_reason: nil,
|
||||
difference: []
|
||||
end
|
|
@ -1,43 +0,0 @@
|
|||
defmodule Philomena.Versions.Version do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
schema "versions" do
|
||||
field :event, :string
|
||||
field :whodunnit, :string
|
||||
field :object, :string
|
||||
|
||||
# fixme: rails polymorphic relation
|
||||
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, type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(version, attrs, item_id) do
|
||||
version
|
||||
|> cast(attrs, [:body, :edit_reason])
|
||||
|> put_object(item_id)
|
||||
end
|
||||
|
||||
defp put_object(changeset, item_id) do
|
||||
body = get_field(changeset, :body)
|
||||
edit_reason = get_field(changeset, :edit_reason)
|
||||
|
||||
object =
|
||||
Jason.encode!(%{
|
||||
id: item_id,
|
||||
body: body,
|
||||
edit_reason: edit_reason
|
||||
})
|
||||
|
||||
change(changeset, object: object)
|
||||
end
|
||||
end
|
|
@ -1,11 +1,9 @@
|
|||
defmodule PhilomenaWeb.Image.Comment.HistoryController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.Versions.Version
|
||||
alias Philomena.Versions
|
||||
alias PhilomenaWeb.MarkdownRenderer
|
||||
alias Philomena.Images.Image
|
||||
alias Philomena.Repo
|
||||
import Ecto.Query
|
||||
alias Philomena.Comments
|
||||
|
||||
plug PhilomenaWeb.CanaryMapPlug, index: :show
|
||||
plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true
|
||||
|
@ -15,18 +13,13 @@ defmodule PhilomenaWeb.Image.Comment.HistoryController do
|
|||
def index(conn, _params) do
|
||||
image = conn.assigns.image
|
||||
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)
|
||||
renderer = &MarkdownRenderer.render_collection(&1, conn)
|
||||
versions = Comments.list_comment_versions(comment, renderer, conn.assigns.scrivener)
|
||||
|
||||
render(conn, "index.html",
|
||||
title: "Comment History for comment #{comment.id} on image #{image.id}",
|
||||
versions: versions
|
||||
versions: versions,
|
||||
body: renderer.([comment])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -111,24 +111,18 @@ defmodule PhilomenaWeb.Image.CommentController do
|
|||
|
||||
def update(conn, %{"comment" => comment_params}) do
|
||||
case Comments.update_comment(conn.assigns.comment, conn.assigns.current_user, comment_params) do
|
||||
{:ok, %{comment: comment}} ->
|
||||
if not comment.approved do
|
||||
Comments.report_non_approved(comment)
|
||||
end
|
||||
|
||||
{:ok, comment} ->
|
||||
PhilomenaWeb.Endpoint.broadcast!(
|
||||
"firehose",
|
||||
"comment:update",
|
||||
PhilomenaWeb.Api.Json.CommentView.render("show.json", %{comment: comment})
|
||||
)
|
||||
|
||||
Comments.reindex_comment(comment)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Comment updated successfully.")
|
||||
|> redirect(to: ~p"/images/#{conn.assigns.image}" <> "#comment_#{comment.id}")
|
||||
|
||||
{:error, :comment, changeset, _changes} ->
|
||||
{:error, changeset} ->
|
||||
render(conn, "edit.html", comment: conn.assigns.comment, changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
defmodule PhilomenaWeb.Topic.Post.HistoryController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.Versions.Version
|
||||
alias Philomena.Versions
|
||||
alias PhilomenaWeb.MarkdownRenderer
|
||||
alias Philomena.Forums.Forum
|
||||
alias Philomena.Repo
|
||||
import Ecto.Query
|
||||
alias Philomena.Posts
|
||||
|
||||
plug PhilomenaWeb.CanaryMapPlug, index: :show
|
||||
|
||||
|
@ -21,14 +19,8 @@ defmodule PhilomenaWeb.Topic.Post.HistoryController do
|
|||
def index(conn, _params) do
|
||||
topic = conn.assigns.topic
|
||||
post = conn.assigns.post
|
||||
|
||||
versions =
|
||||
Version
|
||||
|> where(item_type: "Post", item_id: ^post.id)
|
||||
|> order_by(desc: :created_at)
|
||||
|> limit(25)
|
||||
|> Repo.all()
|
||||
|> Versions.load_data_and_associations(post)
|
||||
renderer = &MarkdownRenderer.render_collection(&1, conn)
|
||||
versions = Posts.list_post_versions(post, renderer, conn.assigns.scrivener)
|
||||
|
||||
render(conn, "index.html",
|
||||
title: "Post History for Post #{post.id} - #{topic.title} - Forums",
|
||||
|
|
|
@ -79,11 +79,7 @@ defmodule PhilomenaWeb.Topic.PostController do
|
|||
user = conn.assigns.current_user
|
||||
|
||||
case Posts.update_post(post, user, post_params) do
|
||||
{:ok, %{post: post}} ->
|
||||
if not post.approved do
|
||||
Posts.report_non_approved(post)
|
||||
end
|
||||
|
||||
{:ok, post} ->
|
||||
conn
|
||||
|> put_flash(:info, "Post successfully edited.")
|
||||
|> redirect(
|
||||
|
@ -92,7 +88,7 @@ defmodule PhilomenaWeb.Topic.PostController do
|
|||
"#post_#{post.id}"
|
||||
)
|
||||
|
||||
{:error, :post, changeset, _changes} ->
|
||||
{:error, changeset} ->
|
||||
render(conn, "edit.html", post: conn.assigns.post, changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,46 +1,49 @@
|
|||
h1
|
||||
' Viewing last 25 versions of comment by
|
||||
' Viewing history for comment by
|
||||
= render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @comment, conn: @conn
|
||||
' on image
|
||||
a href=~p"/images/#{@comment.image}"
|
||||
| #
|
||||
= @comment.image_id
|
||||
|
||||
= for version <- @versions do
|
||||
h2 Current version
|
||||
= render PhilomenaWeb.CommentView, "_comment.html", comment: @comment, body: @body, conn: @conn
|
||||
|
||||
h2 Previous versions
|
||||
= for {version, body} <- @versions do
|
||||
- comment = merge_version(@comment, version)
|
||||
|
||||
article.block.communication
|
||||
.block__content.flex.flex--no-wrap
|
||||
.flex__fixed.spacing-right
|
||||
= render PhilomenaWeb.UserAttributionView, "_anon_user_avatar.html", object: @comment, conn: @conn
|
||||
= 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
|
||||
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
|
||||
= render PhilomenaWeb.UserAttributionView, "_anon_user_title.html", object: comment, conn: @conn
|
||||
|
||||
.communication__body__text
|
||||
= for edit <- version.difference do
|
||||
= case edit do
|
||||
- {:eq, value} ->
|
||||
= escape_nl2br(value)
|
||||
|
||||
- {:ins, value} ->
|
||||
ins.differ = escape_nl2br(value)
|
||||
|
||||
- {:del, value} ->
|
||||
del.differ = escape_nl2br(value)
|
||||
= body
|
||||
|
||||
.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
|
||||
= cond do
|
||||
- version.index > 1 and comment.edit_reason not in [nil, ""] ->
|
||||
' Reason:
|
||||
= comment.edit_reason
|
||||
- version.index > 1 ->
|
||||
' No reason given
|
||||
- true ->
|
||||
' Original
|
||||
|
||||
.flex__right
|
||||
' Edited
|
||||
= if version.index == 1 do
|
||||
' Created
|
||||
- else
|
||||
' Edited
|
||||
=> pretty_time(version.created_at)
|
||||
' by
|
||||
=> render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: version, conn: @conn
|
||||
=> render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: comment, conn: @conn
|
||||
|
|
|
@ -5,7 +5,7 @@ h1
|
|||
a href=(~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}?#{[post_id: @post.id]}" <> "#post_#{@post.id}")
|
||||
= @post.topic.title
|
||||
|
||||
= for version <- @versions do
|
||||
/= for version <- @versions do
|
||||
article.block.communication
|
||||
.block__content.flex.flex--no-wrap
|
||||
.flex__fixed.spacing-right
|
||||
|
@ -31,10 +31,11 @@ h1
|
|||
|
||||
.block__content.communication__options
|
||||
.flex.flex--wrap.flex--spaced-out
|
||||
- edit_reason = version.previous_version.edit_reason
|
||||
div
|
||||
= if version.edit_reason not in [nil, ""] do
|
||||
= if edit_reason not in [nil, ""] do
|
||||
' Reason:
|
||||
= version.edit_reason
|
||||
= edit_reason
|
||||
- else
|
||||
' No reason given
|
||||
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
defmodule PhilomenaWeb.Image.Comment.HistoryView do
|
||||
use PhilomenaWeb, :view
|
||||
|
||||
def merge_version(comment, version) do
|
||||
comment
|
||||
|> Map.put(:body, version.body)
|
||||
|> Map.put(:edited_at, version.created_at)
|
||||
|> Map.put(:edit_reason, version.edit_reason)
|
||||
end
|
||||
end
|
||||
|
|
72
priv/repo/migrations/20240731132924_new_versions.exs
Normal file
72
priv/repo/migrations/20240731132924_new_versions.exs
Normal file
|
@ -0,0 +1,72 @@
|
|||
defmodule Philomena.Repo.Migrations.NewVersions do
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
create table(:comment_versions) do
|
||||
add :comment_id, references(:comments, on_delete: :delete_all), null: false
|
||||
add :user_id, references(:users, on_delete: :delete_all), null: false
|
||||
timestamps inserted_at: :created_at, updated_at: false
|
||||
|
||||
add :body, :string, null: false
|
||||
add :edit_reason, :string
|
||||
end
|
||||
|
||||
create table(:post_versions) do
|
||||
add :post_id, references(:posts, on_delete: :delete_all), null: false
|
||||
add :user_id, references(:users, on_delete: :delete_all), null: false
|
||||
timestamps inserted_at: :created_at, updated_at: false
|
||||
|
||||
add :body, :string, null: false
|
||||
add :edit_reason, :string
|
||||
end
|
||||
|
||||
create index(:comment_versions, [:comment_id, "created_at desc"])
|
||||
create index(:comment_versions, [:user_id])
|
||||
create index(:post_versions, [:post_id, "created_at desc"])
|
||||
create index(:post_versions, [:user_id])
|
||||
|
||||
insert_statements =
|
||||
"""
|
||||
insert into comment_versions (comment_id, user_id, created_at, body, edit_reason)
|
||||
select
|
||||
v.item_id as comment_id,
|
||||
v.whodunnit::bigint as user_id,
|
||||
v.created_at,
|
||||
v.object::json->>'body' as body,
|
||||
v.object::json->>'edit_reason' as edit_reason
|
||||
from versions v
|
||||
where v.item_type = 'Comment'
|
||||
and exists(select 1 from comments c where c.id = v.item_id)
|
||||
and v.whodunnit is not null
|
||||
and v.event = 'update'
|
||||
order by created_at asc;
|
||||
|
||||
insert into post_versions (post_id, user_id, created_at, body, edit_reason)
|
||||
select
|
||||
v.item_id as post_id,
|
||||
v.whodunnit::bigint as user_id,
|
||||
v.created_at,
|
||||
v.object::json->>'body' as body,
|
||||
v.object::json->>'edit_reason' as edit_reason
|
||||
from versions v
|
||||
where v.item_type = 'Post'
|
||||
and exists(select 1 from posts p where p.id = v.item_id)
|
||||
and v.whodunnit is not null
|
||||
and v.event = 'update'
|
||||
order by created_at asc;
|
||||
"""
|
||||
|
||||
# These statements should not be run by the migration in production.
|
||||
# Run them manually in psql instead.
|
||||
if System.get_env("MIX_ENV") != "prod" do
|
||||
for stmt <- String.split(insert_statements, "\n\n") do
|
||||
execute(stmt)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
drop table(:comment_versions)
|
||||
drop table(:post_versions)
|
||||
end
|
||||
end
|
|
@ -272,6 +272,39 @@ CREATE SEQUENCE public.channels_id_seq
|
|||
ALTER SEQUENCE public.channels_id_seq OWNED BY public.channels.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: comment_versions; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public.comment_versions (
|
||||
id bigint NOT NULL,
|
||||
comment_id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
created_at timestamp(0) without time zone NOT NULL,
|
||||
body character varying(255) NOT NULL,
|
||||
edit_reason character varying(255)
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: comment_versions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.comment_versions_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: comment_versions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.comment_versions_id_seq OWNED BY public.comment_versions.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: comments; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -1337,6 +1370,39 @@ CREATE SEQUENCE public.polls_id_seq
|
|||
ALTER SEQUENCE public.polls_id_seq OWNED BY public.polls.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_versions; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public.post_versions (
|
||||
id bigint NOT NULL,
|
||||
post_id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
created_at timestamp(0) without time zone NOT NULL,
|
||||
body character varying(255) NOT NULL,
|
||||
edit_reason character varying(255)
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_versions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.post_versions_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_versions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.post_versions_id_seq OWNED BY public.post_versions.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: posts; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -2267,6 +2333,13 @@ ALTER TABLE ONLY public.badges ALTER COLUMN id SET DEFAULT nextval('public.badge
|
|||
ALTER TABLE ONLY public.channels ALTER COLUMN id SET DEFAULT nextval('public.channels_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: comment_versions id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.comment_versions ALTER COLUMN id SET DEFAULT nextval('public.comment_versions_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: comments id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -2428,6 +2501,13 @@ ALTER TABLE ONLY public.poll_votes ALTER COLUMN id SET DEFAULT nextval('public.p
|
|||
ALTER TABLE ONLY public.polls ALTER COLUMN id SET DEFAULT nextval('public.polls_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_versions id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.post_versions ALTER COLUMN id SET DEFAULT nextval('public.post_versions_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: posts id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -2615,6 +2695,14 @@ ALTER TABLE ONLY public.channels
|
|||
ADD CONSTRAINT channels_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: comment_versions comment_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.comment_versions
|
||||
ADD CONSTRAINT comment_versions_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: comments comments_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -2799,6 +2887,14 @@ ALTER TABLE ONLY public.polls
|
|||
ADD CONSTRAINT polls_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_versions post_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.post_versions
|
||||
ADD CONSTRAINT post_versions_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: posts posts_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -3003,6 +3099,20 @@ CREATE INDEX channel_live_notifications_user_id_read_index ON public.channel_liv
|
|||
CREATE INDEX channel_live_notifications_user_id_updated_at_desc_index ON public.channel_live_notifications USING btree (user_id, updated_at DESC);
|
||||
|
||||
|
||||
--
|
||||
-- Name: comment_versions_comment_id_created_at_desc_index; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX comment_versions_comment_id_created_at_desc_index ON public.comment_versions USING btree (comment_id, created_at DESC);
|
||||
|
||||
|
||||
--
|
||||
-- Name: comment_versions_user_id_index; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX comment_versions_user_id_index ON public.comment_versions USING btree (user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: forum_post_notifications_post_id_index; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -4424,6 +4534,20 @@ CREATE INDEX moderation_logs_user_id_created_at_index ON public.moderation_logs
|
|||
CREATE INDEX moderation_logs_user_id_index ON public.moderation_logs USING btree (user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_versions_post_id_created_at_desc_index; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX post_versions_post_id_created_at_desc_index ON public.post_versions USING btree (post_id, created_at DESC);
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_versions_user_id_index; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX post_versions_user_id_index ON public.post_versions USING btree (user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: reports_system_index; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -4461,6 +4585,22 @@ ALTER TABLE ONLY public.channel_live_notifications
|
|||
ADD CONSTRAINT channel_live_notifications_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: comment_versions comment_versions_comment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.comment_versions
|
||||
ADD CONSTRAINT comment_versions_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES public.comments(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: comment_versions comment_versions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.comment_versions
|
||||
ADD CONSTRAINT comment_versions_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: channels fk_rails_021c624081; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -5389,6 +5529,22 @@ ALTER TABLE ONLY public.moderation_logs
|
|||
ADD CONSTRAINT moderation_logs_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_versions post_versions_post_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.post_versions
|
||||
ADD CONSTRAINT post_versions_post_id_fkey FOREIGN KEY (post_id) REFERENCES public.posts(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: post_versions post_versions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.post_versions
|
||||
ADD CONSTRAINT post_versions_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: source_changes source_changes_image_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -5447,3 +5603,4 @@ INSERT INTO public."schema_migrations" (version) VALUES (20211219194836);
|
|||
INSERT INTO public."schema_migrations" (version) VALUES (20220321173359);
|
||||
INSERT INTO public."schema_migrations" (version) VALUES (20240723122759);
|
||||
INSERT INTO public."schema_migrations" (version) VALUES (20240728191353);
|
||||
INSERT INTO public."schema_migrations" (version) VALUES (20240731132924);
|
||||
|
|
Loading…
Reference in a new issue