trusty-cms 7.1.2 → 7.1.3
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 +4 -3
- data/Gemfile.lock +58 -46
- data/README.md +2 -0
- data/app/controllers/admin/resource_controller.rb +1 -1
- data/app/controllers/application_controller.rb +15 -0
- data/app/models/asset.rb +29 -5
- data/app/models/standard_tags.rb +13 -7
- data/app/models/user.rb +13 -0
- data/lib/trusty_cms/site_scope_auth_reporter.rb +108 -0
- data/lib/trusty_cms/version.rb +1 -1
- data/package.json +1 -1
- data/spec/controllers/admin/snippets_controller_spec.rb +95 -0
- data/spec/controllers/site_controller_spec.rb +63 -0
- data/spec/factories/site.rb +8 -0
- data/spec/helpers/admin/references_helper_spec.rb +54 -0
- data/spec/helpers/admin/url_helper_spec.rb +71 -0
- data/spec/helpers/application_helper_spec.rb +102 -0
- data/spec/helpers/scoped_helper_spec.rb +60 -0
- data/spec/lib/active_record_extensions_spec.rb +65 -0
- data/spec/lib/login_system_spec.rb +61 -0
- data/spec/lib/ostruct_spec.rb +33 -0
- data/spec/lib/string_extensions_spec.rb +48 -0
- data/spec/lib/symbol_extensions_spec.rb +14 -0
- data/spec/lib/trusty_cms/config_cache_spec.rb +73 -0
- data/spec/models/admins_site_spec.rb +19 -0
- data/spec/models/asset_spec.rb +224 -0
- data/spec/models/asset_type_spec.rb +137 -0
- data/spec/models/file_not_found_page_spec.rb +33 -0
- data/spec/models/haml_filter_spec.rb +30 -0
- data/spec/models/menu_renderer_spec.rb +63 -0
- data/spec/models/page_attachment_spec.rb +29 -0
- data/spec/models/page_context_spec.rb +57 -0
- data/spec/models/page_field_spec.rb +15 -0
- data/spec/models/page_part_spec.rb +33 -0
- data/spec/models/page_spec.rb +184 -0
- data/spec/models/rails_page_spec.rb +32 -0
- data/spec/models/site_spec.rb +93 -0
- data/spec/models/snippet_finder_spec.rb +27 -0
- data/spec/models/snippet_tags_spec.rb +45 -0
- data/spec/models/standard_tags/children_spec.rb +159 -0
- data/spec/models/standard_tags/content_spec.rb +168 -0
- data/spec/models/standard_tags/meta_spec.rb +86 -0
- data/spec/models/standard_tags_spec.rb +32 -0
- data/spec/models/status_spec.rb +63 -0
- data/spec/models/text_filter_spec.rb +47 -0
- data/spec/models/trusty_cms/config_spec.rb +69 -0
- data/spec/models/trusty_cms/page_response_cache_director_spec.rb +72 -0
- data/spec/models/user_action_observer_spec.rb +39 -0
- data/spec/models/user_serialize_telemetry_spec.rb +124 -0
- data/spec/rails_helper.rb +13 -6
- data/spec/requests/admin/snippets_spec.rb +81 -0
- data/spec/spec_helper.rb +17 -3
- data/trusty_cms.gemspec +3 -3
- data/yarn.lock +273 -259
- metadata +91 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9f50633a419203ee6b0b6e35814de7af711b70dd362d5fe0f712543d31f1fc75
|
|
4
|
+
data.tar.gz: f5ccf44bd927e9a392e9979388c9d6f65ace4c1f965516d64936ee1da5e50d53
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8324822cde6d47651420dc28038f51e629e1b411c276a0ddded87dac9587a571de5218e36452f929d0e5c55c4106aa47a21cf72f14e06c78b545c849cfafed6a
|
|
7
|
+
data.tar.gz: 4e4dc362cd53e8124eaba08331db95eb085fe28b8778776ceef121a7e3b993f379b26d2c56c17538962adca2e2b47eb0e834c16d6f67f4a45b4a6cbab9a33cb3
|
data/Gemfile
CHANGED
|
@@ -16,18 +16,19 @@ group :development, :test do
|
|
|
16
16
|
gem 'acts_as_list'
|
|
17
17
|
gem 'database_cleaner'
|
|
18
18
|
gem 'devise-two-factor'
|
|
19
|
-
gem 'factory_bot_rails', '6.
|
|
19
|
+
gem 'factory_bot_rails', '6.5.1'
|
|
20
20
|
gem 'file_validators'
|
|
21
21
|
gem 'jsbundling-rails', '~> 1.3'
|
|
22
22
|
gem 'launchy', '~> 3.0.1'
|
|
23
23
|
gem 'mysql2'
|
|
24
24
|
gem 'paper_trail', '~> 16.0.0'
|
|
25
|
-
gem 'paper_trail-association_tracking', '~> 2.
|
|
25
|
+
gem 'paper_trail-association_tracking', '~> 2.3.0'
|
|
26
26
|
gem 'poltergeist', '~> 1.18.1'
|
|
27
27
|
gem 'pry-byebug'
|
|
28
|
-
gem 'psych', '5.
|
|
28
|
+
gem 'psych', '5.4.0'
|
|
29
29
|
gem 'rails-observers'
|
|
30
30
|
gem 'ransack'
|
|
31
|
+
gem 'rspec_junit_formatter'
|
|
31
32
|
gem 'rspec-rails', '~> 7.1.1'
|
|
32
33
|
gem 'simplecov'
|
|
33
34
|
gem 'simplecov-lcov'
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
trusty-cms (7.1.
|
|
4
|
+
trusty-cms (7.1.3)
|
|
5
5
|
RedCloth (= 4.3.4)
|
|
6
6
|
activestorage-validator
|
|
7
7
|
acts_as_list (>= 0.9.5, < 1.3.0)
|
|
@@ -24,15 +24,15 @@ PATH
|
|
|
24
24
|
mutex_m
|
|
25
25
|
mysql2
|
|
26
26
|
paper_trail (~> 16.0.0)
|
|
27
|
-
paper_trail-association_tracking (
|
|
28
|
-
psych (= 5.
|
|
27
|
+
paper_trail-association_tracking (>= 2.2.1, < 2.4.0)
|
|
28
|
+
psych (= 5.4.0)
|
|
29
29
|
rack (>= 2.0.1, < 3.2.0)
|
|
30
30
|
rack-cache (~> 1.7)
|
|
31
31
|
radius (~> 0.8)
|
|
32
32
|
rails (~> 7.2.2.1)
|
|
33
33
|
rake (< 14.0)
|
|
34
34
|
ransack (~> 4.2.1)
|
|
35
|
-
rdoc (>= 5.1, <
|
|
35
|
+
rdoc (>= 5.1, < 9.0)
|
|
36
36
|
roadie-rails
|
|
37
37
|
rqrcode
|
|
38
38
|
sass-rails
|
|
@@ -119,7 +119,7 @@ GEM
|
|
|
119
119
|
minitest (>= 5.1)
|
|
120
120
|
securerandom (>= 0.3)
|
|
121
121
|
tzinfo (~> 2.0, >= 2.0.5)
|
|
122
|
-
acts_as_list (1.2.
|
|
122
|
+
acts_as_list (1.2.6)
|
|
123
123
|
activerecord (>= 6.1)
|
|
124
124
|
activesupport (>= 6.1)
|
|
125
125
|
acts_as_tree (2.9.1)
|
|
@@ -131,7 +131,8 @@ GEM
|
|
|
131
131
|
benchmark (0.5.0)
|
|
132
132
|
bigdecimal (4.1.2)
|
|
133
133
|
builder (3.3.0)
|
|
134
|
-
byebug (
|
|
134
|
+
byebug (13.0.0)
|
|
135
|
+
reline (>= 0.6.0)
|
|
135
136
|
capybara (3.40.0)
|
|
136
137
|
addressable
|
|
137
138
|
matrix
|
|
@@ -146,11 +147,12 @@ GEM
|
|
|
146
147
|
chunky_png (1.4.0)
|
|
147
148
|
cliver (0.3.2)
|
|
148
149
|
coderay (1.1.3)
|
|
149
|
-
concurrent-ruby (1.3.
|
|
150
|
+
concurrent-ruby (1.3.7)
|
|
150
151
|
connection_pool (3.0.2)
|
|
151
|
-
crass (1.0.
|
|
152
|
-
css_parser (
|
|
152
|
+
crass (1.0.7)
|
|
153
|
+
css_parser (3.0.0)
|
|
153
154
|
addressable
|
|
155
|
+
ssrf_filter (~> 1.5)
|
|
154
156
|
csso-rails (0.9.0)
|
|
155
157
|
execjs (>= 1)
|
|
156
158
|
csv (3.3.0)
|
|
@@ -181,11 +183,11 @@ GEM
|
|
|
181
183
|
erb (6.0.4)
|
|
182
184
|
erubi (1.13.1)
|
|
183
185
|
execjs (2.10.0)
|
|
184
|
-
factory_bot (6.
|
|
185
|
-
activesupport (>=
|
|
186
|
-
factory_bot_rails (6.
|
|
186
|
+
factory_bot (6.6.0)
|
|
187
|
+
activesupport (>= 6.1.0)
|
|
188
|
+
factory_bot_rails (6.5.1)
|
|
187
189
|
factory_bot (~> 6.5)
|
|
188
|
-
railties (>=
|
|
190
|
+
railties (>= 6.1.0)
|
|
189
191
|
ffi (1.17.0-aarch64-linux-gnu)
|
|
190
192
|
ffi (1.17.0-aarch64-linux-musl)
|
|
191
193
|
ffi (1.17.0-arm-linux-gnu)
|
|
@@ -216,7 +218,7 @@ GEM
|
|
|
216
218
|
csv
|
|
217
219
|
mini_mime (>= 1.0.0)
|
|
218
220
|
multi_xml (>= 0.5.2)
|
|
219
|
-
i18n (1.
|
|
221
|
+
i18n (1.15.2)
|
|
220
222
|
concurrent-ruby (~> 1.0)
|
|
221
223
|
image_processing (1.13.0)
|
|
222
224
|
mini_magick (>= 4.9.5, < 5)
|
|
@@ -246,7 +248,7 @@ GEM
|
|
|
246
248
|
libv8-node (18.19.0.0-x86_64-linux)
|
|
247
249
|
libv8-node (18.19.0.0-x86_64-linux-musl)
|
|
248
250
|
logger (1.7.0)
|
|
249
|
-
loofah (2.25.
|
|
251
|
+
loofah (2.25.2)
|
|
250
252
|
crass (~> 1.0.2)
|
|
251
253
|
nokogiri (>= 1.12.0)
|
|
252
254
|
mail (2.8.1)
|
|
@@ -284,46 +286,47 @@ GEM
|
|
|
284
286
|
net-smtp (0.5.1)
|
|
285
287
|
net-protocol
|
|
286
288
|
nio4r (2.7.4)
|
|
287
|
-
nokogiri (1.19.
|
|
289
|
+
nokogiri (1.19.4)
|
|
288
290
|
mini_portile2 (~> 2.8.2)
|
|
289
291
|
racc (~> 1.4)
|
|
290
|
-
nokogiri (1.19.
|
|
292
|
+
nokogiri (1.19.4-aarch64-linux-gnu)
|
|
291
293
|
racc (~> 1.4)
|
|
292
|
-
nokogiri (1.19.
|
|
294
|
+
nokogiri (1.19.4-aarch64-linux-musl)
|
|
293
295
|
racc (~> 1.4)
|
|
294
|
-
nokogiri (1.19.
|
|
296
|
+
nokogiri (1.19.4-arm-linux-gnu)
|
|
295
297
|
racc (~> 1.4)
|
|
296
|
-
nokogiri (1.19.
|
|
298
|
+
nokogiri (1.19.4-arm-linux-musl)
|
|
297
299
|
racc (~> 1.4)
|
|
298
|
-
nokogiri (1.19.
|
|
300
|
+
nokogiri (1.19.4-arm64-darwin)
|
|
299
301
|
racc (~> 1.4)
|
|
300
|
-
nokogiri (1.19.
|
|
302
|
+
nokogiri (1.19.4-x86_64-darwin)
|
|
301
303
|
racc (~> 1.4)
|
|
302
|
-
nokogiri (1.19.
|
|
304
|
+
nokogiri (1.19.4-x86_64-linux-gnu)
|
|
303
305
|
racc (~> 1.4)
|
|
304
|
-
nokogiri (1.19.
|
|
306
|
+
nokogiri (1.19.4-x86_64-linux-musl)
|
|
305
307
|
racc (~> 1.4)
|
|
306
308
|
orm_adapter (0.5.0)
|
|
307
309
|
paper_trail (16.0.0)
|
|
308
310
|
activerecord (>= 6.1)
|
|
309
311
|
request_store (~> 1.4)
|
|
310
|
-
paper_trail-association_tracking (2.
|
|
312
|
+
paper_trail-association_tracking (2.3.0)
|
|
311
313
|
paper_trail (>= 12.0)
|
|
312
314
|
poltergeist (1.18.1)
|
|
313
315
|
capybara (>= 2.1, < 4)
|
|
314
316
|
cliver (~> 0.3.1)
|
|
315
317
|
websocket-driver (>= 0.2.0)
|
|
316
|
-
pp (0.6.
|
|
318
|
+
pp (0.6.4)
|
|
317
319
|
prettyprint
|
|
318
320
|
prettyprint (0.2.0)
|
|
319
321
|
prism (1.9.0)
|
|
320
|
-
pry (0.
|
|
322
|
+
pry (0.16.0)
|
|
321
323
|
coderay (~> 1.1)
|
|
322
324
|
method_source (~> 1.0)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
325
|
+
reline (>= 0.6.0)
|
|
326
|
+
pry-byebug (3.12.0)
|
|
327
|
+
byebug (~> 13.0)
|
|
328
|
+
pry (>= 0.13, < 0.17)
|
|
329
|
+
psych (5.4.0)
|
|
327
330
|
date
|
|
328
331
|
stringio
|
|
329
332
|
public_suffix (7.0.5)
|
|
@@ -357,8 +360,8 @@ GEM
|
|
|
357
360
|
activesupport (>= 5.0.0)
|
|
358
361
|
minitest
|
|
359
362
|
nokogiri (>= 1.6)
|
|
360
|
-
rails-html-sanitizer (1.7.
|
|
361
|
-
loofah (~> 2.25)
|
|
363
|
+
rails-html-sanitizer (1.7.1)
|
|
364
|
+
loofah (~> 2.25, >= 2.25.2)
|
|
362
365
|
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
|
363
366
|
rails-observers (0.1.5)
|
|
364
367
|
activemodel (>= 4.0)
|
|
@@ -375,9 +378,14 @@ GEM
|
|
|
375
378
|
activerecord (>= 6.1.5)
|
|
376
379
|
activesupport (>= 6.1.5)
|
|
377
380
|
i18n
|
|
378
|
-
|
|
381
|
+
rbs (4.0.3)
|
|
382
|
+
logger
|
|
383
|
+
prism (>= 1.6.0)
|
|
384
|
+
tsort
|
|
385
|
+
rdoc (8.0.0)
|
|
379
386
|
erb
|
|
380
|
-
|
|
387
|
+
prism (>= 1.6.0)
|
|
388
|
+
rbs (>= 4.0.0)
|
|
381
389
|
tsort
|
|
382
390
|
regexp_parser (2.9.2)
|
|
383
391
|
reline (0.6.3)
|
|
@@ -387,17 +395,17 @@ GEM
|
|
|
387
395
|
responders (3.2.0)
|
|
388
396
|
actionpack (>= 7.0)
|
|
389
397
|
railties (>= 7.0)
|
|
390
|
-
roadie (5.
|
|
391
|
-
css_parser (
|
|
398
|
+
roadie (5.3.1)
|
|
399
|
+
css_parser (>= 1.4, < 4.0)
|
|
392
400
|
nokogiri (~> 1.15)
|
|
393
|
-
roadie-rails (3.
|
|
394
|
-
railties (>= 5.1, < 8.
|
|
401
|
+
roadie-rails (3.4.0)
|
|
402
|
+
railties (>= 5.1, < 8.2)
|
|
395
403
|
roadie (~> 5.0)
|
|
396
404
|
rotp (6.3.0)
|
|
397
|
-
rqrcode (3.
|
|
405
|
+
rqrcode (3.2.0)
|
|
398
406
|
chunky_png (~> 1.0)
|
|
399
407
|
rqrcode_core (~> 2.0)
|
|
400
|
-
rqrcode_core (2.
|
|
408
|
+
rqrcode_core (2.1.0)
|
|
401
409
|
rspec-core (3.13.4)
|
|
402
410
|
rspec-support (~> 3.13.0)
|
|
403
411
|
rspec-expectations (3.13.5)
|
|
@@ -415,6 +423,8 @@ GEM
|
|
|
415
423
|
rspec-mocks (~> 3.13)
|
|
416
424
|
rspec-support (~> 3.13)
|
|
417
425
|
rspec-support (3.13.4)
|
|
426
|
+
rspec_junit_formatter (0.6.0)
|
|
427
|
+
rspec-core (>= 2, < 4, != 2.12.0)
|
|
418
428
|
ruby-vips (2.2.2)
|
|
419
429
|
ffi (~> 1.12)
|
|
420
430
|
logger
|
|
@@ -443,6 +453,7 @@ GEM
|
|
|
443
453
|
actionpack (>= 6.1)
|
|
444
454
|
activesupport (>= 6.1)
|
|
445
455
|
sprockets (>= 3.0.0)
|
|
456
|
+
ssrf_filter (1.5.0)
|
|
446
457
|
stringex (2.8.6)
|
|
447
458
|
stringio (3.2.0)
|
|
448
459
|
temple (0.10.3)
|
|
@@ -456,11 +467,11 @@ GEM
|
|
|
456
467
|
concurrent-ruby (~> 1.0)
|
|
457
468
|
uglifier (4.2.1)
|
|
458
469
|
execjs (>= 0.3.0, < 3)
|
|
459
|
-
uri (1.0.
|
|
470
|
+
uri (1.0.4)
|
|
460
471
|
useragent (0.16.11)
|
|
461
472
|
warden (1.2.9)
|
|
462
473
|
rack (>= 2.0.9)
|
|
463
|
-
websocket-driver (0.8.
|
|
474
|
+
websocket-driver (0.8.2)
|
|
464
475
|
base64
|
|
465
476
|
websocket-extensions (>= 0.1.0)
|
|
466
477
|
websocket-extensions (0.1.5)
|
|
@@ -486,19 +497,20 @@ DEPENDENCIES
|
|
|
486
497
|
acts_as_list
|
|
487
498
|
database_cleaner
|
|
488
499
|
devise-two-factor
|
|
489
|
-
factory_bot_rails (= 6.
|
|
500
|
+
factory_bot_rails (= 6.5.1)
|
|
490
501
|
file_validators
|
|
491
502
|
jsbundling-rails (~> 1.3)
|
|
492
503
|
launchy (~> 3.0.1)
|
|
493
504
|
mysql2
|
|
494
505
|
paper_trail (~> 16.0.0)
|
|
495
|
-
paper_trail-association_tracking (~> 2.
|
|
506
|
+
paper_trail-association_tracking (~> 2.3.0)
|
|
496
507
|
poltergeist (~> 1.18.1)
|
|
497
508
|
pry-byebug
|
|
498
|
-
psych (= 5.
|
|
509
|
+
psych (= 5.4.0)
|
|
499
510
|
rails-observers
|
|
500
511
|
ransack
|
|
501
512
|
rspec-rails (~> 7.1.1)
|
|
513
|
+
rspec_junit_formatter
|
|
502
514
|
simplecov
|
|
503
515
|
simplecov-lcov
|
|
504
516
|
trusty-cms!
|
data/README.md
CHANGED
|
@@ -12,6 +12,8 @@ Rails engine and is built to be installed into an existing Rails 7 application a
|
|
|
12
12
|
|
|
13
13
|
CircleCI: [](https://circleci.com/gh/pgharts/trusty-cms/tree/master)
|
|
14
14
|
|
|
15
|
+
Coverage: [](https://qlty.sh/gh/pgharts/projects/trusty-cms)
|
|
16
|
+
|
|
15
17
|
Qlty: [](https://qlty.sh/gh/pgharts/projects/trusty-cms)
|
|
16
18
|
|
|
17
19
|
TrustyCMS features:
|
|
@@ -42,7 +42,7 @@ class Admin::ResourceController < ApplicationController
|
|
|
42
42
|
r.update.publish(:xml, :json) { head :ok }
|
|
43
43
|
r.update.default { redirect_to redirect_url }
|
|
44
44
|
|
|
45
|
-
r.destroy.publish(:xml, :json) { head :
|
|
45
|
+
r.destroy.publish(:xml, :json) { head :ok }
|
|
46
46
|
r.destroy.default { redirect_to redirect_url }
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -5,6 +5,14 @@ class ApplicationController < ActionController::Base
|
|
|
5
5
|
prepend_view_path("#{TRUSTY_CMS_ROOT}/app/views")
|
|
6
6
|
|
|
7
7
|
protect_from_forgery with: :exception
|
|
8
|
+
# Capture the request host before authentication runs so the site-scope logout
|
|
9
|
+
# telemetry (issue #1040) can compare the host's site to the (global) current
|
|
10
|
+
# site. Thread.current is per-thread and safe across concurrent requests —
|
|
11
|
+
# unlike the class-level Page.current_site the telemetry is investigating.
|
|
12
|
+
# An around_action (prepended so it wraps authenticate_user!) guarantees the
|
|
13
|
+
# value is cleared even if the request raises, so it can't leak into the next
|
|
14
|
+
# request handled by the same thread.
|
|
15
|
+
prepend_around_action :with_request_host_for_telemetry
|
|
8
16
|
before_action :authenticate_user!
|
|
9
17
|
before_action :configure_permitted_parameters, if: :devise_controller?
|
|
10
18
|
before_action :set_timezone
|
|
@@ -53,6 +61,13 @@ class ApplicationController < ActionController::Base
|
|
|
53
61
|
|
|
54
62
|
private
|
|
55
63
|
|
|
64
|
+
def with_request_host_for_telemetry
|
|
65
|
+
Thread.current[:trusty_request_host] = request.host
|
|
66
|
+
yield
|
|
67
|
+
ensure
|
|
68
|
+
Thread.current[:trusty_request_host] = nil
|
|
69
|
+
end
|
|
70
|
+
|
|
56
71
|
def set_mailer
|
|
57
72
|
ActionMailer::Base.default_url_options[:host] = request.host_with_port
|
|
58
73
|
end
|
data/app/models/asset.rb
CHANGED
|
@@ -83,8 +83,17 @@ class Asset < ActiveRecord::Base
|
|
|
83
83
|
self[:asset_file_size]
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
def active_storage_styles
|
|
87
|
+
asset_type&.active_storage_styles || {}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def style_dimensions(style_name)
|
|
91
|
+
asset_type&.style_dimensions(style_name)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def style_format(style_name)
|
|
95
|
+
asset_type&.style_format(style_name)
|
|
96
|
+
end
|
|
88
97
|
|
|
89
98
|
def pdf?
|
|
90
99
|
content_type == 'application/pdf'
|
|
@@ -340,13 +349,28 @@ class Asset < ActiveRecord::Base
|
|
|
340
349
|
|
|
341
350
|
# this is a convenience for image-pickers
|
|
342
351
|
def self.thumbnail_options
|
|
343
|
-
|
|
352
|
+
# The :original style is represented by the explicit entry prepended below,
|
|
353
|
+
# so skip it here to avoid a duplicate (and blank) "original" option.
|
|
354
|
+
asset_sizes = thumbnail_sizes.reject { |k, _| k.to_sym == :original }.map do |k, v|
|
|
344
355
|
size_id = k
|
|
345
|
-
size_description = "#{k}: "
|
|
346
|
-
size_description << (v.is_a?(Array) ? v.join(' as ') : v)
|
|
356
|
+
size_description = "#{k}: #{describe_style(v)}"
|
|
347
357
|
[size_description, size_id]
|
|
348
358
|
end.sort_by { |pair| pair.last.to_s }
|
|
349
359
|
asset_sizes.unshift ['Original (as uploaded)', 'original']
|
|
350
360
|
asset_sizes
|
|
351
361
|
end
|
|
362
|
+
|
|
363
|
+
# Renders a style definition into a short human-readable description for a
|
|
364
|
+
# picker. ActiveStorage styles are hashes (e.g. {geometry: '100x100#',
|
|
365
|
+
# format: :png}); older definitions may be arrays or plain strings.
|
|
366
|
+
def self.describe_style(style)
|
|
367
|
+
case style
|
|
368
|
+
when Hash
|
|
369
|
+
[style[:geometry], style[:format]].compact.join(' as ')
|
|
370
|
+
when Array
|
|
371
|
+
style.join(' as ')
|
|
372
|
+
else
|
|
373
|
+
style.to_s
|
|
374
|
+
end
|
|
375
|
+
end
|
|
352
376
|
end
|
data/app/models/standard_tags.rb
CHANGED
|
@@ -52,8 +52,7 @@ module StandardTags
|
|
|
52
52
|
}
|
|
53
53
|
tag 'children:count' do |tag|
|
|
54
54
|
options = children_find_options(tag)
|
|
55
|
-
|
|
56
|
-
tag.locals.children.count(options)
|
|
55
|
+
tag.locals.children.where(options[:conditions]).count
|
|
57
56
|
end
|
|
58
57
|
|
|
59
58
|
desc %{
|
|
@@ -66,7 +65,7 @@ module StandardTags
|
|
|
66
65
|
}
|
|
67
66
|
tag 'children:first' do |tag|
|
|
68
67
|
options = children_find_options(tag)
|
|
69
|
-
children = tag.locals.children.where(options)
|
|
68
|
+
children = tag.locals.children.where(options[:conditions]).order(options[:order])
|
|
70
69
|
if first = children.first
|
|
71
70
|
tag.locals.page = first
|
|
72
71
|
tag.expand
|
|
@@ -83,7 +82,7 @@ module StandardTags
|
|
|
83
82
|
}
|
|
84
83
|
tag 'children:last' do |tag|
|
|
85
84
|
options = children_find_options(tag)
|
|
86
|
-
children = tag.locals.children.where(options)
|
|
85
|
+
children = tag.locals.children.where(options[:conditions]).order(options[:order])
|
|
87
86
|
if last = children.last
|
|
88
87
|
tag.locals.page = last
|
|
89
88
|
tag.expand
|
|
@@ -942,7 +941,14 @@ module StandardTags
|
|
|
942
941
|
paging = pagination_find_options(tag)
|
|
943
942
|
result = []
|
|
944
943
|
tag.locals.previous_headers = {}
|
|
945
|
-
|
|
944
|
+
scoped = findable.where(options[:conditions]).order(options[:order])
|
|
945
|
+
if paging
|
|
946
|
+
displayed_children = scoped.paginate(paging)
|
|
947
|
+
else
|
|
948
|
+
scoped = scoped.limit(options[:limit]) if options[:limit]
|
|
949
|
+
scoped = scoped.offset(options[:offset]) if options[:offset]
|
|
950
|
+
displayed_children = scoped
|
|
951
|
+
end
|
|
946
952
|
displayed_children.each_with_index do |item, i|
|
|
947
953
|
tag.locals.child = item
|
|
948
954
|
tag.locals.page = item
|
|
@@ -989,13 +995,13 @@ module StandardTags
|
|
|
989
995
|
|
|
990
996
|
status = attr[:status]
|
|
991
997
|
if status == 'all'
|
|
992
|
-
options[:conditions] = ['virtual = ?', false]
|
|
998
|
+
options[:conditions] = ['`virtual` = ?', false]
|
|
993
999
|
else
|
|
994
1000
|
stat = Status[status]
|
|
995
1001
|
if stat.nil?
|
|
996
1002
|
raise TagError.new(%{`status' attribute of `each' tag must be set to a valid status})
|
|
997
1003
|
else
|
|
998
|
-
options[:conditions] = ['(virtual = ?) and (status_id = ?)', false, stat.id]
|
|
1004
|
+
options[:conditions] = ['(`virtual` = ?) and (status_id = ?)', false, stat.id]
|
|
999
1005
|
end
|
|
1000
1006
|
end
|
|
1001
1007
|
options
|
data/app/models/user.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'trusty_cms/site_scope_auth_reporter'
|
|
2
|
+
|
|
1
3
|
class User < ActiveRecord::Base
|
|
2
4
|
has_many :pages, foreign_key: :created_by_id
|
|
3
5
|
self.table_name = 'admins'
|
|
@@ -78,4 +80,15 @@ class User < ActiveRecord::Base
|
|
|
78
80
|
errors.add :password, 'Complexity requirement not met. Length should be 12 characters and include: 1 uppercase, 1 lowercase, 1 digit and 1 special character.'
|
|
79
81
|
end
|
|
80
82
|
|
|
83
|
+
# Observe-only instrument for the intermittent logout bug (issue #1040). This
|
|
84
|
+
# keeps Devise's exact (site-scoped) behavior — it returns the scoped lookup
|
|
85
|
+
# unchanged, so a mismatch still logs the user out exactly as before — and only
|
|
86
|
+
# reports when a valid user was excluded by the site scope. See
|
|
87
|
+
# TrustyCms::SiteScopeAuthReporter. The behavior-change fix is separate (PR #1041).
|
|
88
|
+
def self.serialize_from_session(key, salt)
|
|
89
|
+
record = to_adapter.get(key)
|
|
90
|
+
TrustyCms::SiteScopeAuthReporter.report_miss(key) if record.nil?
|
|
91
|
+
record if record && record.authenticatable_salt == salt
|
|
92
|
+
end
|
|
93
|
+
|
|
81
94
|
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
module TrustyCms
|
|
2
|
+
# Observe-only telemetry for the intermittent CMS logout bug (diagnostic for
|
|
3
|
+
# issue #1040). It changes NO behavior.
|
|
4
|
+
#
|
|
5
|
+
# When the multi_site extension is active, `User` carries a site-scoped
|
|
6
|
+
# default_scope (an INNER JOIN on admins_sites for `Page.current_site`).
|
|
7
|
+
# Devise deserializes the session user every request through that scope, so if
|
|
8
|
+
# `current_site` points at a site the user is not assigned to, the lookup
|
|
9
|
+
# returns nil and the user is logged out. Because `current_site` is
|
|
10
|
+
# process-global mutable state shared across Puma threads, we suspect this is
|
|
11
|
+
# usually a race (the user IS on a site they belong to, but the global was
|
|
12
|
+
# clobbered by a concurrent request for another host).
|
|
13
|
+
#
|
|
14
|
+
# This reporter fires only when a session lookup misses BUT the user actually
|
|
15
|
+
# exists (unscoped) — i.e. a valid user was excluded by the site scope — and
|
|
16
|
+
# records enough context to tell a race from a genuine cross-site access or an
|
|
17
|
+
# unmatched-host fallback. It never raises into the auth path.
|
|
18
|
+
module SiteScopeAuthReporter
|
|
19
|
+
MESSAGE = 'CMS site-scope logout (possible current_site race)'.freeze
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
def report_miss(key)
|
|
24
|
+
return unless multi_site_scoped?
|
|
25
|
+
|
|
26
|
+
excluded = User.unscoped { User.to_adapter.get(key) }
|
|
27
|
+
return if excluded.nil? # ordinary unknown/expired session — not our bug
|
|
28
|
+
|
|
29
|
+
context = build_context(excluded)
|
|
30
|
+
Rails.logger.warn("[multi_site] #{MESSAGE} #{context.to_json}")
|
|
31
|
+
Honeybadger.notify(MESSAGE, context: context) if defined?(Honeybadger)
|
|
32
|
+
nil
|
|
33
|
+
rescue StandardError => e
|
|
34
|
+
# Telemetry must never break authentication.
|
|
35
|
+
Rails.logger.error("[multi_site] SiteScopeAuthReporter error: #{e.class}: #{e.message}")
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Only meaningful when User is actually site-scoped by the multi_site
|
|
40
|
+
# extension; otherwise the scoped and unscoped lookups are identical.
|
|
41
|
+
def multi_site_scoped?
|
|
42
|
+
User.respond_to?(:is_site_scoped?) && User.is_site_scoped?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def build_context(user)
|
|
46
|
+
current = current_site
|
|
47
|
+
host = Thread.current[:trusty_request_host]
|
|
48
|
+
expected = expected_site_for(host)
|
|
49
|
+
site_ids = user.site_ids
|
|
50
|
+
|
|
51
|
+
{
|
|
52
|
+
user_id: user.id,
|
|
53
|
+
user_admin: user.admin?,
|
|
54
|
+
user_site_ids: site_ids,
|
|
55
|
+
current_site_id: current&.id,
|
|
56
|
+
current_site_name: current&.name,
|
|
57
|
+
current_site_domain_blank: current.present? && current.domain.blank?,
|
|
58
|
+
request_host: host,
|
|
59
|
+
expected_site_id: expected&.id,
|
|
60
|
+
expected_site_name: expected&.name,
|
|
61
|
+
classification: classify(site_ids, current, expected, host),
|
|
62
|
+
thread: Thread.current.object_id,
|
|
63
|
+
at: Time.now.utc.iso8601,
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# race -> user belongs to the host's site, but current_site is
|
|
68
|
+
# something else (the global was clobbered)
|
|
69
|
+
# genuine_cross_site -> user hit a host whose site they are not assigned to
|
|
70
|
+
# host_unmatched -> request host matched no site (asset/unknown host);
|
|
71
|
+
# current_site falls back to the default site
|
|
72
|
+
# no_host_captured -> ran outside a captured request (e.g. rake/console)
|
|
73
|
+
def classify(site_ids, current, expected, host)
|
|
74
|
+
return 'no_host_captured' if host.blank?
|
|
75
|
+
return 'host_unmatched' if expected.nil?
|
|
76
|
+
return 'race' if site_ids.include?(expected.id) && (current.nil? || site_ids.exclude?(current.id))
|
|
77
|
+
return 'genuine_cross_site' if site_ids.exclude?(expected.id)
|
|
78
|
+
|
|
79
|
+
'unknown'
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def current_site
|
|
83
|
+
Page.current_site if Page.respond_to?(:current_site)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Mirrors Site.find_for_host's matching but WITHOUT the Site.default /
|
|
87
|
+
# catchall fallback, so telemetry never creates a record and returns nil when
|
|
88
|
+
# the host matches no site (which is what drives the host_unmatched
|
|
89
|
+
# classification). A blank domain compiles to a regexp that matches every
|
|
90
|
+
# host, so blank domains are matched only by exact base_domain, never by the
|
|
91
|
+
# regexp.
|
|
92
|
+
def expected_site_for(host)
|
|
93
|
+
return nil if host.blank?
|
|
94
|
+
|
|
95
|
+
Site.all.detect do |site|
|
|
96
|
+
host == site.base_domain || domain_regexp_match?(site, host)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def domain_regexp_match?(site, host)
|
|
101
|
+
return false if site.domain.blank?
|
|
102
|
+
|
|
103
|
+
!(host =~ Regexp.compile(site.domain)).nil?
|
|
104
|
+
rescue RegexpError
|
|
105
|
+
false
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
data/lib/trusty_cms/version.rb
CHANGED