spina 2.3.5 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d723b3ba40909c360cc1b6ddcf6f78064278f08911415db9852b38c53efa4064
4
- data.tar.gz: 8057a9dab62bb7806f041ec07ecd48c4e91e3f63683c4192df625785a49eecfc
3
+ metadata.gz: cc21967f4a24ee99e20c76c80c6816cf921f4b0a270686049e8fb736f41c7b3d
4
+ data.tar.gz: bd94d0854dd6d9e85569075ef965524f3d458820482fecb874fe2ff669ea28df
5
5
  SHA512:
6
- metadata.gz: 1e41c59c9ce94f638f51ae647ce1d92bde5c930123ada0ca766e02c79725ccb7bbc3b8a667508e813f386770762b16608b94132521faba55a3d2aaaa30133fb8
7
- data.tar.gz: 6391ea4c0b0be38de86fa46cee4057302be8be655044d33784bcdd7e5774221004ae1f1d8e9570e8e05f673fbbbff8ed586b8f47923119f9f6405586a691a8e3
6
+ metadata.gz: 41393817fa4b6e939fa74f3c09d195edfa45b3142c4a30c99a21a6e85c1e0ca6c11516e1902170849ed487d9c47c064416516230685fac412b55af54bff37563
7
+ data.tar.gz: b94ce566a3ce68541b957831193e7931738ae605b95595901f2dfa915159ae0702189127c67fe2badbd1550e3f491262e18fe4c11775e9712b484502d83088c6
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![Ruby](https://github.com/SpinaCMS/Spina/actions/workflows/ruby.yml/badge.svg)](https://github.com/SpinaCMS/Spina/actions/workflows/ruby.yml)
6
6
  [![Code Climate](https://codeclimate.com/github/SpinaCMS/Spina/badges/gpa.svg)](https://codeclimate.com/github/SpinaCMS/Spina)
7
7
  [![Test Coverage](https://codeclimate.com/github/SpinaCMS/Spina/badges/coverage.svg)](https://codeclimate.com/github/SpinaCMS/Spina/coverage)
8
- [![Slack](https://slack-spinacms.herokuapp.com/badge.svg)](https://slack-spinacms.herokuapp.com)
8
+ [![Discord](https://img.shields.io/discord/811903407525986304?label=Discord)](https://discord.gg/bv5Mu4XYcN)
9
9
 
10
10
  ## Getting Started
11
11
  [Read the guide](https://www.spinacms.com/docs) to learn more about how to use Spina. If you just want to get started, create a new Ruby on Rails app and follow these instructions:
@@ -24,6 +24,11 @@ The installer will help you create your first user.
24
24
 
25
25
  Then start `rails s` and access Spina at `/admin`.
26
26
 
27
+ ## Browser support
28
+ Spina's admin UI requires Chrome/Edge 89+, or any other browser with basic ESM support (Safari/Firefox). This is due to a recent browser feature called `import maps` and will be used in Rails 7 as the default way to deploy javascript assets.
29
+
30
+ Browser support for websites built with Spina is entirely up to the developer. Spina doesn't force you to build your frontend a certain way.
31
+
27
32
  ## Contributing
28
33
 
29
34
  Check our [Contributing Guide](CONTRIBUTING.md) for instructions on how to help the project.
@@ -63,6 +68,8 @@ Icons were designed by [@steveschoger](https://twitter.com/steveschoger) - [Hero
63
68
 
64
69
  HotKeys support via [Hotkeys.js](https://wangchujiang.com/hotkeys/)
65
70
 
71
+ Email templates based on Wildbit's [Postmark Templates](https://github.com/wildbit/postmark-templates)
72
+
66
73
  The font used in Spina's admin panel is called Metropolis and was created by Chris Simpson [Metropolis](https://github.com/chrismsimpson/Metropolis)
67
74
 
68
75
  Copyright (c) 2015, Chris Simpson <chris@victoryonemedia.com>.
@@ -2,7 +2,7 @@
2
2
  <button type="button"
3
3
  data-action="media-picker-modal#selectImage selectable#select dblclick->media-picker-modal#instantInsert"
4
4
  data-image-id="<%= @image.id %>"
5
- data-signed-blob-id="<%= @image.file.blob.signed_id %>"
5
+ data-signed-blob-id="<%= @image.file.blob&.signed_id %>"
6
6
  data-filename="<%= @image.file.filename %>"
7
7
  data-thumbnail="<%= helpers.thumbnail_url(@image) %>"
8
8
  data-embedded-url="<%= helpers.embedded_image_url(@image) %>"
@@ -0,0 +1,32 @@
1
+ <% if view_templates.many? %>
2
+
3
+ <%= render Spina::UserInterface::DropdownComponent.new do |dropdown| %>
4
+ <% dropdown.button(classes: "btn btn-primary w-full") do %>
5
+ <%= helpers.heroicon("plus", style: :solid, class: "w-7 h-7 -ml-2") %>
6
+ <%=t 'spina.pages.new' %>
7
+ <% end %>
8
+
9
+ <% dropdown.menu do %>
10
+ <% view_templates.each do |template| %>
11
+ <%= link_to helpers.spina.new_admin_page_path(view_template: template.name, resource_id: @resource&.id), class: "block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900", data: {turbo_frame: "modal", action: "reveal#hide"} do %>
12
+ <div class="font-medium text-gray-700">
13
+ <%= template.title %>
14
+
15
+ <% if template.recommended %>
16
+ <span class="text-green-500 text-xs"><%=t 'spina.pages.recommended' %></span>
17
+ <% end %>
18
+ </div>
19
+ <div class="text-gray-400"><%= template.description %></div>
20
+ <% end %>
21
+ <% end %>
22
+ <% end %>
23
+ <% end %>
24
+
25
+ <% else %>
26
+
27
+ <%= link_to helpers.spina.new_admin_page_path(view_template: view_template.name, resource_id: resource&.id), class: "btn btn-primary w-full", data: {turbo_frame: "modal"} do %>
28
+ <%= helpers.heroicon("plus", style: :solid, class: "w-7 h-7 -ml-2") %>
29
+ <%=t 'spina.pages.new' %>
30
+ <% end %>
31
+
32
+ <% end %>
@@ -0,0 +1,19 @@
1
+ module Spina::Pages
2
+ class NewPageButtonComponent < Spina::ApplicationComponent
3
+ attr_reader :view_templates, :resource
4
+
5
+ def initialize(view_templates = [], resource: nil)
6
+ @view_templates = view_templates
7
+ @resource = resource
8
+ end
9
+
10
+ def view_template
11
+ view_templates.first
12
+ end
13
+
14
+ def render?
15
+ view_templates.any?
16
+ end
17
+
18
+ end
19
+ end
@@ -8,10 +8,9 @@ module Spina
8
8
  def index
9
9
  add_breadcrumb I18n.t('spina.website.pages'), spina.admin_pages_path
10
10
 
11
-
12
11
  if params[:resource_id]
13
12
  @resource = Resource.find(params[:resource_id])
14
- @page_templates = Spina::Current.theme.new_page_templates(recommended: @resource.view_template)
13
+ @page_templates = Spina::Current.theme.new_page_templates(resource: @resource)
15
14
  @pages = @resource.pages.active.roots.includes(:translations)
16
15
  else
17
16
  @pages = Page.active.sorted.roots.main.includes(:translations)
@@ -11,10 +11,8 @@ module Spina
11
11
  def create
12
12
  user = User.find_by(email: params[:email])
13
13
 
14
- if user.present?
15
- user.regenerate_password_reset_token
16
- user.touch(:password_reset_sent_at)
17
- UserMailer.forgot_password(user).deliver_now
14
+ if user&.reset_passord!
15
+ UserMailer.forgot_password(user, request.user_agent).deliver_later
18
16
  redirect_to admin_login_path, flash: {success: t('spina.forgot_password.instructions_sent')}
19
17
  else
20
18
  flash.now[:alert] = t('spina.forgot_password.unknown_user')
@@ -32,7 +32,7 @@ module Spina
32
32
  end
33
33
 
34
34
  def content_type(image)
35
- image.file.content_type.split("/").last
35
+ image.file.content_type&.split("/")&.last || I18n.t("spina.images.missing_image")
36
36
  end
37
37
 
38
38
  end
@@ -0,0 +1,7 @@
1
+ module Spina
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default Spina.config.mailer_defaults
4
+
5
+ layout 'spina/mail'
6
+ end
7
+ end
@@ -1,21 +1,13 @@
1
1
  module Spina
2
- class UserMailer < ActionMailer::Base
3
- layout 'spina/mail'
2
+ class UserMailer < ApplicationMailer
4
3
 
5
- def forgot_password(user)
4
+ def forgot_password(user, user_agent_string = nil)
6
5
  @user = user
7
-
8
- mail(
9
- to: @user.email,
10
- from: current_account.email,
11
- subject: t('spina.forgot_password.mail_subject')
12
- )
6
+ @browser = Browser.new(user_agent_string)
7
+
8
+ mail to: @user.email,
9
+ subject: t('spina.user_mailer.forgot_password.subject')
13
10
  end
14
11
 
15
- private
16
-
17
- def current_account
18
- Spina::Account.first
19
- end
20
12
  end
21
13
  end
@@ -15,6 +15,12 @@ module Spina
15
15
  def to_s
16
16
  name
17
17
  end
18
+
19
+ def reset_passord!
20
+ regenerate_password_reset_token
21
+ self.password_reset_sent_at = Time.current
22
+ save!
23
+ end
18
24
 
19
25
  end
20
26
  end
@@ -1 +1,476 @@
1
- <%= yield %>
1
+ <!-- The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Wildbit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE. -->
22
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
23
+ <html xmlns="http://www.w3.org/1999/xhtml">
24
+ <head>
25
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
26
+ <meta name="x-apple-disable-message-reformatting" />
27
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
28
+ <meta name="color-scheme" content="light dark" />
29
+ <meta name="supported-color-schemes" content="light dark" />
30
+ <title></title>
31
+ <style type="text/css" rel="stylesheet" media="all">
32
+ /* Base ------------------------------ */
33
+
34
+ @import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&amp;display=swap");
35
+ body {
36
+ width: 100% !important;
37
+ height: 100%;
38
+ margin: 0;
39
+ -webkit-text-size-adjust: none;
40
+ }
41
+
42
+ a {
43
+ color: #3869D4;
44
+ }
45
+
46
+ a img {
47
+ border: none;
48
+ }
49
+
50
+ td {
51
+ word-break: break-word;
52
+ }
53
+
54
+ .preheader {
55
+ display: none !important;
56
+ visibility: hidden;
57
+ mso-hide: all;
58
+ font-size: 1px;
59
+ line-height: 1px;
60
+ max-height: 0;
61
+ max-width: 0;
62
+ opacity: 0;
63
+ overflow: hidden;
64
+ }
65
+ /* Type ------------------------------ */
66
+
67
+ body,
68
+ td,
69
+ th {
70
+ font-family: "Nunito Sans", Helvetica, Arial, sans-serif;
71
+ }
72
+
73
+ h1 {
74
+ margin-top: 0;
75
+ color: #333333;
76
+ font-size: 22px;
77
+ font-weight: bold;
78
+ text-align: left;
79
+ }
80
+
81
+ h2 {
82
+ margin-top: 0;
83
+ color: #333333;
84
+ font-size: 16px;
85
+ font-weight: bold;
86
+ text-align: left;
87
+ }
88
+
89
+ h3 {
90
+ margin-top: 0;
91
+ color: #333333;
92
+ font-size: 14px;
93
+ font-weight: bold;
94
+ text-align: left;
95
+ }
96
+
97
+ td,
98
+ th {
99
+ font-size: 16px;
100
+ }
101
+
102
+ p,
103
+ ul,
104
+ ol,
105
+ blockquote {
106
+ margin: .4em 0 1.1875em;
107
+ font-size: 16px;
108
+ line-height: 1.625;
109
+ }
110
+
111
+ p.sub {
112
+ font-size: 13px;
113
+ }
114
+ /* Utilities ------------------------------ */
115
+
116
+ .align-right {
117
+ text-align: right;
118
+ }
119
+
120
+ .align-left {
121
+ text-align: left;
122
+ }
123
+
124
+ .align-center {
125
+ text-align: center;
126
+ }
127
+ /* Buttons ------------------------------ */
128
+
129
+ .button {
130
+ background-color: #3869D4;
131
+ border-top: 10px solid #3869D4;
132
+ border-right: 18px solid #3869D4;
133
+ border-bottom: 10px solid #3869D4;
134
+ border-left: 18px solid #3869D4;
135
+ display: inline-block;
136
+ color: #FFF;
137
+ text-decoration: none;
138
+ border-radius: 7px;
139
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.16);
140
+ -webkit-text-size-adjust: none;
141
+ box-sizing: border-box;
142
+ }
143
+
144
+ .button--green {
145
+ background-color: #22BC66;
146
+ border-top: 10px solid #22BC66;
147
+ border-right: 18px solid #22BC66;
148
+ border-bottom: 10px solid #22BC66;
149
+ border-left: 18px solid #22BC66;
150
+ }
151
+
152
+ .button--spina {
153
+ background-color: #6865b4;
154
+ border-top: 10px solid #6865b4;
155
+ border-right: 18px solid #6865b4;
156
+ border-bottom: 10px solid #6865b4;
157
+ border-left: 18px solid #6865b4;
158
+ }
159
+
160
+ .button--red {
161
+ background-color: #FF6136;
162
+ border-top: 10px solid #FF6136;
163
+ border-right: 18px solid #FF6136;
164
+ border-bottom: 10px solid #FF6136;
165
+ border-left: 18px solid #FF6136;
166
+ }
167
+
168
+ @media only screen and (max-width: 500px) {
169
+ .button {
170
+ width: 100% !important;
171
+ text-align: center !important;
172
+ }
173
+ }
174
+ /* Attribute list ------------------------------ */
175
+
176
+ .attributes {
177
+ margin: 0 0 21px;
178
+ }
179
+
180
+ .attributes_content {
181
+ background-color: #F4F4F7;
182
+ padding: 16px;
183
+ }
184
+
185
+ .attributes_item {
186
+ padding: 0;
187
+ }
188
+ /* Related Items ------------------------------ */
189
+
190
+ .related {
191
+ width: 100%;
192
+ margin: 0;
193
+ padding: 25px 0 0 0;
194
+ -premailer-width: 100%;
195
+ -premailer-cellpadding: 0;
196
+ -premailer-cellspacing: 0;
197
+ }
198
+
199
+ .related_item {
200
+ padding: 10px 0;
201
+ color: #CBCCCF;
202
+ font-size: 15px;
203
+ line-height: 18px;
204
+ }
205
+
206
+ .related_item-title {
207
+ display: block;
208
+ margin: .5em 0 0;
209
+ }
210
+
211
+ .related_item-thumb {
212
+ display: block;
213
+ padding-bottom: 10px;
214
+ }
215
+
216
+ .related_heading {
217
+ border-top: 1px solid #CBCCCF;
218
+ text-align: center;
219
+ padding: 25px 0 10px;
220
+ }
221
+ /* Discount Code ------------------------------ */
222
+
223
+ .discount {
224
+ width: 100%;
225
+ margin: 0;
226
+ padding: 24px;
227
+ -premailer-width: 100%;
228
+ -premailer-cellpadding: 0;
229
+ -premailer-cellspacing: 0;
230
+ background-color: #F4F4F7;
231
+ border: 2px dashed #CBCCCF;
232
+ }
233
+
234
+ .discount_heading {
235
+ text-align: center;
236
+ }
237
+
238
+ .discount_body {
239
+ text-align: center;
240
+ font-size: 15px;
241
+ }
242
+ /* Social Icons ------------------------------ */
243
+
244
+ .social {
245
+ width: auto;
246
+ }
247
+
248
+ .social td {
249
+ padding: 0;
250
+ width: auto;
251
+ }
252
+
253
+ .social_icon {
254
+ height: 20px;
255
+ margin: 0 8px 10px 8px;
256
+ padding: 0;
257
+ }
258
+ /* Data table ------------------------------ */
259
+
260
+ .purchase {
261
+ width: 100%;
262
+ margin: 0;
263
+ padding: 35px 0;
264
+ -premailer-width: 100%;
265
+ -premailer-cellpadding: 0;
266
+ -premailer-cellspacing: 0;
267
+ }
268
+
269
+ .purchase_content {
270
+ width: 100%;
271
+ margin: 0;
272
+ padding: 25px 0 0 0;
273
+ -premailer-width: 100%;
274
+ -premailer-cellpadding: 0;
275
+ -premailer-cellspacing: 0;
276
+ }
277
+
278
+ .purchase_item {
279
+ padding: 10px 0;
280
+ color: #51545E;
281
+ font-size: 15px;
282
+ line-height: 18px;
283
+ }
284
+
285
+ .purchase_heading {
286
+ padding-bottom: 8px;
287
+ border-bottom: 1px solid #EAEAEC;
288
+ }
289
+
290
+ .purchase_heading p {
291
+ margin: 0;
292
+ color: #85878E;
293
+ font-size: 12px;
294
+ }
295
+
296
+ .purchase_footer {
297
+ padding-top: 15px;
298
+ border-top: 1px solid #EAEAEC;
299
+ }
300
+
301
+ .purchase_total {
302
+ margin: 0;
303
+ text-align: right;
304
+ font-weight: bold;
305
+ color: #333333;
306
+ }
307
+
308
+ .purchase_total--label {
309
+ padding: 0 15px 0 0;
310
+ }
311
+
312
+ body {
313
+ background-color: #FFF;
314
+ color: #333;
315
+ }
316
+
317
+ p {
318
+ color: #333;
319
+ }
320
+
321
+ .email-wrapper {
322
+ width: 100%;
323
+ margin: 0;
324
+ padding: 0;
325
+ -premailer-width: 100%;
326
+ -premailer-cellpadding: 0;
327
+ -premailer-cellspacing: 0;
328
+ }
329
+
330
+ .email-content {
331
+ width: 100%;
332
+ margin: 0;
333
+ padding: 0;
334
+ -premailer-width: 100%;
335
+ -premailer-cellpadding: 0;
336
+ -premailer-cellspacing: 0;
337
+ }
338
+ /* Masthead ----------------------- */
339
+
340
+ .email-masthead {
341
+ padding: 25px 0;
342
+ text-align: center;
343
+ }
344
+
345
+ .email-masthead_logo {
346
+ width: 94px;
347
+ }
348
+
349
+ .email-masthead_name {
350
+ font-size: 16px;
351
+ font-weight: bold;
352
+ color: #A8AAAF;
353
+ text-decoration: none;
354
+ text-shadow: 0 1px 0 white;
355
+ }
356
+ /* Body ------------------------------ */
357
+
358
+ .email-body {
359
+ width: 100%;
360
+ margin: 0;
361
+ padding: 0;
362
+ -premailer-width: 100%;
363
+ -premailer-cellpadding: 0;
364
+ -premailer-cellspacing: 0;
365
+ }
366
+
367
+ .email-body_inner {
368
+ width: 570px;
369
+ margin: 0 auto;
370
+ padding: 0;
371
+ -premailer-width: 570px;
372
+ -premailer-cellpadding: 0;
373
+ -premailer-cellspacing: 0;
374
+ }
375
+
376
+ .email-footer {
377
+ width: 570px;
378
+ margin: 0 auto;
379
+ padding: 0;
380
+ -premailer-width: 570px;
381
+ -premailer-cellpadding: 0;
382
+ -premailer-cellspacing: 0;
383
+ text-align: center;
384
+ }
385
+
386
+ .email-footer p {
387
+ color: #A8AAAF;
388
+ }
389
+
390
+ .body-action {
391
+ width: 100%;
392
+ margin: 30px auto;
393
+ padding: 0;
394
+ -premailer-width: 100%;
395
+ -premailer-cellpadding: 0;
396
+ -premailer-cellspacing: 0;
397
+ text-align: center;
398
+ }
399
+
400
+ .body-sub {
401
+ margin-top: 25px;
402
+ padding-top: 25px;
403
+ border-top: 1px solid #EAEAEC;
404
+ }
405
+
406
+ .content-cell {
407
+ padding: 35px;
408
+ }
409
+ /*Media Queries ------------------------------ */
410
+
411
+ @media only screen and (max-width: 600px) {
412
+ .email-body_inner,
413
+ .email-footer {
414
+ width: 100% !important;
415
+ }
416
+ }
417
+
418
+ @media (prefers-color-scheme: dark) {
419
+ body {
420
+ background-color: #333333 !important;
421
+ color: #FFF !important;
422
+ }
423
+ p,
424
+ ul,
425
+ ol,
426
+ blockquote,
427
+ h1,
428
+ h2,
429
+ h3,
430
+ span,
431
+ .purchase_item {
432
+ color: #FFF !important;
433
+ }
434
+ .attributes_content,
435
+ .discount {
436
+ background-color: #222 !important;
437
+ }
438
+ .email-masthead_name {
439
+ text-shadow: none !important;
440
+ }
441
+ }
442
+
443
+ :root {
444
+ color-scheme: light dark;
445
+ supported-color-schemes: light dark;
446
+ }
447
+ </style>
448
+ <!--[if mso]>
449
+ <style type="text/css">
450
+ .f-fallback {
451
+ font-family: Arial, sans-serif;
452
+ }
453
+ </style>
454
+ <![endif]-->
455
+ <style type="text/css" rel="stylesheet" media="all">
456
+ body {
457
+ width: 100% !important;
458
+ height: 100%;
459
+ margin: 0;
460
+ -webkit-text-size-adjust: none;
461
+ }
462
+
463
+ body {
464
+ font-family: "Nunito Sans", Helvetica, Arial, sans-serif;
465
+ }
466
+
467
+ body {
468
+ background-color: #FFF;
469
+ color: #333;
470
+ }
471
+ </style>
472
+ </head>
473
+ <body style="width: 100% !important; height: 100%; -webkit-text-size-adjust: none; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; background-color: #FFF; color: #333; margin: 0;" bgcolor="#FFF">
474
+ <%= yield %>
475
+ </body>
476
+ </html>
@@ -15,7 +15,7 @@
15
15
  <%= Spina::Page.human_attribute_name :view_template %>
16
16
  </label>
17
17
 
18
- <%= f.select :view_template, options_for_select(current_theme.view_templates.map { |template| [template[:title], template[:name], {'data-page-parts' => template[:page_parts]}] }, @page.view_template), {}, class: "mt-1 form-select w-full" %>
18
+ <%= f.select :view_template, current_theme.new_page_templates(resource: @page.resource).map{ |template| [template.title, template.name] }, {}, class: "mt-1 form-select w-full" %>
19
19
  </div>
20
20
  </div>
21
21
  </div>
@@ -8,27 +8,7 @@
8
8
  <% end %>
9
9
 
10
10
  <div class="ml-3">
11
- <%= render Spina::UserInterface::DropdownComponent.new do |dropdown| %>
12
- <% dropdown.button(classes: "btn btn-primary w-full") do %>
13
- <%= heroicon("plus", style: :solid, class: "w-7 h-7 -ml-2") %>
14
- <%=t 'spina.pages.new' %>
15
- <% end %>
16
-
17
- <% dropdown.menu do %>
18
- <% @page_templates.each do |template| %>
19
- <%= link_to spina.new_admin_page_path(view_template: template.name, resource_id: @resource&.id), class: "block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900", data: {turbo_frame: "modal", action: "reveal#hide"} do %>
20
- <div class="font-medium text-gray-700">
21
- <%= template.title %>
22
-
23
- <% if template.recommended %>
24
- <span class="text-green-500 text-xs"><%=t 'spina.pages.recommended' %></span>
25
- <% end %>
26
- </div>
27
- <div class="text-gray-400"><%= template.description %></div>
28
- <% end %>
29
- <% end %>
30
- <% end %>
31
- <% end %>
11
+ <%= render Spina::Pages::NewPageButtonComponent.new(@page_templates, resource: @resource) %>
32
12
  </div>
33
13
  <% end %>
34
14
 
@@ -1 +1,57 @@
1
- You forgot your password. Here's a link to setup a new one: <%= link_to 'Reset password', spina.edit_admin_password_reset_url(@user.password_reset_token) %>. It expires in 2 hours.
1
+ <span class="preheader" style="display: none !important; visibility: hidden; mso-hide: all; font-size: 1px; line-height: 1px; max-height: 0; max-width: 0; opacity: 0; overflow: hidden;"><%=t "spina.user_mailer.forgot_password.preheader" %></span>
2
+ <table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation" style="width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0; padding: 0;">
3
+ <tr>
4
+ <td align="center" style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px;">
5
+ <table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation" style="width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0; padding: 0;">
6
+ <!-- Email Body -->
7
+ <tr>
8
+ <td class="email-body" width="570" cellpadding="0" cellspacing="0" style="word-break: break-word; margin: 0; padding: 0; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px; width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0;">
9
+ <table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation" style="width: 570px; -premailer-width: 570px; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0 auto; padding: 0;">
10
+ <!-- Body content -->
11
+ <tr>
12
+ <td class="content-cell" style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px; padding: 35px;">
13
+ <div class="f-fallback">
14
+ <h1 style="margin-top: 0; color: #333333; font-size: 22px; font-weight: bold; text-align: left;" align="left">
15
+ <%=t "spina.user_mailer.forgot_password.salutation", name: @user.name %>
16
+ </h1>
17
+ <p style="font-size: 16px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;">
18
+ <%=t "spina.user_mailer.forgot_password.introduction_html" %>
19
+ </p>
20
+ <!-- Action -->
21
+ <table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation" style="width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; text-align: center; margin: 30px auto; padding: 0;">
22
+ <tr>
23
+ <td align="center" style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px;">
24
+ <!-- Border based button
25
+ https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design -->
26
+ <table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
27
+ <tr>
28
+ <td align="center" style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px;">
29
+ <a href="<%= spina.edit_admin_password_reset_url(@user.password_reset_token) %>" class="f-fallback button button--green" target="_blank" style="color: #FFF; border-color: #6865b4; border-style: solid; border-width: 10px 18px; background-color: #6865b4; display: inline-block; text-decoration: none; border-radius: 7px; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.16); -webkit-text-size-adjust: none; box-sizing: border-box;"><%=t "spina.user_mailer.forgot_password.button" %></a>
30
+ </td>
31
+ </tr>
32
+ </table>
33
+ </td>
34
+ </tr>
35
+ </table>
36
+ <p style="font-size: 16px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;">
37
+ <%=t "spina.user_mailer.forgot_password.request_origin", platform: @browser.platform.name, browser: @browser.name %>
38
+ </p>
39
+ <!-- Sub copy -->
40
+ <table class="body-sub" role="presentation" style="margin-top: 25px; padding-top: 25px; border-top-width: 1px; border-top-color: #EAEAEC; border-top-style: solid;">
41
+ <tr>
42
+ <td style="word-break: break-word; font-family: &quot;Nunito Sans&quot;, Helvetica, Arial, sans-serif; font-size: 16px;">
43
+ <p class="f-fallback sub" style="font-size: 13px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;"><%=t "spina.user_mailer.forgot_password.trouble" %></p>
44
+ <p class="f-fallback sub" style="font-size: 13px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;"><%= spina.edit_admin_password_reset_url(@user.password_reset_token) %></p>
45
+ </td>
46
+ </tr>
47
+ </table>
48
+ </div>
49
+ </td>
50
+ </tr>
51
+ </table>
52
+ </td>
53
+ </tr>
54
+ </table>
55
+ </td>
56
+ </tr>
57
+ </table>
@@ -0,0 +1,9 @@
1
+ <%=t "spina.user_mailer.forgot_password.preheader" %>
2
+
3
+ <%=t "spina.user_mailer.forgot_password.salutation", name: @user.name %>
4
+
5
+ <%=t "spina.user_mailer.forgot_password.introduction" %>
6
+
7
+ <%= spina.edit_admin_password_reset_url(@user.password_reset_token) %>
8
+
9
+ <%=t "spina.user_mailer.forgot_password.request_origin", platform: @browser.platform.name, browser: @browser.name %>
@@ -0,0 +1,12 @@
1
+ Spina.config.importmap.draw do
2
+ # Stimulus & Turbo
3
+ pin "@hotwired/stimulus", to: "stimulus.js"
4
+ pin "@hotwired/stimulus-autoloader", to: "stimulus-autoloader.js"
5
+ pin "@hotwired/turbo-rails", to: "turbo.js"
6
+
7
+ # Spina entrypoint
8
+ pin "application", to: "spina/application.js"
9
+
10
+ pin_all_from Spina::Engine.root.join("app/assets/javascripts/spina/controllers"), under: "controllers", to: "spina/controllers"
11
+ pin_all_from Spina::Engine.root.join("app/assets/javascripts/spina/libraries"), under: "libraries", to: "spina/libraries"
12
+ end
@@ -140,6 +140,7 @@ en:
140
140
  insert_photo: Insert photo
141
141
  insert_photos: Insert photos
142
142
  link: Link
143
+ missing_image: Missing image
143
144
  new_folder: New folder
144
145
  organize: Organize
145
146
  remove_image: Remove image
@@ -321,6 +322,16 @@ en:
321
322
  rename: Rename
322
323
  save_changes: Save changes
323
324
  saving: Saving...
325
+ user_mailer:
326
+ forgot_password:
327
+ button: Reset your password
328
+ introduction: You recently requested to reset your password for your Spina CMS account. This password reset is only valid for the next 2 hours. Copy and paste the URL below into your web browser.
329
+ introduction_html: "You recently requested to reset your password for your Spina CMS account. Use the button below to reset it. <strong>This password reset is only valid for the next 2 hours.</strong>"
330
+ preheader: Use this link to reset your password. The link is only valid for 2 hours.
331
+ request_origin: For security, this request was received from a %{platform} device using %{browser}. If you did not request a password reset, please ignore this email.
332
+ salutation: Hi, %{name}
333
+ subject: Reset your password
334
+ trouble: If you’re having trouble with the button above, copy and paste the URL below into your web browser.
324
335
  users:
325
336
  authorization: Authorization
326
337
  authorization_description: Administrators can manage users. Regular users cannot.
@@ -341,6 +352,7 @@ en:
341
352
  content: Content
342
353
  documents: Documents
343
354
  images: Images
355
+ main: Main
344
356
  media_library: Media library
345
357
  media_library_description: Here's where you'll find all of your images and documents
346
358
  pages: Pages
@@ -337,6 +337,7 @@ nl:
337
337
  all_pages: Alle pagina's
338
338
  documents: Documenten
339
339
  images: Afbeeldingen
340
+ main: Pagina's
340
341
  media_library: Mediabibliotheek
341
342
  media_library_description: Hier staan al je afbeeldingen, video's en documenten
342
343
  pages: Pagina's
@@ -1,2 +1,2 @@
1
- <h1><%= current_page.title %>
1
+ <h1><%= current_page.title %></h1>
2
2
  <%= content.html :text %>
@@ -39,6 +39,13 @@ Spina.configure do |config|
39
39
  # The default is Spina::Authentication::Sessions and includes basic user management
40
40
  # config.authentication = "Spina::Authentication::Sessions"
41
41
 
42
+ # Mailers
43
+ # ===============
44
+ # In order to send emails, you need to set a default from address.
45
+ # You can set an optional reply_to address as well.
46
+ # config.mailer_defaults.from = "no-reply@example.com"
47
+ # config.mailer_defaults.reply_to = "support@example.com"
48
+
42
49
  # API
43
50
  # ===============
44
51
  # Set an API key to activate Spina's API.
@@ -37,7 +37,7 @@ Spina::Theme.register do |theme|
37
37
  theme.view_templates = [
38
38
  {name: 'homepage', title: 'Homepage', parts: %w(headline body image_collection)},
39
39
  {name: 'show', title: 'Default', parts: %w(body image repeater)},
40
- {name: 'demo', title: 'Demo', parts: %w(body image_collection image repeater)}
40
+ {name: 'demo', title: 'Demo', parts: %w(body image_collection image repeater), exclude_from: %w(articles)}
41
41
  ]
42
42
 
43
43
  # Custom pages
data/lib/spina/engine.rb CHANGED
@@ -11,6 +11,7 @@ require 'babosa'
11
11
  require 'attr_json'
12
12
  require 'view_component/engine'
13
13
  require 'jsonapi/serializer'
14
+ require 'browser'
14
15
 
15
16
  module Spina
16
17
  class Engine < ::Rails::Engine
@@ -37,22 +38,6 @@ module Spina
37
38
  Spina::Parts::Option,
38
39
  Spina::Parts::Attachment
39
40
  )
40
- end
41
-
42
- initializer "spina.importmap" do
43
- Spina.config.importmap.draw do
44
- # Stimulus & Turbo
45
- pin "@hotwired/stimulus", to: "stimulus.js"
46
- pin "@hotwired/stimulus-autoloader", to: "stimulus-autoloader.js"
47
- pin "@hotwired/turbo-rails", to: "turbo.js"
48
-
49
- # Spina entrypoint
50
- pin "application", to: "spina/application.js"
51
-
52
- pin_all_from Spina::Engine.root.join("app/assets/javascripts/spina/controllers"), under: "controllers", to: "spina/controllers"
53
- pin_all_from Spina::Engine.root.join("app/assets/javascripts/spina/libraries"), under: "libraries", to: "spina/libraries"
54
- end
55
- end
56
-
41
+ end
57
42
  end
58
43
  end
data/lib/spina/theme.rb CHANGED
@@ -36,15 +36,17 @@ module Spina
36
36
  @public_theme = false
37
37
  end
38
38
 
39
- def new_page_templates(recommended: "")
39
+ def new_page_templates(resource: nil)
40
+ page_collection = resource&.name || "main"
40
41
  @view_templates.map do |view_template|
41
42
  next if is_custom_undeletable_page?(view_template[:name])
43
+ next if view_template[:exclude_from]&.include?(page_collection)
42
44
 
43
45
  OpenStruct.new({
44
46
  name: view_template[:name],
45
47
  title: view_template[:title],
46
48
  description: view_template[:description],
47
- recommended: view_template[:name] == recommended
49
+ recommended: view_template[:name] == resource&.view_template
48
50
  })
49
51
  end.compact.sort_by do |page_template|
50
52
  [page_template.recommended ? 0 : 1]
data/lib/spina/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Spina
2
- VERSION = "2.3.5"
2
+ VERSION = "2.4.0"
3
3
  end
data/lib/spina.rb CHANGED
@@ -26,6 +26,7 @@ module Spina
26
26
  :disable_decorator_load,
27
27
  :locales,
28
28
  :embedded_image_size,
29
+ :mailer_defaults,
29
30
  :thumbnail_image_size,
30
31
  :party_pooper,
31
32
  :tailwind_purge_content,
@@ -40,6 +41,7 @@ module Spina
40
41
  self.disable_frontend_routes = false
41
42
  self.disable_decorator_load = false
42
43
  self.embedded_image_size = [2000, 2000]
44
+ self.mailer_defaults = ActiveSupport::OrderedOptions.new
43
45
  self.thumbnail_image_size = [400, 400]
44
46
  self.frontend_parent_controller = "ApplicationController"
45
47
  self.locales = [I18n.default_locale]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spina
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.5
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bram Jetten
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-22 00:00:00.000000000 Z
11
+ date: 2021-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -184,14 +184,14 @@ dependencies:
184
184
  requirements:
185
185
  - - '='
186
186
  - !ruby/object:Gem::Version
187
- version: 0.3.1
187
+ version: 0.5.1
188
188
  type: :runtime
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - '='
193
193
  - !ruby/object:Gem::Version
194
- version: 0.3.1
194
+ version: 0.5.1
195
195
  - !ruby/object:Gem::Dependency
196
196
  name: turbo-rails
197
197
  requirement: !ruby/object:Gem::Requirement
@@ -210,16 +210,22 @@ dependencies:
210
210
  name: stimulus-rails
211
211
  requirement: !ruby/object:Gem::Requirement
212
212
  requirements:
213
- - - "~>"
213
+ - - ">="
214
214
  - !ruby/object:Gem::Version
215
215
  version: 0.3.8
216
+ - - "<"
217
+ - !ruby/object:Gem::Version
218
+ version: 0.5.0
216
219
  type: :runtime
217
220
  prerelease: false
218
221
  version_requirements: !ruby/object:Gem::Requirement
219
222
  requirements:
220
- - - "~>"
223
+ - - ">="
221
224
  - !ruby/object:Gem::Version
222
225
  version: 0.3.8
226
+ - - "<"
227
+ - !ruby/object:Gem::Version
228
+ version: 0.5.0
223
229
  - !ruby/object:Gem::Dependency
224
230
  name: babosa
225
231
  requirement: !ruby/object:Gem::Requirement
@@ -248,6 +254,20 @@ dependencies:
248
254
  - - ">="
249
255
  - !ruby/object:Gem::Version
250
256
  version: '0'
257
+ - !ruby/object:Gem::Dependency
258
+ name: browser
259
+ requirement: !ruby/object:Gem::Requirement
260
+ requirements:
261
+ - - ">="
262
+ - !ruby/object:Gem::Version
263
+ version: '0'
264
+ type: :runtime
265
+ prerelease: false
266
+ version_requirements: !ruby/object:Gem::Requirement
267
+ requirements:
268
+ - - ">="
269
+ - !ruby/object:Gem::Version
270
+ version: '0'
251
271
  description: CMS
252
272
  email:
253
273
  - bram@denkgroot.com
@@ -874,6 +894,8 @@ files:
874
894
  - app/components/spina/pages/list_component.rb
875
895
  - app/components/spina/pages/location_component.html.erb
876
896
  - app/components/spina/pages/location_component.rb
897
+ - app/components/spina/pages/new_page_button_component.html.erb
898
+ - app/components/spina/pages/new_page_button_component.rb
877
899
  - app/components/spina/pages/page_component.html.erb
878
900
  - app/components/spina/pages/page_component.rb
879
901
  - app/components/spina/pages/page_select_component.html.erb
@@ -932,6 +954,7 @@ files:
932
954
  - app/helpers/spina/spina_helper.rb
933
955
  - app/jobs/spina/application_job.rb
934
956
  - app/jobs/spina/resource_pages_update_job.rb
957
+ - app/mailers/spina/application_mailer.rb
935
958
  - app/mailers/spina/user_mailer.rb
936
959
  - app/models/concerns/spina/gravatar.rb
937
960
  - app/models/concerns/spina/partable.rb
@@ -1036,7 +1059,8 @@ files:
1036
1059
  - app/views/spina/admin/users/new.html.erb
1037
1060
  - app/views/spina/sitemaps/show.xml.builder
1038
1061
  - app/views/spina/user_mailer/forgot_password.html.erb
1039
- - app/views/spina/user_mailer/forgot_password.txt.erb
1062
+ - app/views/spina/user_mailer/forgot_password.text.erb
1063
+ - config/initializers/importmap.rb
1040
1064
  - config/locales/TH.yml
1041
1065
  - config/locales/bg.yml
1042
1066
  - config/locales/de.yml
@@ -1 +0,0 @@
1
- You forgot your password. Here's a link to setup a new one: <%= spina.edit_admin_password_reset_url(@user.password_reset_token) %>. It expires in 2 hours.