This commit is contained in:
Liam 2024-07-31 10:37:35 -04:00
parent f91d9f2143
commit 4d7f229641
17 changed files with 474 additions and 204 deletions

View file

@ -10,13 +10,13 @@ defmodule Philomena.Comments do
alias PhilomenaQuery.Search alias PhilomenaQuery.Search
alias Philomena.UserStatistics alias Philomena.UserStatistics
alias Philomena.Comments.Comment alias Philomena.Comments.Comment
alias Philomena.Comments.Version
alias Philomena.Comments.SearchIndex, as: CommentIndex alias Philomena.Comments.SearchIndex, as: CommentIndex
alias Philomena.IndexWorker alias Philomena.IndexWorker
alias Philomena.Images.Image alias Philomena.Images.Image
alias Philomena.Images alias Philomena.Images
alias Philomena.Notifications alias Philomena.Notifications
alias Philomena.NotificationWorker alias Philomena.NotificationWorker
alias Philomena.Versions
alias Philomena.Reports alias Philomena.Reports
@doc """ @doc """
@ -93,21 +93,46 @@ defmodule Philomena.Comments do
""" """
def update_comment(%Comment{} = comment, editor, attrs) do def update_comment(%Comment{} = comment, editor, attrs) do
now = DateTime.utc_now(:second) version_changeset =
current_body = comment.body Version.changeset(%Version{}, comment, editor, %{
current_reason = comment.edit_reason 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.new()
|> Multi.update(:comment, comment_changes) |> Multi.insert(:version, version_changeset)
|> Multi.run(:version, fn _repo, _changes -> |> Multi.update(:comment, comment_changeset)
Versions.create_version("Comment", comment.id, editor.id, %{
"body" => current_body,
"edit_reason" => current_reason
})
end)
|> Repo.transaction() |> 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 end
@doc """ @doc """

View 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

View file

@ -12,12 +12,12 @@ defmodule Philomena.Posts do
alias Philomena.Topics alias Philomena.Topics
alias Philomena.UserStatistics alias Philomena.UserStatistics
alias Philomena.Posts.Post alias Philomena.Posts.Post
alias Philomena.Posts.Version
alias Philomena.Posts.SearchIndex, as: PostIndex alias Philomena.Posts.SearchIndex, as: PostIndex
alias Philomena.IndexWorker alias Philomena.IndexWorker
alias Philomena.Forums.Forum alias Philomena.Forums.Forum
alias Philomena.Notifications alias Philomena.Notifications
alias Philomena.NotificationWorker alias Philomena.NotificationWorker
alias Philomena.Versions
alias Philomena.Reports alias Philomena.Reports
@doc """ @doc """
@ -142,32 +142,46 @@ defmodule Philomena.Posts do
""" """
def update_post(%Post{} = post, editor, attrs) do def update_post(%Post{} = post, editor, attrs) do
now = DateTime.utc_now(:second) version_changeset =
current_body = post.body Version.changeset(%Version{}, post, editor, %{
current_reason = post.edit_reason 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.new()
|> Multi.update(:post, post_changes) |> Multi.insert(:version, version_changeset)
|> Multi.run(:version, fn _repo, _changes -> |> Multi.update(:post, post_changeset)
Versions.create_version("Post", post.id, editor.id, %{
"body" => current_body,
"edit_reason" => current_reason
})
end)
|> Repo.transaction() |> Repo.transaction()
|> case do |> case do
{:ok, %{post: post}} = result -> {:ok, %{post: post}} ->
if not post.approved do
report_non_approved(post)
end
reindex_post(post) reindex_post(post)
result {:ok, post}
error -> _ ->
error {:error, post_changeset}
end end
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 """ @doc """
Deletes a Post. Deletes a Post.

View 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

View file

@ -5,75 +5,77 @@ defmodule Philomena.Versions do
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias Philomena.Repo alias Philomena.Repo
alias Philomena.Versions.Difference
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)
@doc """ @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 ## Examples
iex> create_version(%{field: value}) iex> compute_text_differences(CommentVersion, %Comment{}, :body, page: 1, page_size: 25)
{:ok, %Version{}} %Scrivener.Page{}
iex> create_version(%{field: bad_value})
{:error, %Ecto.Changeset{}}
""" """
def create_version(item_type, item_id, whodunnit, attrs \\ %{}) do def compute_text_differences(query, parent, name, pagination) do
%Version{ page = Repo.paginate(preload_and_order(query), pagination)
item_type: item_type, initial = get_comparison_version(query, parent, page)
item_id: item_id,
event: "update", {differences, _prev} =
whodunnit: whodunnit(whodunnit) Enum.map_reduce(page, initial, fn older, newer ->
} d = %Difference{
|> Version.changeset(attrs, item_id) previous_version: newer,
|> Repo.insert() created_at: older.created_at,
parent: parent,
user: older.user,
difference: difference(older, newer, name)
}
{d, older}
end)
%{page | entries: differences}
end end
# revolver ocelot #
defp whodunnit(user_id) when is_integer(user_id), do: to_string(user_id) # Get the first version to use when reducing the list of differences.
defp whodunnit(nil), do: nil #
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 end

View file

@ -1,14 +1,14 @@
defimpl Philomena.Attribution, for: Philomena.Versions.Version do defimpl Philomena.Attribution, for: Philomena.Versions.Difference do
def object_identifier(version) do def object_identifier(difference) do
Philomena.Attribution.object_identifier(version.parent) Philomena.Attribution.object_identifier(difference.parent)
end end
def best_user_identifier(version) do def best_user_identifier(difference) do
Philomena.Attribution.best_user_identifier(version.parent) Philomena.Attribution.best_user_identifier(difference.parent)
end end
def anonymous?(version) do def anonymous?(difference) do
same_user?(version.user, version.parent) and !!version.parent.anonymous same_user?(difference.user, difference.parent) and !!difference.parent.anonymous
end end
defp same_user?(%{id: id}, %{user_id: id}), do: true defp same_user?(%{id: id}, %{user_id: id}), do: true

View 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

View file

@ -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

View file

@ -1,11 +1,9 @@
defmodule PhilomenaWeb.Image.Comment.HistoryController do defmodule PhilomenaWeb.Image.Comment.HistoryController do
use PhilomenaWeb, :controller use PhilomenaWeb, :controller
alias Philomena.Versions.Version alias PhilomenaWeb.MarkdownRenderer
alias Philomena.Versions
alias Philomena.Images.Image alias Philomena.Images.Image
alias Philomena.Repo alias Philomena.Comments
import Ecto.Query
plug PhilomenaWeb.CanaryMapPlug, index: :show plug PhilomenaWeb.CanaryMapPlug, index: :show
plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true 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 def index(conn, _params) do
image = conn.assigns.image image = conn.assigns.image
comment = conn.assigns.comment comment = conn.assigns.comment
renderer = &MarkdownRenderer.render_collection(&1, conn)
versions = versions = Comments.list_comment_versions(comment, renderer, conn.assigns.scrivener)
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", render(conn, "index.html",
title: "Comment History for comment #{comment.id} on image #{image.id}", title: "Comment History for comment #{comment.id} on image #{image.id}",
versions: versions versions: versions,
body: renderer.([comment])
) )
end end
end end

View file

@ -111,24 +111,18 @@ defmodule PhilomenaWeb.Image.CommentController do
def update(conn, %{"comment" => comment_params}) do def update(conn, %{"comment" => comment_params}) do
case Comments.update_comment(conn.assigns.comment, conn.assigns.current_user, comment_params) do case Comments.update_comment(conn.assigns.comment, conn.assigns.current_user, comment_params) do
{:ok, %{comment: comment}} -> {:ok, comment} ->
if not comment.approved do
Comments.report_non_approved(comment)
end
PhilomenaWeb.Endpoint.broadcast!( PhilomenaWeb.Endpoint.broadcast!(
"firehose", "firehose",
"comment:update", "comment:update",
PhilomenaWeb.Api.Json.CommentView.render("show.json", %{comment: comment}) PhilomenaWeb.Api.Json.CommentView.render("show.json", %{comment: comment})
) )
Comments.reindex_comment(comment)
conn conn
|> put_flash(:info, "Comment updated successfully.") |> put_flash(:info, "Comment updated successfully.")
|> redirect(to: ~p"/images/#{conn.assigns.image}" <> "#comment_#{comment.id}") |> 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) render(conn, "edit.html", comment: conn.assigns.comment, changeset: changeset)
end end
end end

View file

@ -1,11 +1,9 @@
defmodule PhilomenaWeb.Topic.Post.HistoryController do defmodule PhilomenaWeb.Topic.Post.HistoryController do
use PhilomenaWeb, :controller use PhilomenaWeb, :controller
alias Philomena.Versions.Version alias PhilomenaWeb.MarkdownRenderer
alias Philomena.Versions
alias Philomena.Forums.Forum alias Philomena.Forums.Forum
alias Philomena.Repo alias Philomena.Posts
import Ecto.Query
plug PhilomenaWeb.CanaryMapPlug, index: :show plug PhilomenaWeb.CanaryMapPlug, index: :show
@ -21,14 +19,8 @@ defmodule PhilomenaWeb.Topic.Post.HistoryController do
def index(conn, _params) do def index(conn, _params) do
topic = conn.assigns.topic topic = conn.assigns.topic
post = conn.assigns.post post = conn.assigns.post
renderer = &MarkdownRenderer.render_collection(&1, conn)
versions = versions = Posts.list_post_versions(post, renderer, conn.assigns.scrivener)
Version
|> where(item_type: "Post", item_id: ^post.id)
|> order_by(desc: :created_at)
|> limit(25)
|> Repo.all()
|> Versions.load_data_and_associations(post)
render(conn, "index.html", render(conn, "index.html",
title: "Post History for Post #{post.id} - #{topic.title} - Forums", title: "Post History for Post #{post.id} - #{topic.title} - Forums",

View file

@ -79,11 +79,7 @@ defmodule PhilomenaWeb.Topic.PostController do
user = conn.assigns.current_user user = conn.assigns.current_user
case Posts.update_post(post, user, post_params) do case Posts.update_post(post, user, post_params) do
{:ok, %{post: post}} -> {:ok, post} ->
if not post.approved do
Posts.report_non_approved(post)
end
conn conn
|> put_flash(:info, "Post successfully edited.") |> put_flash(:info, "Post successfully edited.")
|> redirect( |> redirect(
@ -92,7 +88,7 @@ defmodule PhilomenaWeb.Topic.PostController do
"#post_#{post.id}" "#post_#{post.id}"
) )
{:error, :post, changeset, _changes} -> {:error, changeset} ->
render(conn, "edit.html", post: conn.assigns.post, changeset: changeset) render(conn, "edit.html", post: conn.assigns.post, changeset: changeset)
end end
end end

View file

@ -1,46 +1,49 @@
h1 h1
' Viewing last 25 versions of comment by ' Viewing history for comment by
= render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @comment, conn: @conn = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @comment, conn: @conn
' on image ' on image
a href=~p"/images/#{@comment.image}" a href=~p"/images/#{@comment.image}"
| # | #
= @comment.image_id = @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 article.block.communication
.block__content.flex.flex--no-wrap .block__content.flex.flex--no-wrap
.flex__fixed.spacing-right .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 .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 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 .communication__body__text
= for edit <- version.difference do = body
= case edit do
- {:eq, value} ->
= escape_nl2br(value)
- {:ins, value} ->
ins.differ = escape_nl2br(value)
- {:del, value} ->
del.differ = escape_nl2br(value)
.block__content.communication__options .block__content.communication__options
.flex.flex--wrap.flex--spaced-out .flex.flex--wrap.flex--spaced-out
div div
= if version.edit_reason not in [nil, ""] do = cond do
' Reason: - version.index > 1 and comment.edit_reason not in [nil, ""] ->
= version.edit_reason ' Reason:
- else = comment.edit_reason
' No reason given - version.index > 1 ->
' No reason given
- true ->
' Original
.flex__right .flex__right
' Edited = if version.index == 1 do
' Created
- else
' Edited
=> pretty_time(version.created_at) => pretty_time(version.created_at)
' by ' by
=> render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: version, conn: @conn => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: comment, conn: @conn

View file

@ -5,7 +5,7 @@ h1
a href=(~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}?#{[post_id: @post.id]}" <> "#post_#{@post.id}") a href=(~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}?#{[post_id: @post.id]}" <> "#post_#{@post.id}")
= @post.topic.title = @post.topic.title
= for version <- @versions do /= for version <- @versions do
article.block.communication article.block.communication
.block__content.flex.flex--no-wrap .block__content.flex.flex--no-wrap
.flex__fixed.spacing-right .flex__fixed.spacing-right
@ -31,10 +31,11 @@ h1
.block__content.communication__options .block__content.communication__options
.flex.flex--wrap.flex--spaced-out .flex.flex--wrap.flex--spaced-out
- edit_reason = version.previous_version.edit_reason
div div
= if version.edit_reason not in [nil, ""] do = if edit_reason not in [nil, ""] do
' Reason: ' Reason:
= version.edit_reason = edit_reason
- else - else
' No reason given ' No reason given

View file

@ -1,3 +1,10 @@
defmodule PhilomenaWeb.Image.Comment.HistoryView do defmodule PhilomenaWeb.Image.Comment.HistoryView do
use PhilomenaWeb, :view 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 end

View 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

View file

@ -272,6 +272,39 @@ CREATE SEQUENCE public.channels_id_seq
ALTER SEQUENCE public.channels_id_seq OWNED BY public.channels.id; 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: - -- 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; 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: - -- 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); 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: - -- 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); 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: - -- Name: posts id; Type: DEFAULT; Schema: public; Owner: -
-- --
@ -2615,6 +2695,14 @@ ALTER TABLE ONLY public.channels
ADD CONSTRAINT channels_pkey PRIMARY KEY (id); 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: - -- 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); 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: - -- 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); 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: - -- 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); 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: - -- 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; 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: - -- 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; 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: - -- 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 (20220321173359);
INSERT INTO public."schema_migrations" (version) VALUES (20240723122759); INSERT INTO public."schema_migrations" (version) VALUES (20240723122759);
INSERT INTO public."schema_migrations" (version) VALUES (20240728191353); INSERT INTO public."schema_migrations" (version) VALUES (20240728191353);
INSERT INTO public."schema_migrations" (version) VALUES (20240731132924);