paper_trail_viewer 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/test.yml +43 -0
- data/.gitignore +41 -0
- data/Appraisals +13 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +23 -0
- data/README.md +53 -0
- data/Rakefile +45 -0
- data/app/controllers/paper_trail_viewer/js_controller.rb +14 -0
- data/app/controllers/paper_trail_viewer/versions_controller.rb +119 -0
- data/app/controllers/paper_trail_viewer/viewer_controller.rb +4 -0
- data/app/views/paper_trail_viewer/viewer/index.html.erb +5 -0
- data/bin/setup +8 -0
- data/config/routes.rb +5 -0
- data/gemfiles/rails_6.0_paper_trail_11.1.gemfile +10 -0
- data/gemfiles/rails_6.0_paper_trail_12.2.gemfile +10 -0
- data/gemfiles/rails_7.0_paper_trail_12.2.gemfile +10 -0
- data/javascript/compiled.js +2 -0
- data/javascript/src/app.tsx +100 -0
- data/javascript/src/components/change_diff.tsx +57 -0
- data/javascript/src/components/config_modal.tsx +22 -0
- data/javascript/src/components/controls.tsx +62 -0
- data/javascript/src/components/full_object_modal.tsx +21 -0
- data/javascript/src/components/index.ts +3 -0
- data/javascript/src/components/modal.tsx +34 -0
- data/javascript/src/components/pagination.tsx +53 -0
- data/javascript/src/components/versions_list.tsx +142 -0
- data/javascript/src/index.ts +1 -0
- data/javascript/src/types.ts +42 -0
- data/lib/paper_trail_viewer/data_source/active_record.rb +30 -0
- data/lib/paper_trail_viewer/data_source/bigquery.rb +45 -0
- data/lib/paper_trail_viewer/engine.rb +5 -0
- data/lib/paper_trail_viewer/version.rb +3 -0
- data/lib/paper_trail_viewer.rb +11 -0
- data/package.json +43 -0
- data/paper_trail_viewer.gemspec +34 -0
- data/spec/app_template.rb +19 -0
- data/spec/rails_helper.rb +18 -0
- data/spec/support/factories.rb +13 -0
- data/spec/system/paper_trail_viewer_spec.rb +114 -0
- data/tsconfig.json +13 -0
- data/webpack.config.js +26 -0
- metadata +251 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 06cbe395c995ac810d7164e0efcc15786a545fd5abe4277a6c2e800ed2cb0ef4
|
4
|
+
data.tar.gz: 991c4f1ed961a5d8a5c5967d3b14c78534cdb549d6d7a235e3e38de634797e14
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: af134d7971dff9b4d2344c534aa47f61e4bcd52fa06bcba99e7336db544a50aa0e0ac262db3e03b44830a2df7cf517e9aa9c44e9ce2912c37c0342bf18eaf29b
|
7
|
+
data.tar.gz: 8508f49cf1d1267b793f20917ec7f52363ce186898f6d8c36827e50619387bff0eecb3ee2f5a9bfe9c9d47c5974cc03cef2edd32958e6a4d082da91b16467f11
|
@@ -0,0 +1,43 @@
|
|
1
|
+
name: Tests
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
test:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
|
9
|
+
strategy:
|
10
|
+
fail-fast: false
|
11
|
+
matrix:
|
12
|
+
ruby:
|
13
|
+
- 2.7
|
14
|
+
- 3.0
|
15
|
+
gemfile:
|
16
|
+
- gemfiles/rails_6.0_paper_trail_11.1.gemfile
|
17
|
+
- gemfiles/rails_6.0_paper_trail_12.2.gemfile
|
18
|
+
- gemfiles/rails_7.0_paper_trail_12.2.gemfile
|
19
|
+
|
20
|
+
env:
|
21
|
+
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
22
|
+
|
23
|
+
steps:
|
24
|
+
- uses: actions/checkout@v2
|
25
|
+
- uses: actions/setup-ruby@v1
|
26
|
+
with:
|
27
|
+
ruby-version: ${{ matrix.ruby }}
|
28
|
+
- uses: actions/cache@v1
|
29
|
+
with:
|
30
|
+
path: vendor/bundle
|
31
|
+
key: ${{ runner.os }}-${{ matrix.ruby}}-gems-${{ hashFiles('**/paper_trail_viewer.gemspec') }}
|
32
|
+
restore-keys: |
|
33
|
+
${{ runner.os }}-${{ matrix.ruby }}-gems-
|
34
|
+
- uses: actions/setup-node@v2
|
35
|
+
with:
|
36
|
+
node-version: '16'
|
37
|
+
# - uses: nanasess/setup-chromedriver@v1.0.1
|
38
|
+
- name: Install dependencies
|
39
|
+
run: |
|
40
|
+
bundle install --jobs 4 --retry 3
|
41
|
+
npm install
|
42
|
+
- name: Build
|
43
|
+
run: bundle exec rake
|
data/.gitignore
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
*.a
|
2
|
+
*.bundle
|
3
|
+
*.gem
|
4
|
+
*.gemfile.lock
|
5
|
+
*.iml
|
6
|
+
*.o
|
7
|
+
*.so
|
8
|
+
*.stTheme.cache
|
9
|
+
*.sublime-project
|
10
|
+
*.sublime-workspace
|
11
|
+
*.swp
|
12
|
+
*.tmPreferences.cache
|
13
|
+
*.tmlanguage.cache
|
14
|
+
*~
|
15
|
+
.DS_Store
|
16
|
+
.byebug_history
|
17
|
+
.idea/
|
18
|
+
.rspec_status
|
19
|
+
.ruby-gemset
|
20
|
+
.ruby-version
|
21
|
+
.tags
|
22
|
+
.tags1
|
23
|
+
.tool-versions
|
24
|
+
.vscode
|
25
|
+
/.bundle/
|
26
|
+
/.yardoc
|
27
|
+
/_yardoc/
|
28
|
+
/coverage/
|
29
|
+
/doc/
|
30
|
+
/javascript/compiled*
|
31
|
+
/node_modules
|
32
|
+
/pkg/
|
33
|
+
/spec/reports/
|
34
|
+
/tmp/
|
35
|
+
Gemfile.lock
|
36
|
+
bbin/
|
37
|
+
binstubs/*
|
38
|
+
bundler_stubs/*/.yardoc
|
39
|
+
mkmf.log
|
40
|
+
package-lock.json
|
41
|
+
spec/dummy
|
data/Appraisals
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
{
|
2
|
+
'6.0' => %w[11.1 12.2],
|
3
|
+
'7.0' => %w[12.2],
|
4
|
+
}.each do |rails_version, paper_trail_versions|
|
5
|
+
paper_trail_versions.each do |paper_trail_version|
|
6
|
+
appraise "rails-#{rails_version}-paper_trail-#{paper_trail_version}" do
|
7
|
+
gem 'rails', "~> #{rails_version}"
|
8
|
+
gem 'sqlite3', '~> 1.4'
|
9
|
+
gem 'paper_trail', "~> #{paper_trail_version}"
|
10
|
+
gem 'kaminari'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
5
|
+
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [1.0.0] - 2022-03-26
|
8
|
+
|
9
|
+
Initial release.
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright for portions of paper_trail_viewer are held by Igal Koshevoy, 2011,
|
2
|
+
as part of project paper_trail_manager.
|
3
|
+
|
4
|
+
All other copyright for paper_trail_viewer is held by Janosch Müller, 2022.
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/paper_trail_viewer.svg)](http://badge.fury.io/rb/paper_trail_viewer)
|
2
|
+
[![Build Status](https://github.com/jaynetics/paper_trail_viewer/workflows/tests/badge.svg)](https://github.com/jaynetics/paper_trail_viewer/actions)
|
3
|
+
|
4
|
+
# PaperTrailViewer
|
5
|
+
|
6
|
+
Browse changes to records when using Ruby on Rails and the [`paper_trail` gem](https://github.com/paper-trail-gem/paper_trail).
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add `paper_trail_viewer` to your bundle and add the following line to your `config/routes.rb`:
|
11
|
+
|
12
|
+
mount PaperTrailViewer::Engine => '/changes'
|
13
|
+
|
14
|
+
You can pick any path. Restart the server and go to the chosen path to view your versions.
|
15
|
+
|
16
|
+
To limit access to this view, do something like:
|
17
|
+
|
18
|
+
authenticate :user, ->*{ |u| u.superadmin? } do
|
19
|
+
mount PaperTrailViewer::Engine => '/changes'
|
20
|
+
end
|
21
|
+
|
22
|
+
### Configuration
|
23
|
+
|
24
|
+
Put configuration in `config/initializers/paper_trail_viewer.rb`.
|
25
|
+
|
26
|
+
E.g. for linking (or not) to the whodunnit user with a custom path helper:
|
27
|
+
|
28
|
+
PaperTrailViewer.user_path_method = :admin_path # default is :user_path
|
29
|
+
PaperTrailViewer.user_path_method = nil # don't link to the user
|
30
|
+
|
31
|
+
## Development
|
32
|
+
|
33
|
+
### Setup
|
34
|
+
|
35
|
+
* Clone the repository
|
36
|
+
* Go into the directory
|
37
|
+
* Run `bin/setup` to install Ruby and JS dependencies
|
38
|
+
|
39
|
+
### Running tests
|
40
|
+
|
41
|
+
* This repo uses the [Appraisal](https://github.com/thoughtbot/appraisal) gem
|
42
|
+
* Run `appraisal generate`
|
43
|
+
* Run `appraisal install`
|
44
|
+
* Run `appraisal rake generate_spec_app`
|
45
|
+
* Run `appraisal rake`
|
46
|
+
|
47
|
+
## License
|
48
|
+
|
49
|
+
This program is provided under an MIT open source license, read the [LICENSE.txt](https://github.com/jaynetics/paper_trail_viewer/blob/master/LICENSE.txt) file for details.
|
50
|
+
|
51
|
+
## To Note:
|
52
|
+
|
53
|
+
This project started as a fork of [PaperTrailManager](https://github.com/fusion94/paper_trail_manager), which was originally developed by [Igal Koshevoy](https://github.com/igal), [Reid Beels](https://github.com/reidab), and [Micah Geisel](https://github.com/botandrose).
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rubygems/package_task'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
Bundler::GemHelper.install_tasks
|
7
|
+
|
8
|
+
RSpec::Core::RakeTask.new
|
9
|
+
task default: [:compile_js, :generate_spec_app, :spec]
|
10
|
+
|
11
|
+
task :compile_js do
|
12
|
+
`npm run compile`
|
13
|
+
end
|
14
|
+
|
15
|
+
task :generate_spec_app do
|
16
|
+
sh 'rm -rf spec/dummy'
|
17
|
+
sh *%w[
|
18
|
+
rails new spec/dummy
|
19
|
+
--template=spec/app_template.rb
|
20
|
+
--skip-action-cable
|
21
|
+
--skip-action-mailbox
|
22
|
+
--skip-action-mailer
|
23
|
+
--skip-action-text
|
24
|
+
--skip-active-job
|
25
|
+
--skip-active-storage
|
26
|
+
--skip-asset-pipeline
|
27
|
+
--skip-bootsnap
|
28
|
+
--skip-bundle
|
29
|
+
--skip-git
|
30
|
+
--skip-hotwire
|
31
|
+
--skip-javascript
|
32
|
+
--skip-jbuilder
|
33
|
+
--skip-keeps
|
34
|
+
--skip-listen
|
35
|
+
--skip-spring
|
36
|
+
--skip-sprockets
|
37
|
+
--skip-system-test
|
38
|
+
--skip-test
|
39
|
+
--skip-turbolinks
|
40
|
+
--skip-webpack
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
# ensure fresh js is compiled when packaging the gem
|
45
|
+
task build: [:compile_js]
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# This returns the pre-compiled JS for the viewer component.
|
2
|
+
# Yes, that is a hacky way to make the gem work without
|
3
|
+
# forcing users to install and integrate an npm package.
|
4
|
+
class PaperTrailViewer::JsController < ActionController::Base
|
5
|
+
protect_from_forgery unless: -> { request.format.js? }
|
6
|
+
|
7
|
+
def show
|
8
|
+
send_file js_file, type: 'text/javascript', disposition: 'inline'
|
9
|
+
end
|
10
|
+
|
11
|
+
def js_file
|
12
|
+
File.join(__dir__, '..', '..', '..', 'javascript', 'compiled.js')
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
class PaperTrailViewer::VersionsController < ActionController::Base
|
2
|
+
# Return the queried versions as JSON.
|
3
|
+
def index
|
4
|
+
query = sanitized_params
|
5
|
+
versions = PaperTrailViewer.data_source.call(**query)
|
6
|
+
versions_as_json = versions.map { |v| version_as_json(v) }
|
7
|
+
|
8
|
+
render json: {
|
9
|
+
hasNextPage: !versions.last_page? && versions_as_json.any?,
|
10
|
+
query: query,
|
11
|
+
versions: versions_as_json,
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
# Rollback a change
|
16
|
+
def update
|
17
|
+
unless version = PaperTrail::Version.find_by(id: params[:id])
|
18
|
+
flash[:error] = 'No such version.'
|
19
|
+
return redirect_to(root_url)
|
20
|
+
end
|
21
|
+
|
22
|
+
result =
|
23
|
+
if version.event == 'create'
|
24
|
+
version.item_type.constantize.find(version.item_id).destroy
|
25
|
+
else
|
26
|
+
version.reify.save
|
27
|
+
end
|
28
|
+
|
29
|
+
if result
|
30
|
+
if version.event == 'create'
|
31
|
+
flash[:notice] = 'Rolled back newly-created record by destroying it.'
|
32
|
+
redirect_to root_url
|
33
|
+
else
|
34
|
+
flash[:notice] = 'Rolled back changes to this record.'
|
35
|
+
redirect_to change_item_url(version)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
flash[:error] = "Couldn't rollback. Sorry."
|
39
|
+
redirect_to root_url
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def sanitized_params
|
46
|
+
{
|
47
|
+
event: params[:event].presence_in(%w[create update destroy]),
|
48
|
+
filter: (params[:filter].presence if params[:filter] != '%%'),
|
49
|
+
item_id: params[:item_id].presence,
|
50
|
+
item_type: params[:item_type].presence,
|
51
|
+
page: (page = params[:page].to_i) > 0 ? page : 1,
|
52
|
+
per_page: (per = params[:per_page].to_i).clamp(1, 1000),
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def version_as_json(version)
|
57
|
+
{
|
58
|
+
**version.attributes.slice(*%w[id whodunnit event created_at]),
|
59
|
+
changeset: changeset_for(version),
|
60
|
+
object: load_object(version),
|
61
|
+
item_id: version.item_id.to_s,
|
62
|
+
item_type: version.item_type,
|
63
|
+
item_url: change_item_url(version),
|
64
|
+
user_url: user_url(version),
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def changeset_for(version)
|
69
|
+
case version.event
|
70
|
+
when 'create', 'update'
|
71
|
+
version.changeset || {}
|
72
|
+
when 'destroy'
|
73
|
+
record = version.reify rescue nil
|
74
|
+
return {} unless record
|
75
|
+
|
76
|
+
record.attributes.each_with_object({}) do |changes, (k, v)|
|
77
|
+
changes[k] = [v, nil] unless v.nil?
|
78
|
+
end
|
79
|
+
else
|
80
|
+
raise ArgumentError, "Unknown event: #{version.event}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Return the URL for the item represented by the +version+,
|
85
|
+
# e.g. a Company record instance referenced by a version.
|
86
|
+
def change_item_url(version)
|
87
|
+
version_type = version.item_type.underscore.split('/').last
|
88
|
+
main_app.try("#{version_type}_url", version.item_id)
|
89
|
+
end
|
90
|
+
|
91
|
+
def user_url(version)
|
92
|
+
(path_method = PaperTrailViewer.user_path_method).present? &&
|
93
|
+
(id = version.whodunnit).present? &&
|
94
|
+
!id.start_with?('#') &&
|
95
|
+
main_app.try(path_method, id) ||
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
|
99
|
+
def load_object(version)
|
100
|
+
obj = version.object
|
101
|
+
if obj.is_a?(String)
|
102
|
+
PaperTrail.serializer.load(obj)
|
103
|
+
elsif obj.is_a?(Hash)
|
104
|
+
OpenStruct.new(obj)
|
105
|
+
else
|
106
|
+
obj
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# backport deserialization fix for recent rubies
|
112
|
+
# https://github.com/paper-trail-gem/paper_trail/pull/1338
|
113
|
+
if PaperTrail::VERSION.to_s < '12.2.0'
|
114
|
+
module PaperTrail::Serializers::YAML
|
115
|
+
def load(string)
|
116
|
+
::YAML.respond_to?(:unsafe_load) ? ::YAML.unsafe_load(string) : ::YAML.load(string)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/bin/setup
ADDED
data/config/routes.rb
ADDED