locomotivecms 4.0.3 → 4.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -0
  3. data/app/api/locomotive/api/entities/content_type_entity.rb +1 -1
  4. data/app/api/locomotive/api/entities/site_entity.rb +1 -1
  5. data/app/api/locomotive/api/forms/content_type_form.rb +2 -1
  6. data/app/api/locomotive/api/forms/site_form.rb +1 -0
  7. data/app/api/locomotive/api/helpers/persistence_helper.rb +1 -1
  8. data/app/api/locomotive/api/resources/content_asset_resource.rb +6 -0
  9. data/app/api/locomotive/api/resources/content_type_resource.rb +2 -0
  10. data/app/api/locomotive/api/resources/current_site_resource.rb +1 -1
  11. data/app/api/locomotive/api/resources/site_resource.rb +3 -1
  12. data/app/assets/javascripts/locomotive/editor.js +7819 -100
  13. data/app/assets/javascripts/locomotive/views/content_entry_imports/new_view.js.coffee +12 -0
  14. data/app/assets/javascripts/locomotive/views/content_entry_imports/show_view.js.coffee +8 -0
  15. data/app/assets/stylesheets/locomotive/application.scss +2 -0
  16. data/app/assets/stylesheets/locomotive/editor.css +105 -12
  17. data/app/assets/stylesheets/locomotive/new/_dashboard.scss +20 -0
  18. data/app/assets/stylesheets/locomotive/old/_content_assets.scss +1 -1
  19. data/app/controllers/locomotive/content_assets_controller.rb +1 -1
  20. data/app/controllers/locomotive/content_entry_imports_controller.rb +50 -0
  21. data/app/helpers/locomotive/base_helper.rb +1 -1
  22. data/app/helpers/locomotive/shared/pages_helper.rb +1 -1
  23. data/app/jobs/locomotive/import_content_entry_job.rb +12 -0
  24. data/app/mailers/locomotive/notifications.rb +37 -11
  25. data/app/models/locomotive/account.rb +1 -0
  26. data/app/models/locomotive/concerns/asset/vignette.rb +7 -5
  27. data/app/models/locomotive/concerns/content_type/import.rb +124 -0
  28. data/app/models/locomotive/concerns/site/metafields.rb +26 -0
  29. data/app/models/locomotive/content_asset.rb +9 -2
  30. data/app/models/locomotive/content_type.rb +1 -0
  31. data/app/models/locomotive/section.rb +3 -1
  32. data/app/models/locomotive/site.rb +2 -0
  33. data/app/policies/locomotive/content_entry_policy.rb +1 -2
  34. data/app/policies/locomotive/content_type_policy.rb +3 -0
  35. data/app/policies/locomotive/site_policy.rb +5 -3
  36. data/app/services/locomotive/content_asset_service.rb +27 -4
  37. data/app/services/locomotive/content_entry_import_service.rb +107 -0
  38. data/app/services/locomotive/content_entry_service.rb +1 -1
  39. data/app/uploaders/locomotive/picture_uploader.rb +1 -1
  40. data/app/uploaders/locomotive/theme_asset_uploader.rb +1 -1
  41. data/app/views/locomotive/content_assets/_dropzone.html.slim +5 -1
  42. data/app/views/locomotive/content_entries/index.html.slim +8 -0
  43. data/app/views/locomotive/content_entry_imports/new.html.slim +23 -0
  44. data/app/views/locomotive/content_entry_imports/show.html.slim +54 -0
  45. data/app/views/locomotive/current_site/form/_advanced.html.slim +2 -0
  46. data/app/views/locomotive/page_content/edit.html.erb +1 -0
  47. data/config/locales/editor.en.yml +22 -0
  48. data/config/locales/editor.fr.yml +22 -0
  49. data/config/locales/en.yml +19 -0
  50. data/config/locales/es.yml +2 -0
  51. data/config/locales/flash.en.yml +6 -0
  52. data/config/locales/mongoid.en.yml +5 -1
  53. data/config/locales/mongoid.es.yml +4 -0
  54. data/config/locales/simple_form.en.yml +13 -1
  55. data/config/locales/simple_form.es.yml +2 -0
  56. data/config/routes.rb +3 -2
  57. data/lib/locomotive/configuration.rb +1 -0
  58. data/lib/locomotive/dragonfly.rb +6 -4
  59. data/lib/locomotive/version.rb +1 -1
  60. metadata +38 -29
@@ -0,0 +1,12 @@
1
+ Locomotive.Views.ContentEntryImports ||= {}
2
+
3
+ class Locomotive.Views.ContentEntryImports.NewView extends Locomotive.Views.Shared.FormView
4
+
5
+ el: '.main'
6
+
7
+ initialize: ->
8
+ super()
9
+ $('#collapseContentTypes').addClass('in')
10
+
11
+ render: ->
12
+ super()
@@ -0,0 +1,8 @@
1
+ Locomotive.Views.ContentEntryImports ||= {}
2
+
3
+ class Locomotive.Views.ContentEntryImports.ShowView extends Backbone.View
4
+
5
+ el: '.main'
6
+
7
+ initialize: ->
8
+ $('#collapseContentTypes').addClass('in')
@@ -111,3 +111,5 @@
111
111
  @import "old/dashboard";
112
112
  @import "old/activity_feed";
113
113
  @import "old/developers";
114
+
115
+ @import "new/dashboard";
@@ -25,6 +25,7 @@
25
25
  opacity: 0.3;
26
26
  cursor: default;
27
27
  }
28
+
28
29
  .rdw-dropdown-wrapper {
29
30
  height: 30px;
30
31
  background: white;
@@ -88,6 +89,7 @@
88
89
  box-shadow: 1px 1px 0px #BFBDBD;
89
90
  background-color: #FFFFFF;
90
91
  }
92
+
91
93
  .rdw-dropdownoption-default {
92
94
  min-height: 25px;
93
95
  display: flex;
@@ -104,6 +106,7 @@
104
106
  opacity: 0.3;
105
107
  cursor: default;
106
108
  }
109
+
107
110
  .rdw-inline-wrapper {
108
111
  display: flex;
109
112
  align-items: center;
@@ -118,6 +121,7 @@
118
121
  display: flex;
119
122
  justify-content: center;
120
123
  }
124
+
121
125
  .rdw-block-wrapper {
122
126
  display: flex;
123
127
  align-items: center;
@@ -127,6 +131,7 @@
127
131
  .rdw-block-dropdown {
128
132
  width: 110px;
129
133
  }
134
+
130
135
  .rdw-fontsize-wrapper {
131
136
  display: flex;
132
137
  align-items: center;
@@ -140,6 +145,7 @@
140
145
  display: flex;
141
146
  justify-content: center;
142
147
  }
148
+
143
149
  .rdw-fontfamily-wrapper {
144
150
  display: flex;
145
151
  align-items: center;
@@ -158,6 +164,7 @@
158
164
  .rdw-fontfamily-optionwrapper {
159
165
  width: 140px;
160
166
  }
167
+
161
168
  .rdw-list-wrapper {
162
169
  display: flex;
163
170
  align-items: center;
@@ -173,6 +180,7 @@
173
180
  display: flex;
174
181
  justify-content: center;
175
182
  }
183
+
176
184
  .rdw-text-align-wrapper {
177
185
  display: flex;
178
186
  align-items: center;
@@ -212,6 +220,7 @@
212
220
  .rdw-justify-aligned-block > div {
213
221
  display: inline-block;
214
222
  }
223
+
215
224
  .rdw-colorpicker-wrapper {
216
225
  display: flex;
217
226
  align-items: center;
@@ -280,6 +289,7 @@
280
289
  .rdw-colorpicker-option-active {
281
290
  box-shadow: 0px 0px 2px 2px #BFBDBD;
282
291
  }
292
+
283
293
  .rdw-link-wrapper {
284
294
  display: flex;
285
295
  align-items: center;
@@ -366,6 +376,7 @@
366
376
  .rdw-history-dropdown {
367
377
  width: 50px;
368
378
  }
379
+
369
380
  .rdw-embedded-wrapper {
370
381
  display: flex;
371
382
  align-items: center;
@@ -470,6 +481,7 @@
470
481
  .rdw-embedded-modal-size-input:focus {
471
482
  outline: none;
472
483
  }
484
+
473
485
  .rdw-emoji-wrapper {
474
486
  display: flex;
475
487
  align-items: center;
@@ -503,6 +515,7 @@
503
515
  justify-content: center;
504
516
  align-items: center;
505
517
  }
518
+
506
519
  .rdw-spinner {
507
520
  display: flex;
508
521
  align-items: center;
@@ -541,6 +554,7 @@
541
554
  transform: scale(1.0);
542
555
  }
543
556
  }
557
+
544
558
  .rdw-image-wrapper {
545
559
  display: flex;
546
560
  align-items: center;
@@ -711,6 +725,7 @@
711
725
  margin-left: 3px;
712
726
  margin-right: 3px;
713
727
  }
728
+
714
729
  .rdw-remove-wrapper {
715
730
  display: flex;
716
731
  align-items: center;
@@ -718,6 +733,7 @@
718
733
  position: relative;
719
734
  flex-wrap: wrap
720
735
  }
736
+
721
737
  .rdw-history-wrapper {
722
738
  display: flex;
723
739
  align-items: center;
@@ -732,6 +748,7 @@
732
748
  .rdw-history-dropdown {
733
749
  width: 50px;
734
750
  }
751
+
735
752
  .rdw-link-decorator-wrapper {
736
753
  position: relative;
737
754
  }
@@ -742,6 +759,7 @@
742
759
  cursor: pointer;
743
760
  background-color: white;
744
761
  }
762
+
745
763
  .rdw-mention-link {
746
764
  text-decoration: none;
747
765
  color: #1236ff;
@@ -749,6 +767,7 @@
749
767
  padding: 1px 2px;
750
768
  border-radius: 2px;
751
769
  }
770
+
752
771
  .rdw-suggestion-wrapper {
753
772
  position: relative;
754
773
  }
@@ -770,6 +789,7 @@
770
789
  .rdw-suggestion-option-active {
771
790
  background-color: #F1F1F1;
772
791
  }
792
+
773
793
  .rdw-hashtag-link {
774
794
  text-decoration: none;
775
795
  color: #1236ff;
@@ -777,6 +797,7 @@
777
797
  padding: 1px 2px;
778
798
  border-radius: 2px;
779
799
  }
800
+
780
801
  .rdw-image-alignment-options-popup {
781
802
  position: absolute;
782
803
  background: white;
@@ -816,6 +837,7 @@
816
837
  .rdw-image-alignment-options-popup-right {
817
838
  right: 0;
818
839
  }
840
+
819
841
  .rdw-editor-main {
820
842
  height: 100%;
821
843
  overflow: auto;
@@ -850,7 +872,8 @@
850
872
  background: #f1f1f1;
851
873
  border-radius: 3px;
852
874
  padding: 1px 10px;
853
- }/**
875
+ }
876
+ /**
854
877
  * Draft v0.9.1
855
878
  *
856
879
  * Copyright (c) 2013-present, Facebook, Inc.
@@ -892,12 +915,11 @@
892
915
  }
893
916
  .rc-slider-handle {
894
917
  position: absolute;
895
- margin-left: -7px;
896
- margin-top: -5px;
897
918
  width: 14px;
898
919
  height: 14px;
899
920
  cursor: pointer;
900
921
  cursor: -webkit-grab;
922
+ margin-top: -5px;
901
923
  cursor: grab;
902
924
  border-radius: 50%;
903
925
  border: solid 2px #96dbfa;
@@ -962,6 +984,10 @@
962
984
  .rc-slider-dot-active {
963
985
  border-color: #96dbfa;
964
986
  }
987
+ .rc-slider-dot-reverse {
988
+ margin-left: 0;
989
+ margin-right: -4px;
990
+ }
965
991
  .rc-slider-disabled {
966
992
  background-color: #e9e9e9;
967
993
  }
@@ -995,7 +1021,6 @@
995
1021
  }
996
1022
  .rc-slider-vertical .rc-slider-handle {
997
1023
  margin-left: -5px;
998
- margin-bottom: -7px;
999
1024
  -ms-touch-action: pan-y;
1000
1025
  touch-action: pan-y;
1001
1026
  }
@@ -1284,6 +1309,12 @@
1284
1309
  width: 14px;
1285
1310
  margin-left: -7px; } }
1286
1311
 
1312
+ .ml-3 {
1313
+ margin-left: 1rem; }
1314
+
1315
+ .mr-3 {
1316
+ margin-right: 1rem; }
1317
+
1287
1318
  .editor-list-item {
1288
1319
  display: flex;
1289
1320
  align-items: center;
@@ -1297,6 +1328,8 @@
1297
1328
  -moz-user-select: none;
1298
1329
  -ms-user-select: none;
1299
1330
  user-select: none; }
1331
+ .editor-list-item--dragging {
1332
+ box-shadow: 0 0 0 1px rgba(63, 63, 68, 0.25), 0 1px 3px 0 rgba(63, 63, 68, 0.75); }
1300
1333
  .editor-list-item.with-separator {
1301
1334
  margin-top: 4rem; }
1302
1335
  .editor-list-item--icon, .editor-list-item--image {
@@ -1377,14 +1410,10 @@
1377
1410
  padding: 0.5rem 0.8rem;
1378
1411
  border-radius: 3px;
1379
1412
  border: 1px solid #e3e4e7; }
1380
- .editor-input--text::-webkit-input-placeholder {
1381
- color: #c8cbcf; }
1382
1413
  .editor-input--text::-moz-placeholder {
1383
1414
  color: #c8cbcf; }
1384
1415
  .editor-input--text:-ms-input-placeholder {
1385
1416
  color: #c8cbcf; }
1386
- .editor-input--text::-ms-input-placeholder {
1387
- color: #c8cbcf; }
1388
1417
  .editor-input--text::placeholder {
1389
1418
  color: #c8cbcf; }
1390
1419
  .editor-input--select {
@@ -1422,6 +1451,12 @@
1422
1451
  align-items: center;
1423
1452
  justify-content: center; }
1424
1453
 
1454
+ .editor-input-asset-picker--actions {
1455
+ margin-top: 1rem;
1456
+ display: flex;
1457
+ align-items: center;
1458
+ justify-content: center; }
1459
+
1425
1460
  .editor-input-url--info {
1426
1461
  display: flex;
1427
1462
  align-items: center; }
@@ -1434,6 +1469,16 @@
1434
1469
  align-items: center;
1435
1470
  justify-content: center; }
1436
1471
 
1472
+ .editor-input-content-entry--info {
1473
+ display: flex;
1474
+ align-items: center; }
1475
+
1476
+ .editor-input-content-entry--actions {
1477
+ margin-top: 1rem;
1478
+ display: flex;
1479
+ align-items: center;
1480
+ justify-content: center; }
1481
+
1437
1482
  .editor-input-radio--option input {
1438
1483
  margin-right: 1rem; }
1439
1484
 
@@ -1757,6 +1802,7 @@
1757
1802
  .preview-iframe > .preview-iframe-loader {
1758
1803
  background: #fff;
1759
1804
  display: flex;
1805
+ flex-direction: column;
1760
1806
  align-items: center;
1761
1807
  justify-content: center;
1762
1808
  position: absolute;
@@ -1769,6 +1815,8 @@
1769
1815
  .preview-iframe > .preview-iframe-loader img {
1770
1816
  display: block;
1771
1817
  width: 12rem; }
1818
+ .preview-iframe > .preview-iframe-loader p {
1819
+ margin-top: 2rem; }
1772
1820
  .preview-iframe--loading > .scrollable {
1773
1821
  opacity: 0;
1774
1822
  z-index: 998; }
@@ -1903,6 +1951,46 @@
1903
1951
  background: none;
1904
1952
  border: none; }
1905
1953
 
1954
+ .editor-asset-uploader {
1955
+ display: flex;
1956
+ align-items: center;
1957
+ justify-content: center;
1958
+ width: 100%;
1959
+ background: #fff;
1960
+ border: dashed 1px #e3e4e7;
1961
+ border-radius: 3px;
1962
+ margin-bottom: 1rem;
1963
+ padding: 1rem 1.2rem; }
1964
+ .editor-asset-uploader input[type=file] {
1965
+ display: none; }
1966
+ .editor-asset-uploader--button {
1967
+ text-transform: uppercase;
1968
+ cursor: pointer; }
1969
+
1970
+ .editor-asset-list {
1971
+ margin: 2rem; }
1972
+ .editor-asset-list--loading {
1973
+ height: 100%;
1974
+ display: flex;
1975
+ align-items: center;
1976
+ justify-content: center; }
1977
+ .editor-asset-list--container {
1978
+ margin-top: 2rem;
1979
+ display: flex;
1980
+ flex-wrap: wrap;
1981
+ flex-direction: row;
1982
+ align-content: flex-start;
1983
+ justify-content: space-around; }
1984
+ .editor-asset-list .editor-list-item {
1985
+ width: 100%;
1986
+ cursor: pointer; }
1987
+ .editor-asset-list--pagination {
1988
+ display: flex;
1989
+ align-items: center;
1990
+ margin-top: 2rem; }
1991
+ .editor-asset-list--pagination .pagination {
1992
+ margin: 0 auto; }
1993
+
1906
1994
  .editor-image-uploader {
1907
1995
  display: flex;
1908
1996
  align-items: center;
@@ -2008,14 +2096,10 @@
2008
2096
  color: #91979f;
2009
2097
  margin-bottom: 0;
2010
2098
  cursor: pointer; }
2011
- .url-picker .react-autosuggest__input::-webkit-input-placeholder {
2012
- color: #c8cbcf; }
2013
2099
  .url-picker .react-autosuggest__input::-moz-placeholder {
2014
2100
  color: #c8cbcf; }
2015
2101
  .url-picker .react-autosuggest__input:-ms-input-placeholder {
2016
2102
  color: #c8cbcf; }
2017
- .url-picker .react-autosuggest__input::-ms-input-placeholder {
2018
- color: #c8cbcf; }
2019
2103
  .url-picker .react-autosuggest__input::placeholder {
2020
2104
  color: #c8cbcf; }
2021
2105
 
@@ -2023,6 +2107,15 @@
2023
2107
  min-width: 42rem;
2024
2108
  padding: 2rem 0 0 0; }
2025
2109
 
2110
+ .content-entry-picker {
2111
+ padding: 2rem; }
2112
+ .content-entry-picker .react-autosuggest__input::-moz-placeholder {
2113
+ color: #c8cbcf; }
2114
+ .content-entry-picker .react-autosuggest__input:-ms-input-placeholder {
2115
+ color: #c8cbcf; }
2116
+ .content-entry-picker .react-autosuggest__input::placeholder {
2117
+ color: #c8cbcf; }
2118
+
2026
2119
  .locale-switcher {
2027
2120
  position: relative;
2028
2121
  display: inline-block;
@@ -0,0 +1,20 @@
1
+ .metric {
2
+ background: rgb(21, 90, 148);
3
+ border-radius: 3px;
4
+ padding: 2rem 1rem;
5
+ text-align: center;
6
+
7
+ h2 {
8
+ color: rgba(255, 255, 255, 0.6);
9
+ font-weight: bold;
10
+ font-size: 1.15rem;
11
+ text-transform: uppercase;
12
+ }
13
+
14
+ h3 {
15
+ margin-top: 1rem;
16
+ color: #fff;
17
+ font-weight: bold;
18
+ font-size: 2.5rem;
19
+ }
20
+ }
@@ -58,7 +58,7 @@ body.live-editing .drawer {
58
58
  color: #555;
59
59
  }
60
60
 
61
- i {
61
+ i.fas {
62
62
  font-size: 65px;
63
63
  color: #bec1c2;
64
64
  }
@@ -17,7 +17,7 @@ module Locomotive
17
17
 
18
18
  def create
19
19
  authorize Locomotive::ContentAsset
20
- @content_asset = current_site.content_assets.create(content_asset_params)
20
+ @content_asset = service.create(content_asset_params)
21
21
  respond_with @content_asset, location: content_assets_path
22
22
  end
23
23
 
@@ -0,0 +1,50 @@
1
+ module Locomotive
2
+ class ContentEntryImportsController < BaseController
3
+
4
+ account_required & within_site
5
+
6
+ before_action :only_if_import_enabled
7
+
8
+ def show
9
+ authorize @content_type, :import?
10
+ end
11
+
12
+ def new
13
+ authorize @content_type, :import?
14
+ @import = Locomotive::ContentEntryImport.new
15
+ end
16
+
17
+ def create
18
+ authorize @content_type, :import?
19
+ @import = Locomotive::ContentEntryImport.new(import_params)
20
+ service.async_import(@import.file, @import.options) if @import.valid?
21
+ respond_with @import, location: content_entry_import_path(current_site, @content_type.slug)
22
+ end
23
+
24
+ def destroy
25
+ authorize @content_type, :import?
26
+ message = t('flash.locomotive.content_entry_imports.destroy.notice')
27
+ service.cancel(message)
28
+ flash[:alert] = message
29
+ redirect_to new_content_entry_import_path(current_site, @content_type.slug)
30
+ end
31
+
32
+ private
33
+
34
+ def only_if_import_enabled
35
+ redirect_to content_entries_path(current_site, content_type.slug) unless content_type.import_enabled
36
+ end
37
+
38
+ def content_type
39
+ @content_type ||= current_site.content_types.where(slug: params[:slug]).first!
40
+ end
41
+
42
+ def service
43
+ @service ||= Locomotive::ContentEntryImportService.new(content_type)
44
+ end
45
+
46
+ def import_params
47
+ params.require(:content_entry_import).permit(:file, :col_sep, :quote_char)
48
+ end
49
+ end
50
+ end
@@ -49,7 +49,7 @@ module Locomotive
49
49
  def sidebar_current_section_class
50
50
  case self.controller.controller_name
51
51
  when 'pages', 'editable_elements' then :pages
52
- when 'content_types', 'content_entries', 'public_submission_accounts', 'select_options' then :content_types
52
+ when 'content_types', 'content_entries', 'content_entry_imports', 'public_submission_accounts', 'select_options' then :content_types
53
53
  else
54
54
  self.controller.controller_name
55
55
  end
@@ -104,7 +104,7 @@ module Locomotive
104
104
  end
105
105
 
106
106
  def templatized_parent?
107
- parent.templatized?
107
+ parent&.templatized?
108
108
  end
109
109
 
110
110
  def templatized_children?
@@ -0,0 +1,12 @@
1
+ module Locomotive
2
+ class ImportContentEntryJob < ActiveJob::Base
3
+
4
+ queue_as :default
5
+
6
+ def perform(content_type_id, csv_asset_id, csv_options)
7
+ content_type = Locomotive::ContentType.find(content_type_id)
8
+ service = Locomotive::ContentEntryImportService.new(content_type)
9
+ service.import(csv_asset_id, csv_options)
10
+ end
11
+ end
12
+ end
@@ -3,20 +3,18 @@ require 'adomain'
3
3
  module Locomotive
4
4
  class Notifications < ActionMailer::Base
5
5
 
6
- def new_content_entry(account, entry)
7
- @site, @account = entry.site, account
8
- @entry, @type = entry, entry.content_type
6
+ SMTP_SETTINGS_NAMES = %w(smtp_settings mailer_settings email_settings).freeze
7
+ SMTP_ATTRIBUTES = %w(address authentication port enable_starttls_auto user_name password domain).freeze
8
+
9
+ after_action :set_delivery_options
9
10
 
10
- @domain = entry.site.domains.first ||
11
- ActionMailer::Base.default_url_options[:host] ||
12
- 'localhost'
11
+ def new_content_entry(site, account, entry)
12
+ @site, @account = site, account
13
+ @entry, @type = entry, entry.content_type
14
+ @domain = fetch_domain
13
15
 
14
16
  subject = new_content_entry_subject(entry, domain: @domain, type: @type.name, locale: account.locale)
15
- from = (if top_level_domain = Adomain.domain(@domain)
16
- "noreply@#{top_level_domain}"
17
- else
18
- Locomotive.config.mailer_sender
19
- end)
17
+ from = fetch_from
20
18
 
21
19
  # attach uploaded files
22
20
  if @type.public_submission_email_attachments
@@ -39,5 +37,33 @@ module Locomotive
39
37
  end
40
38
  end
41
39
 
40
+ def site_mailer_settings
41
+ SMTP_SETTINGS_NAMES
42
+ .map { |namespace| @site.cast_metafields(namespace) }
43
+ .compact.first || {}
44
+ end
45
+
46
+ def fetch_domain
47
+ @site.domains.first || ActionMailer::Base.default_url_options[:host] || 'localhost'
48
+ end
49
+
50
+ def fetch_from
51
+ if from = site_mailer_settings['from']
52
+ from
53
+ elsif top_level_domain = Adomain.domain(@domain)
54
+ "noreply@#{top_level_domain}"
55
+ else
56
+ Locomotive.config.mailer_sender
57
+ end
58
+ end
59
+
60
+ def set_delivery_options
61
+ smtp_settings = site_mailer_settings.slice(*SMTP_ATTRIBUTES).delete_if { |_, value| value.blank? }.symbolize_keys
62
+
63
+ if smtp_settings && smtp_settings[:address].present?
64
+ mail.delivery_method.settings = smtp_settings
65
+ end
66
+ end
67
+
42
68
  end
43
69
  end
@@ -30,6 +30,7 @@ module Locomotive
30
30
 
31
31
  ## validations ##
32
32
  validates_presence_of :name
33
+ validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }
33
34
 
34
35
  ## associations ##
35
36
  has_many :created_sites, class_name: 'Locomotive::Site', validate: false, autosave: false
@@ -5,13 +5,15 @@ module Locomotive
5
5
 
6
6
  def vignette_url
7
7
  if self.image?
8
- if self.width < 85 && self.height < 85
8
+ # In some case (like an invalid extension) the height, width can be nill
9
+ # In that case we should directly return the url
10
+ if self.width && self.height && self.width < 85 && self.height < 85
9
11
  self.source.url
10
12
  else
11
- Locomotive::Dragonfly.resize_url(self.source, '85x85#')
13
+ Locomotive::Dragonfly.resize_url(self.source, '85x85#', self.updated_at.to_i)
12
14
  end
13
15
  elsif self.pdf?
14
- Locomotive::Dragonfly.thumbnail_pdf(self.source, '85x85#')
16
+ Locomotive::Dragonfly.thumbnail_pdf(self.source, '85x85#', self.updated_at.to_i)
15
17
  end
16
18
  end
17
19
 
@@ -22,7 +24,7 @@ module Locomotive
22
24
  '190x120#'
23
25
  end
24
26
 
25
- Locomotive::Dragonfly.thumbnail_pdf(self.source, format) if format
27
+ Locomotive::Dragonfly.thumbnail_pdf(self.source, format, self.updated_at.to_i) if format
26
28
  end
27
29
 
28
30
  def big_vignette_url
@@ -30,7 +32,7 @@ module Locomotive
30
32
  '200x200#'
31
33
  end
32
34
 
33
- Locomotive::Dragonfly.thumbnail_pdf(self.source, format) if format
35
+ Locomotive::Dragonfly.thumbnail_pdf(self.source, format, self.updated_at.to_i) if format
34
36
  end
35
37
 
36
38
  end