forked from e621ng/e621ng
Add simple versioning replacement
This replaces the old simple versioning helper that existed on e621. The implementaiton is roughly the same but has been updated to use newer rails methods. No, it's not very flexible, but it didn't need to be.
This commit is contained in:
parent
0006ded1b9
commit
06db14aceb
14
app/controllers/edit_histories_controller.rb
Normal file
14
app/controllers/edit_histories_controller.rb
Normal file
@ -0,0 +1,14 @@
|
||||
class EditHistoriesController < ApplicationController
|
||||
respond_to :html
|
||||
before_action :moderator_only
|
||||
|
||||
def index
|
||||
@edit_history = EditHistory.includes(:user).paginate(params[:page], limit: params[:limit])
|
||||
respond_with(@edit_history)
|
||||
end
|
||||
|
||||
def show
|
||||
@edits = EditHistoryDecorator.decorate_collection(EditHistory.includes(:user).where('versionable_id = ? AND versionable_type = ?', params[:id], params[:type]).order(:id))
|
||||
respond_with(@edits)
|
||||
end
|
||||
end
|
58
app/decorators/edit_history_decorator.rb
Normal file
58
app/decorators/edit_history_decorator.rb
Normal file
@ -0,0 +1,58 @@
|
||||
class EditHistoryDecorator < ApplicationDecorator
|
||||
def self.collection_decorator_class
|
||||
PaginatedDecorator
|
||||
end
|
||||
|
||||
delegate_all
|
||||
|
||||
def diff(other)
|
||||
pattern = Regexp.new('(?:<.+?>)|(?:\w+)|(?:[ \t]+)|(?:\r?\n)|(?:.+?)')
|
||||
|
||||
thisarr = other.body.scan(pattern)
|
||||
otharr = object.body.scan(pattern)
|
||||
|
||||
cbo = Diff::LCS::ContextDiffCallbacks.new
|
||||
diffs = thisarr.diff(otharr, cbo)
|
||||
|
||||
escape_html = ->(str) {str.gsub(/&/, '&').gsub(/</, '<').gsub(/>/, '>')}
|
||||
|
||||
output = thisarr
|
||||
output.each {|q| q.replace(escape_html[q])}
|
||||
|
||||
diffs.reverse_each do |hunk|
|
||||
newchange = hunk.max {|a, b| a.old_position <=> b.old_position}
|
||||
newstart = newchange.old_position
|
||||
oldstart = hunk.min {|a, b| a.old_position <=> b.old_position}.old_position
|
||||
|
||||
if newchange.action == '+'
|
||||
output.insert(newstart, '</ins>')
|
||||
end
|
||||
|
||||
hunk.reverse_each do |chg|
|
||||
case chg.action
|
||||
when '-'
|
||||
oldstart = chg.old_position
|
||||
output[chg.old_position] = '<br>' if chg.old_element.match(/^\r?\n$/)
|
||||
when '+'
|
||||
if chg.new_element.match(/^\r?\n$/)
|
||||
output.insert(chg.old_position, '<br>')
|
||||
else
|
||||
output.insert(chg.old_position, "#{escape_html[chg.new_element]}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if newchange.action == '+'
|
||||
output.insert(newstart, '<ins>')
|
||||
end
|
||||
|
||||
if hunk[0].action == '-'
|
||||
output.insert((newstart == oldstart || newchange.action != '+') ? newstart + 1 : newstart, '</del>')
|
||||
output.insert(oldstart, '<del>')
|
||||
end
|
||||
end
|
||||
|
||||
output.join.gsub(/\r?\n/, '<br>').html_safe
|
||||
end
|
||||
|
||||
end
|
20
app/javascript/src/styles/specific/edit_history.scss
Normal file
20
app/javascript/src/styles/specific/edit_history.scss
Normal file
@ -0,0 +1,20 @@
|
||||
@import "../base/000_vars.scss";
|
||||
|
||||
#c-edit-history {
|
||||
.edit-item {
|
||||
display: flex;
|
||||
&>div {
|
||||
margin-right: 1em;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
ins {
|
||||
background-color: $success_color;
|
||||
font-weight: bold;
|
||||
}
|
||||
del {
|
||||
background-color: $error_color;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
@ -241,6 +241,60 @@ class ApplicationRecord < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
concerning :SimpleVersioningMethods do
|
||||
class_methods do
|
||||
def simple_versioning(options = {})
|
||||
cattr_accessor :versioning_body_column, :versioning_ip_column, :versioning_user_column, :versioning_subject_column
|
||||
self.versioning_body_column = options[:body_column] || "body"
|
||||
self.versioning_subject_column = options[:subject_column]
|
||||
self.versioning_ip_column = options[:ip_column] || "creator_ip_addr"
|
||||
self.versioning_user_column = options[:user_column] || "creator_id"
|
||||
|
||||
class_eval do
|
||||
has_many :versions, class_name: 'EditHistory', as: :versionable
|
||||
after_update :save_version, if: :should_version_change
|
||||
|
||||
define_method :should_version_change do
|
||||
if self.versioning_subject_column
|
||||
return true if send "saved_change_to_#{self.versioning_subject_column}?"
|
||||
end
|
||||
send "saved_change_to_#{self.versioning_body_column}?"
|
||||
end
|
||||
|
||||
define_method :save_version do
|
||||
EditHistory.transaction do
|
||||
our_next_version = next_version
|
||||
if our_next_version == 0
|
||||
our_next_version += 1
|
||||
new = EditHistory.new
|
||||
new.versionable = self
|
||||
new.version = 1
|
||||
new.ip_addr = self.send self.versioning_ip_column
|
||||
new.body = self.send "#{self.versioning_body_column}_before_last_save"
|
||||
new.user_id = self.send self.versioning_user_column
|
||||
new.subject = self.send "#{self.versioning_subject_column}_before_last_save" if self.versioning_subject_column
|
||||
new.created_at = self.created_at
|
||||
new.save
|
||||
end
|
||||
|
||||
version = EditHistory.new
|
||||
version.version = our_next_version + 1
|
||||
version.versionable = self
|
||||
version.ip_addr = CurrentUser.ip_addr
|
||||
version.body = self.send self.versioning_body_column
|
||||
version.user_id = CurrentUser.id
|
||||
version.save
|
||||
end
|
||||
end
|
||||
|
||||
define_method :next_version do
|
||||
versions.count
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
concerning :UserMethods do
|
||||
class_methods do
|
||||
def belongs_to_creator(options = {})
|
||||
|
@ -1,4 +1,5 @@
|
||||
class Blip < ApplicationRecord
|
||||
simple_versioning
|
||||
belongs_to_creator
|
||||
validates_presence_of :body
|
||||
belongs_to :parent, class_name: "Blip", foreign_key: "response_to", optional: true
|
||||
|
@ -1,6 +1,7 @@
|
||||
class Comment < ApplicationRecord
|
||||
include Mentionable
|
||||
|
||||
simple_versioning
|
||||
validate :validate_post_exists, :on => :create
|
||||
validate :validate_creator_is_not_limited, :on => :create
|
||||
validates_presence_of :body, :message => "has no content"
|
||||
|
14
app/models/edit_history.rb
Normal file
14
app/models/edit_history.rb
Normal file
@ -0,0 +1,14 @@
|
||||
class EditHistory < ApplicationRecord
|
||||
self.table_name = 'edit_histories'
|
||||
belongs_to :versionable, polymorphic: true
|
||||
belongs_to :user
|
||||
|
||||
attr_accessor :difference
|
||||
|
||||
|
||||
TYPE_MAP = {
|
||||
comment: 'Comment',
|
||||
forum: 'ForumPost',
|
||||
blip: 'Blip'
|
||||
}
|
||||
end
|
@ -1,6 +1,7 @@
|
||||
class ForumPost < ApplicationRecord
|
||||
include Mentionable
|
||||
|
||||
simple_versioning
|
||||
attr_readonly :topic_id
|
||||
belongs_to_creator
|
||||
belongs_to_updater
|
||||
@ -160,7 +161,7 @@ class ForumPost < ApplicationRecord
|
||||
end
|
||||
|
||||
def category_allows_replies
|
||||
if topic && !topic.can_rely?(creator)
|
||||
if topic && !topic.can_reply?(creator)
|
||||
errors[:topic] << "does not allow replies"
|
||||
return false
|
||||
end
|
||||
|
@ -53,6 +53,7 @@
|
||||
|
||||
<% if CurrentUser.is_moderator? %>
|
||||
<li>|</li>
|
||||
<li><%= link_to "Show Edits", edit_history_path(id: blip.id, type: 'Blip') %></li>
|
||||
<li>
|
||||
<strong>IP</strong>
|
||||
<span><%= link_to_ip blip.creator_ip_addr %></span>
|
||||
|
@ -40,6 +40,7 @@
|
||||
<li><%= link_to "Report", new_ticket_path(disp_id: comment.id, type: 'comment') %></li>
|
||||
<% if CurrentUser.is_moderator? %>
|
||||
<li>|</li>
|
||||
<li><%= link_to "Show Edits", edit_history_path(id: comment.id, type: 'Comment') %></li>
|
||||
<li>
|
||||
<strong>IP</strong>
|
||||
<span><%= link_to_ip comment.creator_ip_addr %></span>
|
||||
|
37
app/views/edit_histories/index.html.erb
Normal file
37
app/views/edit_histories/index.html.erb
Normal file
@ -0,0 +1,37 @@
|
||||
<div id="c-edit-history">
|
||||
<div id="a-index">
|
||||
<h1>Recent Edits</h1>
|
||||
|
||||
<table class='striped' style='width:100%;'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Type</th>
|
||||
<th>Date</th>
|
||||
<th>IP Address</th>
|
||||
<th>Editor</th>
|
||||
<th>Body</th>
|
||||
<th>Subject</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% @edit_history.each do |edit| %>
|
||||
<tr id="edit-<%= edit.id %>">
|
||||
<td><%= link_to "Show", action: "show", id: edit.versionable_id, type: edit.versionable_type %></td>
|
||||
<td><%= edit.versionable_type %></td>
|
||||
<td><%= edit.updated_at.strftime("%b %d, %Y %I:%M %p") %></td>
|
||||
<td><%= link_to_ip edit.ip_addr %></td>
|
||||
<td><%= link_to_user edit.user %></td>
|
||||
<td><%= edit.body[0..30] %></td>
|
||||
<td><%= edit.subject&[0..30] %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id="paginator">
|
||||
<%= numbered_paginator(@edit_history) %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
26
app/views/edit_histories/show.html.erb
Normal file
26
app/views/edit_histories/show.html.erb
Normal file
@ -0,0 +1,26 @@
|
||||
<div id="c-edit-history">
|
||||
<div id="a-show">
|
||||
<h1>Edits for <%= h params[:type] %> #<%= h params[:id] %></h1>
|
||||
|
||||
<div class="response-list" id="edit-history">
|
||||
<% @edits.each_with_index do |edit, idx| %>
|
||||
<div class="edit-item box-section">
|
||||
<div class="author">
|
||||
<h6><%= link_to_user edit.user %></h6>
|
||||
<span class="date" title="<%= time_ago_in_words(edit.created_at) + " ago" %>"><%= edit.created_at.strftime("%b %d, %Y %I:%M %p") %></span>
|
||||
<div><%= link_to_ip edit.ip_addr %></div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="body">
|
||||
<% if edit.version > 1 %>
|
||||
<%= edit.diff(@edits[idx-1]) %>
|
||||
<% else %>
|
||||
<%= edit.body %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -40,6 +40,11 @@
|
||||
<% if CurrentUser.is_member? %>
|
||||
<li><%= link_to "Report", new_ticket_path(disp_id: forum_post.id, type: 'forum') %></li>
|
||||
<% end %>
|
||||
<% if CurrentUser.is_moderator? %>
|
||||
<li>|</li>
|
||||
<li><%= link_to "Show Edits", edit_history_path(id: forum_post.id, type: 'ForumPost') %></li>
|
||||
<li>|</li>
|
||||
<% end %>
|
||||
<% if params[:controller] == "forum_posts" %>
|
||||
<li><%= link_to "Parent", forum_topic_path(forum_post.topic, :page => forum_post.forum_topic_page, :anchor => "forum_post_#{forum_post.id}") %></li>
|
||||
<% else %>
|
||||
|
@ -10,6 +10,7 @@ Rails.application.routes.draw do
|
||||
resource :alias_and_implication_import, :only => [:new, :create]
|
||||
resource :dashboard, :only => [:show]
|
||||
end
|
||||
resources :edit_histories
|
||||
namespace :moderator do
|
||||
resource :bulk_revert, :only => [:new, :create]
|
||||
resource :dashboard, :only => [:show]
|
||||
|
19
db/migrate/20190427163107_create_edit_histories.rb
Normal file
19
db/migrate/20190427163107_create_edit_histories.rb
Normal file
@ -0,0 +1,19 @@
|
||||
class CreateEditHistories < ActiveRecord::Migration[5.2]
|
||||
def self.up
|
||||
create_table :edit_histories do |t|
|
||||
t.timestamps
|
||||
t.text :body, null: false
|
||||
t.text :subject, null: true
|
||||
t.string :versionable_type, limit: 100, null: false
|
||||
t.integer :versionable_id, null: false
|
||||
t.integer :version, null: false
|
||||
t.column :ip_addr, "inet", null: false
|
||||
t.integer :user_id, index: true, null: false
|
||||
t.index [:versionable_id, :versionable_type]
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :edit_histories
|
||||
end
|
||||
end
|
8
db/migrate/20190427181805_add_ip_to_forums.rb
Normal file
8
db/migrate/20190427181805_add_ip_to_forums.rb
Normal file
@ -0,0 +1,8 @@
|
||||
class AddIpToForums < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
ForumPost.without_timeout do
|
||||
add_column :forum_posts, :creator_ip_addr, :inet
|
||||
add_column :forum_topics, :creator_ip_addr, :inet
|
||||
end
|
||||
end
|
||||
end
|
@ -779,6 +779,43 @@ CREATE SEQUENCE public.dmails_id_seq
|
||||
ALTER SEQUENCE public.dmails_id_seq OWNED BY public.dmails.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: edit_histories; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public.edit_histories (
|
||||
id bigint NOT NULL,
|
||||
created_at timestamp without time zone NOT NULL,
|
||||
updated_at timestamp without time zone NOT NULL,
|
||||
body text NOT NULL,
|
||||
subject text,
|
||||
versionable_type character varying(100) NOT NULL,
|
||||
versionable_id integer NOT NULL,
|
||||
version integer NOT NULL,
|
||||
ip_addr inet NOT NULL,
|
||||
user_id integer NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: edit_histories_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.edit_histories_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: edit_histories_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.edit_histories_id_seq OWNED BY public.edit_histories.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: email_blacklists; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
@ -958,7 +995,8 @@ CREATE TABLE public.forum_posts (
|
||||
text_index tsvector NOT NULL,
|
||||
is_deleted boolean DEFAULT false NOT NULL,
|
||||
created_at timestamp without time zone,
|
||||
updated_at timestamp without time zone
|
||||
updated_at timestamp without time zone,
|
||||
creator_ip_addr inet
|
||||
);
|
||||
|
||||
|
||||
@ -1066,7 +1104,8 @@ CREATE TABLE public.forum_topics (
|
||||
created_at timestamp without time zone,
|
||||
updated_at timestamp without time zone,
|
||||
category_id integer DEFAULT 0 NOT NULL,
|
||||
min_level integer DEFAULT 0 NOT NULL
|
||||
min_level integer DEFAULT 0 NOT NULL,
|
||||
creator_ip_addr inet
|
||||
);
|
||||
|
||||
|
||||
@ -2649,6 +2688,13 @@ ALTER TABLE ONLY public.dmail_filters ALTER COLUMN id SET DEFAULT nextval('publi
|
||||
ALTER TABLE ONLY public.dmails ALTER COLUMN id SET DEFAULT nextval('public.dmails_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: edit_histories id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.edit_histories ALTER COLUMN id SET DEFAULT nextval('public.edit_histories_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: email_blacklists id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
@ -3116,6 +3162,14 @@ ALTER TABLE ONLY public.dmails
|
||||
ADD CONSTRAINT dmails_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: edit_histories edit_histories_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.edit_histories
|
||||
ADD CONSTRAINT edit_histories_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: email_blacklists email_blacklists_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
@ -3778,6 +3832,20 @@ CREATE INDEX index_dmails_on_message_index ON public.dmails USING gin (message_i
|
||||
CREATE INDEX index_dmails_on_owner_id ON public.dmails USING btree (owner_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_edit_histories_on_user_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX index_edit_histories_on_user_id ON public.edit_histories USING btree (user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_edit_histories_on_versionable_id_and_versionable_type; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX index_edit_histories_on_versionable_id_and_versionable_type ON public.edit_histories USING btree (versionable_id, versionable_type);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_favorite_groups_on_creator_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
@ -4810,6 +4878,8 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20190409195837'),
|
||||
('20190410022203'),
|
||||
('20190413055451'),
|
||||
('20190418093745');
|
||||
('20190418093745'),
|
||||
('20190427163107'),
|
||||
('20190427181805');
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user