spina 2.3.5 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of spina might be problematic. Click here for more details.

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.