trusty-cms 7.0.30 → 7.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +17 -7
- data/app/assets/javascripts/admin/recent-changes.js +17 -0
- data/app/assets/javascripts/admin.js +1 -0
- data/app/assets/stylesheets/admin/main.scss +1 -0
- data/app/assets/stylesheets/admin/partials/_preferences.scss +1 -1
- data/app/assets/stylesheets/admin/partials/_recent_changes.scss +80 -0
- data/app/assets/stylesheets/admin/partials/_table.scss +37 -39
- data/app/controllers/admin/changes_controller.rb +118 -0
- data/app/controllers/admin/pages_controller.rb +1 -0
- data/app/controllers/admin/resource_controller.rb +7 -0
- data/app/helpers/admin/pages_helper.rb +6 -0
- data/app/models/page.rb +8 -7
- data/app/models/page_part.rb +2 -2
- data/app/views/admin/changes/show.html.haml +57 -0
- data/app/views/admin/configuration/show.html.haml +1 -1
- data/app/views/admin/layouts/_site_chooser.html.haml +1 -1
- data/app/views/admin/pages/_previous_versions.haml +14 -5
- data/app/views/admin/users/_form.html.haml +3 -0
- data/app/views/admin/users/_password_fields.html.haml +14 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20250606144908_add_object_changes_to_versions.trusty_cms.rb +8 -0
- data/lib/trusty_cms/admin_ui.rb +17 -3
- data/lib/trusty_cms/version.rb +1 -1
- data/trusty_cms.gemspec +1 -0
- data/vendor/extensions/multi-site-extension/lib/multi_site/pages_controller_extensions.rb +1 -1
- data/vendor/extensions/multi-site-extension/multi_site_extension.rb +2 -0
- metadata +22 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fcafb280a56612c280982244b9a3bc76e4460aabef9ea7bb28eed7cdccfd880c
|
4
|
+
data.tar.gz: 281618eabbfbf3c6b14dba86edc1ce57aa5d362607c7e68f4aab09ad81a4fbca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf4134454382b20f1e8bad14286963d02fa3dd7079ab3786516408e1a744514a147e58bfdd3f4ae60c2e355f252aa4bc6cab8fb22a8315f924aba6303a9f30ac
|
7
|
+
data.tar.gz: 27bce35c3fb49517d7af5825049bf68e40ca0af29c117f60aca5066c005080cc5e8e3fa4c60dc78691d9c5898299f5b3c286851bd6a23644c7253b3478cc7a1a
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
trusty-cms (7.0.
|
4
|
+
trusty-cms (7.0.32)
|
5
5
|
RedCloth (= 4.3.3)
|
6
6
|
activestorage-validator
|
7
7
|
acts_as_list (>= 0.9.5, < 1.3.0)
|
@@ -12,6 +12,7 @@ PATH
|
|
12
12
|
delocalize (>= 0.2, < 2.0)
|
13
13
|
devise
|
14
14
|
devise-two-factor
|
15
|
+
diffy
|
15
16
|
drb
|
16
17
|
execjs (~> 2.7)
|
17
18
|
haml (>= 5.0, < 6.0)
|
@@ -168,6 +169,7 @@ GEM
|
|
168
169
|
railties (>= 7.0, < 8.1)
|
169
170
|
rotp (~> 6.0)
|
170
171
|
diff-lcs (1.5.1)
|
172
|
+
diffy (3.4.4)
|
171
173
|
docile (1.4.1)
|
172
174
|
drb (2.2.1)
|
173
175
|
erubi (1.13.0)
|
@@ -246,6 +248,7 @@ GEM
|
|
246
248
|
mime-types-data (3.2024.1105)
|
247
249
|
mini_magick (4.13.2)
|
248
250
|
mini_mime (1.1.5)
|
251
|
+
mini_portile2 (2.8.9)
|
249
252
|
mini_racer (0.16.0)
|
250
253
|
libv8-node (~> 18.19.0.0)
|
251
254
|
minitest (5.25.4)
|
@@ -264,17 +267,24 @@ GEM
|
|
264
267
|
net-smtp (0.5.0)
|
265
268
|
net-protocol
|
266
269
|
nio4r (2.7.3)
|
267
|
-
nokogiri (1.
|
270
|
+
nokogiri (1.18.8)
|
271
|
+
mini_portile2 (~> 2.8.2)
|
268
272
|
racc (~> 1.4)
|
269
|
-
nokogiri (1.
|
273
|
+
nokogiri (1.18.8-aarch64-linux-gnu)
|
270
274
|
racc (~> 1.4)
|
271
|
-
nokogiri (1.
|
275
|
+
nokogiri (1.18.8-aarch64-linux-musl)
|
272
276
|
racc (~> 1.4)
|
273
|
-
nokogiri (1.
|
277
|
+
nokogiri (1.18.8-arm-linux-gnu)
|
274
278
|
racc (~> 1.4)
|
275
|
-
nokogiri (1.
|
279
|
+
nokogiri (1.18.8-arm-linux-musl)
|
276
280
|
racc (~> 1.4)
|
277
|
-
nokogiri (1.
|
281
|
+
nokogiri (1.18.8-arm64-darwin)
|
282
|
+
racc (~> 1.4)
|
283
|
+
nokogiri (1.18.8-x86_64-darwin)
|
284
|
+
racc (~> 1.4)
|
285
|
+
nokogiri (1.18.8-x86_64-linux-gnu)
|
286
|
+
racc (~> 1.4)
|
287
|
+
nokogiri (1.18.8-x86_64-linux-musl)
|
278
288
|
racc (~> 1.4)
|
279
289
|
orm_adapter (0.5.0)
|
280
290
|
paper_trail (16.0.0)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
document.addEventListener("DOMContentLoaded", () => {
|
2
|
+
document.querySelectorAll(".toggle-diff").forEach(link => {
|
3
|
+
link.addEventListener("click", event => {
|
4
|
+
event.preventDefault();
|
5
|
+
|
6
|
+
const targetId = link.dataset.toggle;
|
7
|
+
const row = document.getElementById(targetId);
|
8
|
+
if (!row) return;
|
9
|
+
|
10
|
+
const isHidden = window.getComputedStyle(row).display === "none";
|
11
|
+
row.style.display = isHidden ? "table-row" : "none";
|
12
|
+
|
13
|
+
link.textContent = isHidden ? "Hide Diff" : "Show Diff";
|
14
|
+
link.setAttribute("aria-expanded", isHidden.toString());
|
15
|
+
});
|
16
|
+
});
|
17
|
+
});
|
@@ -0,0 +1,80 @@
|
|
1
|
+
.diff {
|
2
|
+
overflow: visible;
|
3
|
+
}
|
4
|
+
|
5
|
+
.diff ul {
|
6
|
+
background: #fff;
|
7
|
+
display: table;
|
8
|
+
font-size: 13px;
|
9
|
+
list-style: none;
|
10
|
+
margin: 0;
|
11
|
+
overflow: auto;
|
12
|
+
padding: 0;
|
13
|
+
width: 100%;
|
14
|
+
}
|
15
|
+
|
16
|
+
.diff li {
|
17
|
+
display: table-row;
|
18
|
+
height: 1em;
|
19
|
+
margin: 0;
|
20
|
+
padding: 0;
|
21
|
+
}
|
22
|
+
|
23
|
+
.diff li.ins {
|
24
|
+
background: #dfd;
|
25
|
+
color: #080;
|
26
|
+
}
|
27
|
+
|
28
|
+
.diff li.del {
|
29
|
+
background: #fee;
|
30
|
+
color: #b00;
|
31
|
+
}
|
32
|
+
|
33
|
+
.diff del,
|
34
|
+
.diff ins,
|
35
|
+
.diff span {
|
36
|
+
display: block;
|
37
|
+
font-family: courier;
|
38
|
+
text-decoration: none;
|
39
|
+
white-space: pre-wrap;
|
40
|
+
}
|
41
|
+
|
42
|
+
.diff del strong {
|
43
|
+
background: #fcc;
|
44
|
+
font-weight: normal;
|
45
|
+
}
|
46
|
+
|
47
|
+
.diff ins strong {
|
48
|
+
background: #9f9;
|
49
|
+
font-weight: normal;
|
50
|
+
}
|
51
|
+
|
52
|
+
.diff li.diff-comment {
|
53
|
+
display: none;
|
54
|
+
}
|
55
|
+
|
56
|
+
.diff li.diff-block-info {
|
57
|
+
background: gray;
|
58
|
+
}
|
59
|
+
|
60
|
+
.dynamic-diff-row {
|
61
|
+
display: none;
|
62
|
+
}
|
63
|
+
|
64
|
+
table#recent-changes {
|
65
|
+
width: 100%;
|
66
|
+
}
|
67
|
+
|
68
|
+
table#recent-changes td {
|
69
|
+
overflow-wrap: break-word;
|
70
|
+
padding: 12px;
|
71
|
+
word-break: break-word;
|
72
|
+
}
|
73
|
+
|
74
|
+
table#recent-changes tr:hover.diff-row td {
|
75
|
+
background: none !important;
|
76
|
+
}
|
77
|
+
|
78
|
+
table#recent-changes tr:hover.dynamic-diff-row td {
|
79
|
+
background: none !important;
|
80
|
+
}
|
@@ -7,7 +7,6 @@
|
|
7
7
|
width: $width;
|
8
8
|
}
|
9
9
|
|
10
|
-
|
11
10
|
table {
|
12
11
|
th {
|
13
12
|
background-color: $light-gray;
|
@@ -51,23 +50,23 @@ table {
|
|
51
50
|
color: $dark-gray;
|
52
51
|
text-decoration: none;
|
53
52
|
|
53
|
+
&.selected,
|
54
54
|
&:hover {
|
55
55
|
background: $light-gray;
|
56
56
|
border: 1px solid darken($light-gray, 10%);
|
57
57
|
margin: 0 24px 0 0;
|
58
58
|
}
|
59
|
-
|
60
|
-
&.selected {
|
61
|
-
background: $light-gray;
|
62
|
-
border: 1px solid darken($light-gray, 10%);
|
63
|
-
margin: 0 24px 0 0;
|
64
|
-
}
|
65
59
|
}
|
66
60
|
|
67
61
|
span.action.disabled {
|
68
62
|
color: #cccccc;
|
69
63
|
}
|
70
64
|
|
65
|
+
&.actions {
|
66
|
+
font-size: 85%;
|
67
|
+
white-space: nowrap;
|
68
|
+
}
|
69
|
+
|
71
70
|
&.empty {
|
72
71
|
color: silver;
|
73
72
|
font-style: italic;
|
@@ -83,10 +82,12 @@ table {
|
|
83
82
|
text-decoration: none;
|
84
83
|
}
|
85
84
|
}
|
85
|
+
}
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
87
|
+
thead {
|
88
|
+
tr,
|
89
|
+
tr:first-child {
|
90
|
+
border-top: none;
|
90
91
|
}
|
91
92
|
}
|
92
93
|
|
@@ -104,17 +105,17 @@ table {
|
|
104
105
|
}
|
105
106
|
}
|
106
107
|
}
|
107
|
-
|
108
|
-
thead {
|
109
|
-
tr,
|
110
|
-
tr:first-child {
|
111
|
-
border-top: none;
|
112
|
-
}
|
113
|
-
}
|
114
108
|
}
|
115
109
|
|
116
110
|
table.index#pages {
|
117
111
|
td.name {
|
112
|
+
.info {
|
113
|
+
color: #9eb3bf;
|
114
|
+
font-size: 0.90em;
|
115
|
+
font-style: italic;
|
116
|
+
font-weight: normal;
|
117
|
+
}
|
118
|
+
|
118
119
|
.w1 {
|
119
120
|
position: relative;
|
120
121
|
|
@@ -124,24 +125,35 @@ table.index#pages {
|
|
124
125
|
position: absolute;
|
125
126
|
}
|
126
127
|
}
|
128
|
+
}
|
127
129
|
|
128
|
-
|
129
|
-
|
130
|
-
font-size: 0.90em;
|
131
|
-
font-style: italic;
|
132
|
-
font-weight: normal;
|
133
|
-
}
|
130
|
+
td.status {
|
131
|
+
font-size: 0.80em;
|
134
132
|
}
|
135
133
|
|
136
134
|
tr.page.virtual td.name a .title {
|
137
135
|
color: #9eb3bf;
|
138
136
|
}
|
137
|
+
}
|
139
138
|
|
140
|
-
|
141
|
-
|
139
|
+
table.index#users {
|
140
|
+
td.name {
|
141
|
+
padding-bottom: 8px;
|
142
|
+
padding-top: 8px;
|
143
|
+
|
144
|
+
.login {
|
145
|
+
color: #9eb3bf;
|
146
|
+
font-size: 90%;
|
147
|
+
font-style: italic;
|
148
|
+
font-weight: normal;
|
149
|
+
}
|
142
150
|
}
|
143
151
|
}
|
144
152
|
|
153
|
+
table#versions-table td {
|
154
|
+
padding: 12px;
|
155
|
+
}
|
156
|
+
|
145
157
|
.status {
|
146
158
|
a {
|
147
159
|
@include plain-link;
|
@@ -155,17 +167,3 @@ table.index#pages {
|
|
155
167
|
.hidden_status {
|
156
168
|
@include status-badge(#9eb3bf);
|
157
169
|
}
|
158
|
-
|
159
|
-
table.index#users {
|
160
|
-
td.name {
|
161
|
-
padding-bottom: 8px;
|
162
|
-
padding-top: 8px;
|
163
|
-
|
164
|
-
.login {
|
165
|
-
color: #9eb3bf;
|
166
|
-
font-size: 90%;
|
167
|
-
font-style: italic;
|
168
|
-
font-weight: normal;
|
169
|
-
}
|
170
|
-
}
|
171
|
-
}
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'diffy'
|
2
|
+
|
3
|
+
class Admin::ChangesController < Admin::ResourceController
|
4
|
+
before_action :initialize_variables
|
5
|
+
|
6
|
+
def show
|
7
|
+
@changes = load_changes
|
8
|
+
@change_error = 'Version ID not found.' if params[:version_id].present? && @changes.empty?
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def load_changes
|
14
|
+
return load_single_change if params[:version_id].present?
|
15
|
+
|
16
|
+
load_recent_changes
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_single_change
|
20
|
+
version = PaperTrail::Version.find_by(id: params[:version_id])
|
21
|
+
version ? [build_change_entry(version)] : []
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_recent_changes
|
25
|
+
fetch_recent_page_versions.map { |version| build_change_entry(version) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def fetch_recent_page_versions
|
29
|
+
PaperTrail::Version.
|
30
|
+
where(item_type: 'Page').
|
31
|
+
joins('INNER JOIN pages ON pages.id = versions.item_id').
|
32
|
+
where(pages: { site_id: current_site.id }).
|
33
|
+
where('versions.created_at >= ?', 1.month.ago).
|
34
|
+
order(created_at: :desc).
|
35
|
+
limit(25)
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_change_entry(version)
|
39
|
+
page = Page.find_by(id: version.item_id)
|
40
|
+
return {} unless page
|
41
|
+
|
42
|
+
{
|
43
|
+
action: version.event.titleize,
|
44
|
+
diff: build_diff(version),
|
45
|
+
id: version.id,
|
46
|
+
page_title: page.title,
|
47
|
+
page_url: admin_page_url(page),
|
48
|
+
updated_at: format_timestamp(version.created_at),
|
49
|
+
user_name: user_name(version.whodunnit),
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_diff(version)
|
54
|
+
related_versions = PaperTrail::Version.where(transaction_id: version.transaction_id)
|
55
|
+
diffs = related_versions.flat_map { |v| diff_fields(v) }.compact
|
56
|
+
diffs.any? ? diffs.join('<br />') : nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def diff_fields(version)
|
60
|
+
version.changeset.map do |field, (old_val, new_val)|
|
61
|
+
next unless renderable_diff?(field, old_val, new_val)
|
62
|
+
|
63
|
+
render_field_diff(version, field, old_val, new_val)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def renderable_diff?(field, old_val, new_val)
|
68
|
+
!ignored_fields.include?(field) && !(old_val.nil? && new_val == '')
|
69
|
+
end
|
70
|
+
|
71
|
+
def render_field_diff(version, field, old_val, new_val)
|
72
|
+
diff_html = Diffy::Diff.new(old_val, new_val, context: 1).to_s(:html)
|
73
|
+
|
74
|
+
label = version.item_type == 'Page' ? field : label_for_version(version)
|
75
|
+
"<h2>#{label.humanize.titleize}</h2>#{diff_html}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def ignored_fields
|
79
|
+
%w[
|
80
|
+
created_at
|
81
|
+
created_by_id
|
82
|
+
id
|
83
|
+
lock_version
|
84
|
+
name
|
85
|
+
page_id
|
86
|
+
published_at
|
87
|
+
updated_at
|
88
|
+
updated_by_id
|
89
|
+
].freeze
|
90
|
+
end
|
91
|
+
|
92
|
+
def user_name(whodunnit)
|
93
|
+
user_id = Integer(whodunnit, exception: false)
|
94
|
+
User.find_by(id: user_id)&.name || 'Unknown User'
|
95
|
+
end
|
96
|
+
|
97
|
+
def label_for_version(version)
|
98
|
+
case version.item_type
|
99
|
+
when 'PagePart' then PagePart.find_by(id: version.item_id)&.name || 'Unknown PagePart'
|
100
|
+
when 'PageField' then PageField.find_by(id: version.item_id)&.name || 'Unknown PageField'
|
101
|
+
else version.item_type
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def format_timestamp(timestamp)
|
106
|
+
timestamp.strftime('%A, %B %d, %I:%M %p')
|
107
|
+
end
|
108
|
+
|
109
|
+
def admin_page_url(page)
|
110
|
+
"/admin/pages/#{page.id}"
|
111
|
+
end
|
112
|
+
|
113
|
+
def initialize_variables
|
114
|
+
@user = current_user
|
115
|
+
@controller_name = 'changes'
|
116
|
+
@template_name = 'show'
|
117
|
+
end
|
118
|
+
end
|
@@ -134,6 +134,7 @@ class Admin::PagesController < Admin::ResourceController
|
|
134
134
|
.sort_by(&:created_at).reverse
|
135
135
|
.map do |version|
|
136
136
|
{
|
137
|
+
id: version&.id,
|
137
138
|
index: version&.index,
|
138
139
|
update_date: version&.created_at&.strftime('%B %d, %Y'),
|
139
140
|
update_time: version&.created_at&.strftime('%I:%M %p'),
|
@@ -9,6 +9,7 @@ class Admin::ResourceController < ApplicationController
|
|
9
9
|
before_action :load_models, only: :index
|
10
10
|
before_action :load_model, only: %i[new create edit update remove destroy]
|
11
11
|
before_action :set_owner_or_editor, only: %i[new create update]
|
12
|
+
before_action :set_page_updated_at, only: :update
|
12
13
|
after_action :clear_model_cache, only: %i[create update destroy]
|
13
14
|
|
14
15
|
cattr_reader :paginated
|
@@ -145,6 +146,12 @@ class Admin::ResourceController < ApplicationController
|
|
145
146
|
end
|
146
147
|
end
|
147
148
|
|
149
|
+
def set_page_updated_at
|
150
|
+
if model.class.name.include?('Page') && model.has_attribute?(:updated_at)
|
151
|
+
model.updated_at = Time.zone.now
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
148
155
|
def model
|
149
156
|
instance_variable_get("@#{model_symbol}") || load_model
|
150
157
|
end
|
@@ -25,4 +25,10 @@ module Admin::PagesHelper
|
|
25
25
|
selected_page_id = page.parent_id
|
26
26
|
options_for_select(parent_pages.map { |p| [p.title, p.id] }, selected_page_id)
|
27
27
|
end
|
28
|
+
|
29
|
+
def revert_confirmation_message(version)
|
30
|
+
date = version[:update_date]
|
31
|
+
time = version[:update_time]
|
32
|
+
"Are you sure you want to revert the page to the version before the change made on #{date} at #{time}? All changes made after that will be lost."
|
33
|
+
end
|
28
34
|
end
|
data/app/models/page.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'trusty_cms/taggable'
|
2
2
|
|
3
3
|
class Page < ActiveRecord::Base
|
4
|
-
has_paper_trail
|
5
|
-
|
6
4
|
class MissingRootPageError < StandardError
|
7
5
|
def initialize(message = 'Database missing root page')
|
8
6
|
; super
|
@@ -22,6 +20,8 @@ class Page < ActiveRecord::Base
|
|
22
20
|
belongs_to :created_by, class_name: 'User'
|
23
21
|
belongs_to :updated_by, class_name: 'User'
|
24
22
|
|
23
|
+
has_paper_trail
|
24
|
+
|
25
25
|
# Validations
|
26
26
|
validates_presence_of :title, :slug, :breadcrumb, :status_id
|
27
27
|
|
@@ -40,6 +40,7 @@ class Page < ActiveRecord::Base
|
|
40
40
|
|
41
41
|
annotate :description
|
42
42
|
attr_accessor :request, :response, :pagination_parameters
|
43
|
+
|
43
44
|
class_attribute :default_child
|
44
45
|
self.default_child = self
|
45
46
|
|
@@ -264,11 +265,11 @@ class Page < ActiveRecord::Base
|
|
264
265
|
@display_name = string
|
265
266
|
else
|
266
267
|
@display_name ||= begin
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
268
|
+
n = name.to_s
|
269
|
+
n.sub(/^(.+?)Page$/, '\1')
|
270
|
+
n.gsub(/([A-Z])/, ' \1')
|
271
|
+
n.strip
|
272
|
+
end
|
272
273
|
end
|
273
274
|
@display_name = @display_name + ' - not installed' if missing? && @display_name !~ /not installed/
|
274
275
|
@display_name
|
data/app/models/page_part.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
class PagePart < ActiveRecord::Base
|
2
|
-
has_paper_trail
|
3
|
-
|
4
2
|
# Default Order
|
5
3
|
default_scope { order('name') }
|
6
4
|
|
7
5
|
# Associations
|
8
6
|
belongs_to :page
|
9
7
|
|
8
|
+
has_paper_trail
|
9
|
+
|
10
10
|
# Validations
|
11
11
|
validates_presence_of :name
|
12
12
|
validates_length_of :name, maximum: 100
|
@@ -0,0 +1,57 @@
|
|
1
|
+
- @page_title = 'Changes - ' + default_page_title
|
2
|
+
- single_change = params[:version_id].present?
|
3
|
+
|
4
|
+
.outset
|
5
|
+
= render_region :top
|
6
|
+
|
7
|
+
%table#recent-changes
|
8
|
+
%thead
|
9
|
+
%tr
|
10
|
+
%th Version ID
|
11
|
+
%th Title
|
12
|
+
%th Action
|
13
|
+
%th User
|
14
|
+
%th Updated At
|
15
|
+
|
16
|
+
%tbody
|
17
|
+
- if @change_error
|
18
|
+
%tr
|
19
|
+
%td{ colspan: 5 }
|
20
|
+
%span.text-danger= @change_error
|
21
|
+
|
22
|
+
- elsif @changes.present?
|
23
|
+
- @changes.each_with_index do |change, index|
|
24
|
+
%tr.change-row{ data: { toggle_target: "diff-#{index}" } }
|
25
|
+
%td= change[:id]
|
26
|
+
%td= link_to change[:page_title], change[:page_url]
|
27
|
+
%td= change[:action]
|
28
|
+
%td= change[:user_name]
|
29
|
+
%td
|
30
|
+
= change[:updated_at]
|
31
|
+
- unless single_change
|
32
|
+
%br
|
33
|
+
%a.toggle-diff{
|
34
|
+
href: "#",
|
35
|
+
role: "button",
|
36
|
+
tabindex: "0",
|
37
|
+
aria: {
|
38
|
+
expanded: "false",
|
39
|
+
controls: "diff-#{index}"
|
40
|
+
},
|
41
|
+
data: { toggle: "diff-#{index}" }
|
42
|
+
} Show Diff
|
43
|
+
|
44
|
+
- diff_row_id = single_change ? nil : "diff-#{index}"
|
45
|
+
- diff_row_class = single_change ? 'diff-row' : 'dynamic-diff-row'
|
46
|
+
%tr{ id: diff_row_id, class: diff_row_class, role: "region", "aria-live" => "polite" }
|
47
|
+
%td{ colspan: 5 }
|
48
|
+
- if change[:diff].present?
|
49
|
+
!= change[:diff]
|
50
|
+
- else
|
51
|
+
%span.text-muted No differences found.
|
52
|
+
%span.sr-only Diff details for version ID #{change[:id]}.
|
53
|
+
|
54
|
+
- else
|
55
|
+
%tr
|
56
|
+
%td{ colspan: 5 }
|
57
|
+
%span No recent changes.
|
@@ -19,7 +19,7 @@
|
|
19
19
|
%span
|
20
20
|
= current_user.locale
|
21
21
|
.actions
|
22
|
-
= button_to t("edit_preferences"),
|
22
|
+
= button_to t("edit_preferences"), admin_preferences_path, :method => :get
|
23
23
|
|
24
24
|
%fieldset
|
25
25
|
- render_region :user do |user|
|
@@ -3,31 +3,40 @@
|
|
3
3
|
%h4
|
4
4
|
%i.fas.fa-clock-rotate-left
|
5
5
|
Previous Versions
|
6
|
+
|
6
7
|
.drawer_contents#versions
|
7
8
|
- if @versions.present?
|
8
9
|
%section
|
9
|
-
%table
|
10
|
+
%table#versions-table
|
10
11
|
%thead
|
11
12
|
%tr
|
13
|
+
%th Version ID
|
12
14
|
%th Date Updated
|
13
15
|
%th Time Updated
|
14
16
|
%th Updated By
|
15
17
|
%th Action
|
16
|
-
%tbody
|
18
|
+
%tbody
|
17
19
|
- @versions.each do |version|
|
18
20
|
%tr
|
21
|
+
%td= link_to version[:id], admin_changes_path(version_id: version[:id])
|
19
22
|
%td= version[:update_date]
|
20
23
|
%td= version[:update_time]
|
21
24
|
%td= version[:updated_by]
|
22
25
|
%td
|
23
|
-
= button_to '
|
26
|
+
= button_to 'Undo',
|
24
27
|
restore_version_admin_page_path(@page, version_index: version[:index]),
|
25
28
|
method: :put,
|
26
|
-
|
29
|
+
class: 'btn btn-warning',
|
30
|
+
data: { confirm: revert_confirmation_message(version) }
|
27
31
|
- else
|
28
32
|
%section#no-previous-versions
|
29
33
|
%p No previous versions are available.
|
34
|
+
|
30
35
|
.drawer_handle
|
31
|
-
%a.toggle{
|
36
|
+
%a.toggle{
|
37
|
+
href: '#versions',
|
38
|
+
rel: 'toggle[versions]',
|
39
|
+
class: (meta_errors? ? 'less' : 'more')
|
40
|
+
}
|
32
41
|
= meta_label
|
33
42
|
%i.fas.fa-angle-down
|
@@ -15,6 +15,9 @@
|
|
15
15
|
= f.label :email, t('email_address') , :class => 'optional'
|
16
16
|
= f.text_field 'email', :class => 'textbox', :maxlength => 255
|
17
17
|
|
18
|
+
- form.edit_password do
|
19
|
+
= render 'password_fields', :f => f
|
20
|
+
|
18
21
|
- form.edit_roles do
|
19
22
|
- if current_user.admin?
|
20
23
|
%fieldset.multi_option
|
@@ -0,0 +1,14 @@
|
|
1
|
+
%fieldset#display-password{ :style => (@user.new_record? or !@user.valid?) ? 'display: none' : nil }
|
2
|
+
%label= t('password')
|
3
|
+
%a.button{ :href => '#', :onclick => "$('#display-password').hide(); $('#change-password').show()" }= t('change')
|
4
|
+
%fieldset#change-password{ :style => (!@user.new_record? && @user.valid?) ? 'display: none' : nil }
|
5
|
+
%p
|
6
|
+
= f.label :password, t('new_password')
|
7
|
+
= f.password_field 'password', :value => '', :maxlength => 40, :autocomplete => 'new-password'
|
8
|
+
%p
|
9
|
+
= f.label :password_confirmation, t('password_confirmation')
|
10
|
+
= f.password_field 'password_confirmation', :value => '', :maxlength => 40, :autocomplete => 'new-password'
|
11
|
+
- unless @user.new_record?
|
12
|
+
%span
|
13
|
+
= t('or')
|
14
|
+
%a{ :href => '#', :class => 'cancel-button', :onclick => " $('#display-password').show(); $('#change-password').hide()" }= t('cancel', class: 'alt')
|
data/config/routes.rb
CHANGED
@@ -43,6 +43,7 @@ TrustyCms::Application.routes.draw do
|
|
43
43
|
get 'admin' => 'admin/pages#index'
|
44
44
|
|
45
45
|
namespace :admin do
|
46
|
+
resource :changes
|
46
47
|
resource :preferences
|
47
48
|
resource :two_factor, only: [:show, :create], controller: 'two_factor', path: 'two-factor'
|
48
49
|
resource :security, controller: 'security' do
|
data/lib/trusty_cms/admin_ui.rb
CHANGED
@@ -70,7 +70,8 @@ module TrustyCms
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def deprecated_add(name, url, caller)
|
73
|
-
ActiveSupport::Deprecation.warn("admin.tabs.add is no longer supported in TrustyCms 0.9.x. Please update your code to use: \ntab \"Content\" do\n\tadd_item(...)\nend",
|
73
|
+
ActiveSupport::Deprecation.warn("admin.tabs.add is no longer supported in TrustyCms 0.9.x. Please update your code to use: \ntab \"Content\" do\n\tadd_item(...)\nend",
|
74
|
+
caller)
|
74
75
|
NavSubItem.new(name, url)
|
75
76
|
end
|
76
77
|
end
|
@@ -123,7 +124,7 @@ module TrustyCms
|
|
123
124
|
end
|
124
125
|
|
125
126
|
# Region sets
|
126
|
-
%w{page layout user configuration extension}.each do |controller|
|
127
|
+
%w{page layout user changes configuration extension}.each do |controller|
|
127
128
|
attr_accessor controller
|
128
129
|
alias_method "#{controller}s", controller
|
129
130
|
end
|
@@ -147,6 +148,10 @@ module TrustyCms
|
|
147
148
|
design << nav_item('Layouts', '/admin/layouts')
|
148
149
|
nav << design
|
149
150
|
|
151
|
+
changes = nav_tab('Recent Changes')
|
152
|
+
changes << nav_item('Changes', '/admin/changes')
|
153
|
+
nav << changes
|
154
|
+
|
150
155
|
settings = nav_tab('Settings')
|
151
156
|
settings << nav_item('General', '/admin/configuration')
|
152
157
|
settings << nav_item('Personal', '/admin/preferences')
|
@@ -159,6 +164,7 @@ module TrustyCms
|
|
159
164
|
def load_default_regions
|
160
165
|
@page = load_default_page_regions
|
161
166
|
@layout = load_default_layout_regions
|
167
|
+
@changes = load_default_changes_regions
|
162
168
|
@user = load_default_user_regions
|
163
169
|
@configuration = load_default_configuration_regions
|
164
170
|
@extension = load_default_extension_regions
|
@@ -201,7 +207,7 @@ module TrustyCms
|
|
201
207
|
end
|
202
208
|
user.edit = RegionSet.new do |edit|
|
203
209
|
edit.main.concat %w{edit_header edit_form}
|
204
|
-
edit.form.concat %w{edit_first_name edit_last_name edit_email edit_roles edit_notes}
|
210
|
+
edit.form.concat %w{edit_first_name edit_last_name edit_email edit_password edit_roles edit_notes}
|
205
211
|
edit.form_bottom.concat %w{edit_buttons edit_timestamp}
|
206
212
|
end
|
207
213
|
user.index = RegionSet.new do |index|
|
@@ -232,6 +238,14 @@ module TrustyCms
|
|
232
238
|
end
|
233
239
|
end
|
234
240
|
|
241
|
+
def load_default_changes_regions
|
242
|
+
OpenStruct.new.tap do |changes|
|
243
|
+
changes.show = RegionSet.new do |show|
|
244
|
+
show.top.concat %w{}
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
235
249
|
def load_default_configuration_regions
|
236
250
|
OpenStruct.new.tap do |configuration|
|
237
251
|
configuration.show = RegionSet.new do |show|
|
data/lib/trusty_cms/version.rb
CHANGED
data/trusty_cms.gemspec
CHANGED
@@ -34,6 +34,7 @@ a general purpose content management system--not merely a blogging engine.'
|
|
34
34
|
s.add_dependency 'delocalize', '>= 0.2', '< 2.0'
|
35
35
|
s.add_dependency 'devise'
|
36
36
|
s.add_dependency 'devise-two-factor'
|
37
|
+
s.add_dependency 'diffy'
|
37
38
|
s.add_dependency 'drb'
|
38
39
|
s.add_dependency 'execjs', '~> 2.7'
|
39
40
|
s.add_dependency 'haml', '>= 5.0', '< 6.0'
|
@@ -21,10 +21,12 @@ class MultiSiteExtension < TrustyCms::Extension
|
|
21
21
|
Admin::PagesController.send :include, MultiSite::PagesControllerExtensions
|
22
22
|
Admin::ResourceController.send :helper, MultiSite::SiteChooserHelper
|
23
23
|
Admin::PagesController.send :helper, MultiSite::SiteChooserHelper
|
24
|
+
Admin::ChangesController.send :helper, MultiSite::SiteChooserHelper
|
24
25
|
admin.layouts.index.add(:before_nav, "admin/layouts/site_chooser")
|
25
26
|
admin.pages.index.add(:before_nav, "admin/layouts/site_chooser")
|
26
27
|
admin.snippets.index.add(:before_nav, "admin/layouts/site_chooser")
|
27
28
|
admin.pages.search.add(:before_nav, "admin/layouts/site_chooser")
|
29
|
+
admin.changes.show.add(:before_nav, "admin/layouts/site_chooser")
|
28
30
|
Layout.send :is_site_scoped
|
29
31
|
Snippet.send :is_site_scoped
|
30
32
|
User.send :is_site_scoped, :shareable => true
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trusty-cms
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.0.
|
4
|
+
version: 7.0.32
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TrustyCms CMS dev team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activestorage-validator
|
@@ -154,6 +154,20 @@ dependencies:
|
|
154
154
|
- - ">="
|
155
155
|
- !ruby/object:Gem::Version
|
156
156
|
version: '0'
|
157
|
+
- !ruby/object:Gem::Dependency
|
158
|
+
name: diffy
|
159
|
+
requirement: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - ">="
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
type: :runtime
|
165
|
+
prerelease: false
|
166
|
+
version_requirements: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - ">="
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '0'
|
157
171
|
- !ruby/object:Gem::Dependency
|
158
172
|
name: drb
|
159
173
|
requirement: !ruby/object:Gem::Requirement
|
@@ -731,6 +745,7 @@ files:
|
|
731
745
|
- app/assets/javascripts/admin/persist.min.js
|
732
746
|
- app/assets/javascripts/admin/popup.js
|
733
747
|
- app/assets/javascripts/admin/preview.js
|
748
|
+
- app/assets/javascripts/admin/recent-changes.js
|
734
749
|
- app/assets/javascripts/admin/sortable.js
|
735
750
|
- app/assets/javascripts/admin/tabcontrol.js.erb
|
736
751
|
- app/assets/javascripts/admin/tags.js
|
@@ -765,6 +780,7 @@ files:
|
|
765
780
|
- app/assets/stylesheets/admin/partials/_popup.scss
|
766
781
|
- app/assets/stylesheets/admin/partials/_preferences.scss
|
767
782
|
- app/assets/stylesheets/admin/partials/_previous_versions.scss
|
783
|
+
- app/assets/stylesheets/admin/partials/_recent_changes.scss
|
768
784
|
- app/assets/stylesheets/admin/partials/_sidebar.scss
|
769
785
|
- app/assets/stylesheets/admin/partials/_tabcontrol.scss
|
770
786
|
- app/assets/stylesheets/admin/partials/_table.scss
|
@@ -773,6 +789,7 @@ files:
|
|
773
789
|
- app/assets/stylesheets/admin/partials/_typography.scss
|
774
790
|
- app/assets/stylesheets/admin/partials/_validations.scss
|
775
791
|
- app/controllers/admin/assets_controller.rb
|
792
|
+
- app/controllers/admin/changes_controller.rb
|
776
793
|
- app/controllers/admin/configuration_controller.rb
|
777
794
|
- app/controllers/admin/extensions_controller.rb
|
778
795
|
- app/controllers/admin/layouts_controller.rb
|
@@ -848,6 +865,7 @@ files:
|
|
848
865
|
- app/views/admin/assets/index.html.haml
|
849
866
|
- app/views/admin/assets/new.html.haml
|
850
867
|
- app/views/admin/assets/remove.html.haml
|
868
|
+
- app/views/admin/changes/show.html.haml
|
851
869
|
- app/views/admin/configuration/_clipped_edit.html.haml
|
852
870
|
- app/views/admin/configuration/_clipped_show.html.haml
|
853
871
|
- app/views/admin/configuration/edit.html.haml
|
@@ -908,6 +926,7 @@ files:
|
|
908
926
|
- app/views/admin/two_factor/show.html.haml
|
909
927
|
- app/views/admin/users/_choose_site.html.haml
|
910
928
|
- app/views/admin/users/_form.html.haml
|
929
|
+
- app/views/admin/users/_password_fields.html.haml
|
911
930
|
- app/views/admin/users/edit.html.haml
|
912
931
|
- app/views/admin/users/index.html.haml
|
913
932
|
- app/views/admin/users/new.html.haml
|
@@ -1001,6 +1020,7 @@ files:
|
|
1001
1020
|
- db/migrate/20250103191133_create_version_associations.rb
|
1002
1021
|
- db/migrate/20250103191134_add_transaction_id_column_to_versions.rb
|
1003
1022
|
- db/migrate/20250502162215_add_devise_two_factor_to_admins.rb
|
1023
|
+
- db/migrate/20250606144908_add_object_changes_to_versions.trusty_cms.rb
|
1004
1024
|
- db/schema.rb
|
1005
1025
|
- lib/active_record_extensions/active_record_extensions.rb
|
1006
1026
|
- lib/annotatable.rb
|