trusty-cms 7.0.39 → 7.0.40

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +6 -7
  4. data/README.md +25 -0
  5. data/app/assets/builds/trusty_cms/ckeditor5.css +6966 -0
  6. data/app/assets/builds/trusty_cms/ckeditor5.css.map +7 -0
  7. data/app/assets/builds/trusty_cms/ckeditor5.js +105634 -0
  8. data/app/assets/builds/trusty_cms/ckeditor5.js.map +7 -0
  9. data/app/assets/config/trusty-cms-manifest.js +2 -0
  10. data/app/assets/javascripts/admin.js +0 -1
  11. data/app/assets/stylesheets/admin/_ckeditor.css +109 -0
  12. data/app/assets/stylesheets/admin/main.scss +1 -0
  13. data/app/assets/stylesheets/admin/partials/_content.scss +2 -0
  14. data/app/controllers/admin/assets_controller.rb +78 -23
  15. data/app/controllers/admin/pages_controller.rb +31 -0
  16. data/app/javascript/trusty_cms/ckeditor5.js +391 -0
  17. data/app/models/asset.rb +3 -1
  18. data/app/views/admin/page_parts/_page_part.html.haml +13 -9
  19. data/app/views/layouts/application.html.haml +6 -0
  20. data/config/routes.rb +3 -0
  21. data/lib/generators/trusty_cms/templates/application.rb.erb +0 -1
  22. data/lib/trusty_cms/engine.rb +5 -8
  23. data/lib/trusty_cms/version.rb +1 -1
  24. data/lib/trusty_cms.rb +2 -0
  25. data/package.json +20 -2
  26. data/spec/controllers/assets_controller.rb +66 -0
  27. data/spec/dummy/config/application.rb +1 -1
  28. data/spec/dummy/config/storage.yml +7 -0
  29. data/spec/dummy/db/schema.rb +24 -20
  30. data/spec/fixtures/files/sample.ics +10 -0
  31. data/spec/fixtures/files/sample.txt +1 -0
  32. data/spec/models/asset_spec.rb +39 -0
  33. data/trusty_cms.gemspec +1 -1
  34. data/yarn.lock +2213 -493
  35. metadata +21 -15
  36. data/app/assets/javascripts/ckeditor/config.js +0 -22
  37. data/app/assets/javascripts/ckeditor/contents.css +0 -0
@@ -5,3 +5,5 @@
5
5
  //= link admin/assets_admin.js
6
6
  //= link admin/custom_file_upload.js
7
7
  //= link admin/validations/scheduled_status_validation.js
8
+ //= link trusty_cms/ckeditor5.js
9
+ //= link trusty_cms/ckeditor5.css
@@ -17,7 +17,6 @@
17
17
  //= require 'jquery-validation/dist/jquery.validate.min'
18
18
  //= require jquery-treetable/jquery.treetable.js
19
19
  //= require_tree './admin/validations'
20
- //= require ckeditor/init
21
20
  //= require 'admin/persist.min'
22
21
  //= require 'admin/modernizr'
23
22
  //= require 'admin/datecheck'
@@ -0,0 +1,109 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=Oswald&family=PT+Serif:ital,wght@0,400;0,700;1,400&display=swap');
2
+ @import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap');
3
+
4
+ @media print {
5
+ body {
6
+ margin: 0 !important;
7
+ }
8
+ }
9
+
10
+ :root {
11
+ --ck-content-font-family: 'Lato';
12
+ }
13
+
14
+ .main-container {
15
+ font-family: var(--ck-content-font-family);
16
+ width: fit-content;
17
+ /* margin-left: auto;
18
+ margin-right: auto; */
19
+ }
20
+
21
+ .editor-container_classic-editor .editor-container__editor {
22
+ min-width: 750px;
23
+ max-width: 1300px;
24
+ }
25
+
26
+ .ck-content {
27
+ height: 600px;
28
+ }
29
+
30
+ .ck-content h3.category {
31
+ font-family: 'Oswald';
32
+ font-size: 20px;
33
+ font-weight: bold;
34
+ color: #555;
35
+ letter-spacing: 10px;
36
+ margin: 0;
37
+ padding: 0;
38
+ }
39
+
40
+ .ck-content h2.document-title {
41
+ font-family: 'Oswald';
42
+ font-size: 50px;
43
+ font-weight: bold;
44
+ margin: 0;
45
+ padding: 0;
46
+ border: 0;
47
+ }
48
+
49
+ .ck-content h3.document-subtitle {
50
+ font-family: 'Oswald';
51
+ font-size: 20px;
52
+ color: #555;
53
+ margin: 0 0 1em;
54
+ font-weight: bold;
55
+ padding: 0;
56
+ }
57
+
58
+ .ck-content p.info-box {
59
+ --background-size: 30px;
60
+ --background-color: #e91e63;
61
+ padding: 1.2em 2em;
62
+ border: 1px solid var(--background-color);
63
+ background:
64
+ linear-gradient(135deg, var(--background-color) 0%, var(--background-color) var(--background-size), transparent var(--background-size)),
65
+ linear-gradient(
66
+ 135deg,
67
+ transparent calc(100% - var(--background-size)),
68
+ var(--background-color) calc(100% - var(--background-size)),
69
+ var(--background-color)
70
+ );
71
+ border-radius: 10px;
72
+ margin: 1.5em 2em;
73
+ box-shadow: 5px 5px 0 #ffe6ef;
74
+ }
75
+
76
+ .ck-content span.marker {
77
+ background: yellow;
78
+ }
79
+
80
+ .ck-content span.spoiler {
81
+ background: #000;
82
+ color: #000;
83
+ }
84
+
85
+ .ck-content span.spoiler:hover {
86
+ background: #000;
87
+ color: #fff;
88
+ }
89
+
90
+ .ck-content .button {
91
+ display: inline-block;
92
+ width: 260px;
93
+ border-radius: 8px;
94
+ margin: 0 auto;
95
+ padding: 12px;
96
+ color: #ffffff;
97
+ font-size: 24px;
98
+ font-weight: 700;
99
+ text-align: center;
100
+ text-decoration: none;
101
+ }
102
+
103
+ .ck-content .button--green {
104
+ background-color: #406b1e;
105
+ }
106
+
107
+ .ck-content .button--black {
108
+ background-color: #141517;
109
+ }
@@ -4,6 +4,7 @@
4
4
  @import "base";
5
5
  @import "reset";
6
6
  @import "tablesaw/dist/stackonly/tablesaw.stackonly.scss";
7
+ @import "ckeditor";
7
8
 
8
9
  // Partials
9
10
  @import "partials/typography";
@@ -32,6 +32,7 @@ body.reversed {
32
32
  right: 0;
33
33
  top: 0;
34
34
  width: 100%;
35
+ z-index: 5;
35
36
  }
36
37
 
37
38
  .clipped {
@@ -52,6 +53,7 @@ body.reversed {
52
53
  position: fixed;
53
54
  right: 20px;
54
55
  top: 10px;
56
+ z-index: 5;
55
57
 
56
58
  &:hover {
57
59
  opacity: 1;
@@ -1,6 +1,7 @@
1
1
  class Admin::AssetsController < Admin::ResourceController
2
2
  paginate_models(per_page: 50)
3
3
  COMPRESS_FILE_TYPE = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml'].freeze
4
+ APPROVED_CONTENT_TYPES = Asset::APPROVED_CONTENT_TYPES
4
5
 
5
6
  def index
6
7
  assets = Asset.order('created_at DESC')
@@ -28,27 +29,41 @@ class Admin::AssetsController < Admin::ResourceController
28
29
  end
29
30
  end
30
31
 
32
+ def uploader
33
+ @page_attachments = []
34
+ result = process_uploaded_asset(asset_params[:upload])
35
+
36
+ if result[:asset]
37
+ @asset = result[:asset]
38
+ if params[:for_attachment]
39
+ @page = Page.find_by_id(params[:page_id]) || Page.new
40
+ @page_attachments << (@page_attachment = @asset.page_attachments.build(page: @page))
41
+ end
42
+
43
+ render json: { url: @asset.asset.url }
44
+ else
45
+ flash[result.fetch(:flash_type, :error)] = result[:error]
46
+ render json: { error: result[:error] }, status: result.fetch(:status, :unprocessable_entity)
47
+ end
48
+ end
49
+
31
50
  def create
32
51
  @assets = []
33
52
  @page_attachments = []
34
- compress = current_site.try(:compress).nil? ? true : current_site.compress
35
- asset_params[:asset][:asset].reject(&:blank?).each do |uploaded_asset|
36
- if uploaded_asset.content_type == 'application/octet-stream'
37
- flash[:notice] = 'Please only upload assets that have a valid extension in the name.'
38
- else
39
- uploaded_asset = compress(uploaded_asset) if $kraken.api_key.present? && COMPRESS_FILE_TYPE.include?(uploaded_asset.content_type) && compress
40
- @asset = Asset.create(asset: uploaded_asset, caption: asset_params[:asset][:caption])
41
- if @asset.valid?
42
- set_owner_or_editor
43
- if params[:for_attachment]
44
- @page = Page.find_by_id(params[:page_id]) || Page.new
45
- @page_attachments << @page_attachment = @asset.page_attachments.build(page: @page)
46
- end
47
- @assets << @asset
48
- else
49
- error = @asset.errors.first.message
50
- flash[:error] = error
53
+ uploads = Array(asset_params.dig('asset', 'asset')).reject(&:blank?)
54
+
55
+ uploads.each do |uploaded_asset|
56
+ result = process_uploaded_asset(uploaded_asset)
57
+
58
+ if result[:asset]
59
+ @asset = result[:asset]
60
+ if params[:for_attachment]
61
+ @page = Page.find_by_id(params[:page_id]) || Page.new
62
+ @page_attachments << (@page_attachment = @asset.page_attachments.build(page: @page))
51
63
  end
64
+ @assets << @asset
65
+ else
66
+ flash[result.fetch(:flash_type, :error)] = result[:error]
52
67
  end
53
68
  end
54
69
 
@@ -72,6 +87,44 @@ class Admin::AssetsController < Admin::ResourceController
72
87
 
73
88
  private
74
89
 
90
+ def process_uploaded_asset(uploaded_asset)
91
+ return failure_response('No file uploaded.', :unprocessable_entity, :error) unless uploaded_asset.present?
92
+
93
+ if uploaded_asset.content_type == 'application/octet-stream'
94
+ return failure_response('Please only upload assets that have a valid extension in the name.', :unprocessable_entity, :notice)
95
+ end
96
+
97
+ unless APPROVED_CONTENT_TYPES.include?(uploaded_asset.content_type)
98
+ return failure_response('Unsupported file type.', :unsupported_media_type, :error)
99
+ end
100
+
101
+ processed_upload = maybe_compress(uploaded_asset)
102
+ asset = Asset.create(asset: processed_upload, caption: '')
103
+
104
+ if asset.valid?
105
+ set_owner_or_editor(asset)
106
+ { asset: asset }
107
+ else
108
+ failure_response(asset.errors.full_messages.first, :unprocessable_entity, :error)
109
+ end
110
+ end
111
+
112
+ def maybe_compress(uploaded_asset)
113
+ should_compress?(uploaded_asset.content_type) ? compress(uploaded_asset) : uploaded_asset
114
+ end
115
+
116
+ def should_compress?(content_type)
117
+ $kraken.api_key.present? && COMPRESS_FILE_TYPE.include?(content_type) && compression_enabled?
118
+ end
119
+
120
+ def compression_enabled?
121
+ current_site.try(:compress).nil? ? true : current_site.compress
122
+ end
123
+
124
+ def failure_response(message, status, flash_type)
125
+ { error: message, status: status, flash_type: flash_type }
126
+ end
127
+
75
128
  def compress(uploaded_asset)
76
129
  require 'open-uri'
77
130
  data = $kraken.upload(uploaded_asset.tempfile.path, 'lossy' => true)
@@ -80,13 +133,15 @@ class Admin::AssetsController < Admin::ResourceController
80
133
  uploaded_asset
81
134
  end
82
135
 
83
- def set_owner_or_editor
84
- @asset.created_by_id = current_user.id
85
- @asset.updated_by_id = current_user.id
86
- @asset.save! if @asset.id.present?
136
+ def set_owner_or_editor(asset = @asset)
137
+ return unless asset
138
+
139
+ asset.created_by_id = current_user.id
140
+ asset.updated_by_id = current_user.id
141
+ asset.save! if asset.id.present?
87
142
  end
88
143
 
89
144
  def asset_params
90
- params.permit(:id, :for_attachment, asset: [:for_attachment, { asset: [] }])
145
+ params.permit(:id, :upload, :for_attachment, asset: [:for_attachment, { asset: [] }])
91
146
  end
92
- end
147
+ end
@@ -2,6 +2,7 @@ class Admin::PagesController < Admin::ResourceController
2
2
  before_action :initialize_meta_rows_and_buttons, only: %i[new edit create update]
3
3
  before_action :count_deleted_pages, only: [:destroy]
4
4
  before_action :set_page, only: %i[edit restore]
5
+ before_action :append_editor_stylesheets, only: %i[new edit create update]
5
6
  rescue_from ActiveRecord::RecordInvalid, with: :validation_error
6
7
  include Admin::PagesHelper
7
8
  include Admin::UrlHelper
@@ -181,6 +182,36 @@ class Admin::PagesController < Admin::ResourceController
181
182
  raise PreviewStop
182
183
  end
183
184
 
185
+ def append_editor_stylesheets
186
+ return unless TrustyCms.respond_to?(:editor_stylesheets)
187
+
188
+ @stylesheets ||= []
189
+ @stylesheets |= editor_stylesheets_for_current_site
190
+ @editor_style_definitions = TrustyCms.editor_style_definitions
191
+ end
192
+
193
+ def editor_stylesheets_for_current_site
194
+ stylesheets = TrustyCms.editor_stylesheets
195
+ return stylesheets unless respond_to?(:current_site) && current_site.present?
196
+
197
+ stylesheets.filter_map do |entry|
198
+ site_key, path =
199
+ case entry
200
+ when Hash
201
+ [entry[:site], entry[:path]]
202
+ else
203
+ [nil, entry] # backward compatibility with plain strings
204
+ end
205
+
206
+ next path if site_key.blank? # global stylesheet
207
+ next path if site_key.to_s == identifier_for(current_site)
208
+ end
209
+ end
210
+
211
+ def identifier_for(site)
212
+ current_site.try(:name)&.downcase.gsub(' ', '_')
213
+ end
214
+
184
215
  def count_deleted_pages
185
216
  @count = model.children.count + 1
186
217
  end