locomotivecms 4.0.0.alpha3 → 4.0.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/app/api/locomotive/api/entities/content_type_entity.rb +1 -0
  4. data/app/api/locomotive/api/forms/content_type_form.rb +1 -0
  5. data/app/api/locomotive/api/resources/content_type_resource.rb +1 -0
  6. data/app/assets/javascripts/locomotive/editor.js +3028 -3112
  7. data/app/assets/javascripts/locomotive/views/content_assets/edit_image_view.js.coffee +1 -0
  8. data/app/assets/javascripts/locomotive/views/inputs/rte/edit_table_view.js.coffee +1 -0
  9. data/app/assets/javascripts/locomotive/views/inputs/rte/file_view.js.coffee +1 -0
  10. data/app/assets/javascripts/locomotive/views/inputs/rte/link_view.js.coffee +1 -0
  11. data/app/assets/javascripts/locomotive/views/inputs/rte/table_view.js.coffee +1 -0
  12. data/app/assets/javascripts/locomotive/views/inputs/rte_view.js.coffee.erb +2 -1
  13. data/app/assets/stylesheets/locomotive/editor.css +31 -4
  14. data/app/controllers/locomotive/content_entries_controller.rb +8 -2
  15. data/app/controllers/locomotive/content_entry_impersonations_controller.rb +32 -0
  16. data/app/helpers/locomotive/content_types_helper.rb +2 -2
  17. data/app/mailers/locomotive/notifications.rb +8 -3
  18. data/app/models/locomotive/concerns/content_entry/authentication.rb +13 -0
  19. data/app/models/locomotive/concerns/page/editable_elements.rb +1 -1
  20. data/app/models/locomotive/content_entry.rb +1 -0
  21. data/app/models/locomotive/content_type.rb +1 -0
  22. data/app/services/locomotive/content_entry_service.rb +8 -9
  23. data/app/uploaders/locomotive/theme_asset_uploader.rb +1 -1
  24. data/app/views/locomotive/content_entries/_list.html.slim +1 -1
  25. data/app/views/locomotive/content_entries/edit.html.slim +10 -3
  26. data/app/views/locomotive/content_entries/new.html.slim +3 -1
  27. data/config/routes.rb +2 -0
  28. data/lib/generators/locomotive/install/install_generator.rb +2 -10
  29. data/lib/locomotive/dependencies.rb +1 -0
  30. data/lib/locomotive/steam/services/api_content_entry_service.rb +2 -2
  31. data/lib/locomotive/steam/services/api_entry_submission_service.rb +13 -3
  32. data/lib/locomotive/version.rb +1 -1
  33. data/lib/tasks/locomotive_tasks.rake +29 -0
  34. data/vendor/assets/javascripts/locomotive/wysihtml5x-toolbar.js +0 -1
  35. metadata +52 -8
@@ -44,6 +44,7 @@ class Locomotive.Views.ContentAssets.EditImageView extends Backbone.View
44
44
  placement: 'left'
45
45
  content: @$content
46
46
  html: true
47
+ sanitize: false
47
48
  template: '<div class="popover" role="tooltip"><div class="arrow"></div><form><div class="popover-content"></div></form></div>'
48
49
  @$link.data('bs.popover').setContent()
49
50
 
@@ -20,6 +20,7 @@ class Locomotive.Views.Inputs.Rte.EditTableView extends Backbone.View
20
20
  placement: 'bottom'
21
21
  content: @$content.html()
22
22
  html: true
23
+ sanitize: false
23
24
  title: undefined
24
25
 
25
26
  attach_events: ->
@@ -36,6 +36,7 @@ class Locomotive.Views.Inputs.Rte.FileView extends Backbone.View
36
36
  placement: 'left'
37
37
  content: @$popover
38
38
  html: true
39
+ sanitize: false
39
40
  template: '<div class="popover" role="tooltip"><div class="arrow"></div><form class="simple_form"><div class="popover-content"></div></form></div>'
40
41
  @$link.data('bs.popover').setContent()
41
42
 
@@ -27,6 +27,7 @@ class Locomotive.Views.Inputs.Rte.LinkView extends Backbone.View
27
27
  placement: 'bottom'
28
28
  content: @$content
29
29
  html: true
30
+ sanitize: false
30
31
  trigger: 'manual'
31
32
  template: '<div class="popover" role="tooltip"><div class="arrow"></div><form class="simple_form"><div class="popover-content"></div></form></div>'
32
33
  @$link.data('bs.popover').setContent()
@@ -27,6 +27,7 @@ class Locomotive.Views.Inputs.Rte.TableView extends Backbone.View
27
27
  placement: 'bottom'
28
28
  content: @$content
29
29
  html: true
30
+ sanitize: false
30
31
  trigger: 'manual'
31
32
  template: '<div class="popover" role="tooltip"><div class="arrow"></div><form class="simple_form"><div class="popover-content"></div></form></div>'
32
33
  @$link.data('bs.popover').setContent()
@@ -62,6 +62,7 @@ class Locomotive.Views.Inputs.RteView extends Backbone.View
62
62
  placement: 'bottom'
63
63
  content: html
64
64
  html: true
65
+ sanitize: false
65
66
  title: undefined)
66
67
 
67
68
  @$style_popover.data('bs.popover').options.content = html
@@ -79,7 +80,7 @@ class Locomotive.Views.Inputs.RteView extends Backbone.View
79
80
 
80
81
  # Counter
81
82
  count = @editor.currentView.doc.body.innerText.length
82
- @$counter = $("<div class='wysihtml5-counter'>#{count}</div>").insertAfter(@$('.form-wrapper'))
83
+ @$counter = $("<div class='wysihtml5-counter'>#{count}</div>").insertAfter(@$('> .form-wrapper'))
83
84
 
84
85
  $(@editor.currentView.doc.body).on 'keyup', (event) =>
85
86
  @$counter.html(event.currentTarget.innerText.length)
@@ -108,6 +108,7 @@
108
108
  display: flex;
109
109
  align-items: center;
110
110
  margin-bottom: 6px;
111
+ flex-wrap: wrap
111
112
  }
112
113
  .rdw-inline-dropdown {
113
114
  width: 50px;
@@ -121,6 +122,7 @@
121
122
  display: flex;
122
123
  align-items: center;
123
124
  margin-bottom: 6px;
125
+ flex-wrap: wrap
124
126
  }
125
127
  .rdw-block-dropdown {
126
128
  width: 110px;
@@ -129,6 +131,7 @@
129
131
  display: flex;
130
132
  align-items: center;
131
133
  margin-bottom: 6px;
134
+ flex-wrap: wrap
132
135
  }
133
136
  .rdw-fontsize-dropdown {
134
137
  min-width: 40px;
@@ -141,6 +144,7 @@
141
144
  display: flex;
142
145
  align-items: center;
143
146
  margin-bottom: 6px;
147
+ flex-wrap: wrap
144
148
  }
145
149
  .rdw-fontfamily-dropdown {
146
150
  width: 115px;
@@ -158,6 +162,7 @@
158
162
  display: flex;
159
163
  align-items: center;
160
164
  margin-bottom: 6px;
165
+ flex-wrap: wrap
161
166
  }
162
167
  .rdw-list-dropdown {
163
168
  width: 50px;
@@ -172,6 +177,7 @@
172
177
  display: flex;
173
178
  align-items: center;
174
179
  margin-bottom: 6px;
180
+ flex-wrap: wrap
175
181
  }
176
182
  .rdw-text-align-dropdown {
177
183
  width: 50px;
@@ -211,6 +217,7 @@
211
217
  align-items: center;
212
218
  margin-bottom: 6px;
213
219
  position: relative;
220
+ flex-wrap: wrap
214
221
  }
215
222
  .rdw-colorpicker-modal {
216
223
  position: absolute;
@@ -262,7 +269,6 @@
262
269
  width: 22px;
263
270
  height: 22px;
264
271
  min-width: 22px;
265
- box-shadow: 1px 2px 1px #BFBDBD inset;
266
272
  }
267
273
  .rdw-colorpicker-option:hover {
268
274
  box-shadow: 1px 2px 1px #BFBDBD;
@@ -278,6 +284,7 @@
278
284
  align-items: center;
279
285
  margin-bottom: 6px;
280
286
  position: relative;
287
+ flex-wrap: wrap
281
288
  }
282
289
  .rdw-link-dropdown {
283
290
  width: 50px;
@@ -363,6 +370,7 @@
363
370
  align-items: center;
364
371
  margin-bottom: 6px;
365
372
  position: relative;
373
+ flex-wrap: wrap
366
374
  }
367
375
  .rdw-embedded-modal {
368
376
  position: absolute;
@@ -466,6 +474,7 @@
466
474
  align-items: center;
467
475
  margin-bottom: 6px;
468
476
  position: relative;
477
+ flex-wrap: wrap
469
478
  }
470
479
  .rdw-emoji-modal {
471
480
  overflow: auto;
@@ -536,6 +545,7 @@
536
545
  align-items: center;
537
546
  margin-bottom: 6px;
538
547
  position: relative;
548
+ flex-wrap: wrap
539
549
  }
540
550
  .rdw-image-modal {
541
551
  position: absolute;
@@ -705,11 +715,13 @@
705
715
  align-items: center;
706
716
  margin-bottom: 6px;
707
717
  position: relative;
718
+ flex-wrap: wrap
708
719
  }
709
720
  .rdw-history-wrapper {
710
721
  display: flex;
711
722
  align-items: center;
712
723
  margin-bottom: 6px;
724
+ flex-wrap: wrap
713
725
  }
714
726
  .rdw-history-dropdownoption {
715
727
  height: 40px;
@@ -765,7 +777,7 @@
765
777
  border-radius: 2px;
766
778
  }
767
779
  .rdw-image-alignment-options-popup {
768
- position: absolute;;
780
+ position: absolute;
769
781
  background: white;
770
782
  display: flex;
771
783
  padding: 5px 2px;
@@ -1055,8 +1067,11 @@
1055
1067
  margin-left: 2rem;
1056
1068
  font-size: 1.2rem;
1057
1069
  font-weight: bold;
1058
- text-transform: uppercase;
1059
- color: #555; }
1070
+ text-transform: uppercase; }
1071
+ .editor-list-item--label a {
1072
+ color: #555; }
1073
+ .editor-list-item--label a:hover {
1074
+ text-decoration: none; }
1060
1075
  .editor-list-item--actions {
1061
1076
  margin-left: auto;
1062
1077
  margin-right: 2rem;
@@ -1227,6 +1242,18 @@
1227
1242
 
1228
1243
  .DraftEditor-editorContainer, .DraftEditor-root, .public-DraftEditor-content {
1229
1244
  height: 100%; }
1245
+ .DraftEditor-editorContainer h1, .DraftEditor-root h1, .public-DraftEditor-content h1 {
1246
+ font-size: 2.4rem; }
1247
+ .DraftEditor-editorContainer h2, .DraftEditor-root h2, .public-DraftEditor-content h2 {
1248
+ font-size: 2.2rem; }
1249
+ .DraftEditor-editorContainer h3, .DraftEditor-root h3, .public-DraftEditor-content h3 {
1250
+ font-size: 2.0rem; }
1251
+ .DraftEditor-editorContainer h4, .DraftEditor-root h4, .public-DraftEditor-content h4 {
1252
+ font-size: 1.8rem; }
1253
+ .DraftEditor-editorContainer h5, .DraftEditor-root h5, .public-DraftEditor-content h5 {
1254
+ font-size: 1.6rem; }
1255
+ .DraftEditor-editorContainer h6, .DraftEditor-root h6, .public-DraftEditor-content h6 {
1256
+ font-size: 1.4rem; }
1230
1257
 
1231
1258
  .editor-input-rich-text .rdw-editor-wrapper {
1232
1259
  border: 1px solid #e3e4e7;
@@ -16,6 +16,8 @@ module Locomotive
16
16
 
17
17
  helper 'Locomotive::CustomFields'
18
18
 
19
+ helper_method :default_location_params
20
+
19
21
  def index
20
22
  authorize ContentEntry
21
23
  @content_entries = service.all(list_params)
@@ -75,7 +77,7 @@ module Locomotive
75
77
  def bulk_destroy
76
78
  authorize ContentEntry, :destroy?
77
79
  service.bulk_destroy(params[:ids].split(','))
78
- respond_with @content_type, location: content_entries_path(current_site, @content_type.slug, page: params[:page], q: params[:q])
80
+ respond_with @content_type, location: content_entries_path(current_site, @content_type.slug, default_location_params)
79
81
  end
80
82
 
81
83
  private
@@ -104,8 +106,12 @@ module Locomotive
104
106
  params.require(:content_entry).permit(service.permitted_attributes)
105
107
  end
106
108
 
109
+ def default_location_params
110
+ { page: params[:page], q: params[:q] }.compact
111
+ end
112
+
107
113
  def location_after_persisting
108
- default = edit_content_entry_path(current_site, @content_type.slug, @content_entry)
114
+ default = edit_content_entry_path(current_site, @content_type.slug, @content_entry, default_location_params)
109
115
 
110
116
  if params[:_location].present?
111
117
  last_saved_location!(default)
@@ -0,0 +1,32 @@
1
+ module Locomotive
2
+ class ContentEntryImpersonationsController < BaseController
3
+
4
+ account_required & within_site
5
+
6
+ def create
7
+ if content_entry.with_authentication?
8
+ # automatically sign in the entry
9
+ session[:authenticated_entry_type] = content_type.slug
10
+ session[:authenticated_entry_id] = content_entry.id.to_s
11
+
12
+ # add a flag to notify that the sign in was done by impersonating the entry
13
+ session[:authenticated_impersonation] = '1'
14
+
15
+ redirect_to preview_url(current_site)
16
+ else
17
+ redirect_to content_entries_path(current_site, content_type.slug)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def content_type
24
+ @content_type ||= current_site.content_types.where(slug: params[:slug]).first!
25
+ end
26
+
27
+ def content_entry
28
+ @content_entry ||= content_type.entries.find(params[:content_entry_id])
29
+ end
30
+
31
+ end
32
+ end
@@ -10,8 +10,8 @@ module Locomotive
10
10
  #
11
11
  # @return [ String ] The label of the content type entry
12
12
  #
13
- def entry_label(content_type, entry)
14
- link = edit_content_entry_path(current_site, content_type.slug, entry)
13
+ def entry_label(content_type, entry, location_options = {})
14
+ link = edit_content_entry_path(current_site, content_type.slug, entry, location_options)
15
15
 
16
16
  if content_type.entry_template.blank?
17
17
  label = entry._label(content_type).presence || t(:untranslated, scope: 'locomotive.shared.list')
@@ -1,8 +1,8 @@
1
+ require 'adomain'
2
+
1
3
  module Locomotive
2
4
  class Notifications < ActionMailer::Base
3
5
 
4
- default from: Locomotive.config.mailer_sender
5
-
6
6
  def new_content_entry(account, entry)
7
7
  @site, @account = entry.site, account
8
8
  @entry, @type = entry, entry.content_type
@@ -12,8 +12,13 @@ module Locomotive
12
12
  'localhost'
13
13
 
14
14
  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)
15
20
 
16
- mail subject: subject, to: account.email
21
+ mail subject: subject, from: from, to: account.email, reply_to: from
17
22
  end
18
23
 
19
24
  protected
@@ -0,0 +1,13 @@
1
+ module Locomotive
2
+ module Concerns
3
+ module ContentEntry
4
+ module Authentication
5
+
6
+ def with_authentication?
7
+ self.content_type.entries_custom_fields.where(type: 'password').count > 0
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -6,7 +6,7 @@ module Locomotive
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- embeds_many :editable_elements, class_name: 'Locomotive::EditableElement', cascade_callbacks: true
9
+ embeds_many :editable_elements, class_name: 'Locomotive::EditableElement', cascade_callbacks: true
10
10
 
11
11
  accepts_nested_attributes_for :editable_elements
12
12
  end
@@ -13,6 +13,7 @@ module Locomotive
13
13
  include Concerns::ContentEntry::Localized
14
14
  include Concerns::ContentEntry::Counter
15
15
  include Concerns::ContentEntry::FileSize
16
+ include Concerns::ContentEntry::Authentication
16
17
  include Concerns::ContentEntry::NextPrevious
17
18
 
18
19
  ## fields ##
@@ -27,6 +27,7 @@ module Locomotive
27
27
  field :order_by # either a BSON::ObjectId (field id) or a String (:_position, ...etc)
28
28
  field :order_direction, default: 'asc'
29
29
  field :public_submission_enabled, type: Boolean, default: false
30
+ field :recaptcha_required, type: Boolean, default: false
30
31
  field :public_submission_accounts, type: Array, default: []
31
32
  field :number_of_entries
32
33
  field :display_settings, type: Hash
@@ -73,7 +73,7 @@ module Locomotive
73
73
  # A notification email is sent to the selected members of the site.
74
74
  #
75
75
  # @param [ Hash ] attributes The attributes of new content entry.
76
- # @param [ Hash ] options For now, only store the ip address of the person who submitted the content entry.
76
+ # @param [ Hash ] options For now, only store the ip address of the person who submitted the content entry + other accounts
77
77
  #
78
78
  # @return [ Object ] An instance of the content entry.
79
79
  #
@@ -82,8 +82,8 @@ module Locomotive
82
82
 
83
83
  without_tracking_activity { create(form.serializable_hash) }.tap do |entry|
84
84
  if entry.errors.empty?
85
- # send an email to selected local accounts
86
- send_notifications(entry)
85
+ # send an email to selected local accounts + potential external accounts described by their emails
86
+ send_notifications(entry, options[:emails])
87
87
 
88
88
  track_activity 'content_entry.created_public', locale: locale, parameters: activity_parameters(entry)
89
89
  end
@@ -166,14 +166,13 @@ module Locomotive
166
166
  end
167
167
  end
168
168
 
169
- def send_notifications(entry)
169
+ def send_notifications(entry, emails = nil)
170
170
  return unless self.content_type.public_submission_enabled?
171
171
 
172
- account_ids = (self.content_type.public_submission_accounts || []).map(&:to_s)
173
-
174
- self.content_type.site.accounts.each do |account|
175
- next unless account_ids.include?(account._id.to_s)
176
-
172
+ Locomotive::Account.any_of(
173
+ { :_id.in => self.content_type.public_submission_accounts || [] },
174
+ { :email.in => emails || [] }
175
+ ).each do |account|
177
176
  Locomotive::Notifications.new_content_entry(account, entry).deliver
178
177
  end
179
178
  end
@@ -10,7 +10,7 @@ module Locomotive
10
10
  end
11
11
 
12
12
  def extension_whitelist
13
- %w(jpg jpeg gif png css js swf flv mp4 eot svg svgz ttf ttc woff woff2 otf ico htc map html cur txt xml json)
13
+ %w(jpg jpeg gif png css js swf flv mp4 eot svg svgz ttf ttc woff woff2 otf ico htc map html cur txt xml json ogv)
14
14
  end
15
15
 
16
16
  def apply_content_type_exception(value)
@@ -19,7 +19,7 @@
19
19
 
20
20
  .text
21
21
  .item-label
22
- = entry_label(content_type, entry)
22
+ = entry_label(content_type, entry, default_location_params)
23
23
  .stamp
24
24
  == document_stamp(entry)
25
25
 
@@ -3,17 +3,24 @@
3
3
  - help @content_type.description
4
4
 
5
5
  - content_for :actions do
6
+ - if @content_entry.with_authentication?
7
+ = link_to content_entry_impersonation_path(current_site, @content_type.slug, @content_entry, default_location_params), class: 'btn btn-sm btn-primary', method: 'post', target: '_blank' do
8
+ i.fas.fa-user-secret
9
+ = t(:impersonate)
10
+
6
11
  - if @content_type.localized?
7
- = locale_picker_link
12
+ = locale_picker_link { |locale| url_for({ content_locale: locale }.merge(default_location_params)) }
8
13
  | &nbsp;
9
14
 
10
- = link_to t(:back, scope: 'locomotive.content_entries.shared').html_safe, content_entries_path(current_site, @content_type.slug), class: 'btn btn-sm btn-default'
15
+ = link_to t(:back, scope: 'locomotive.content_entries.shared').html_safe, content_entries_path(current_site, @content_type.slug, default_location_params), class: 'btn btn-sm btn-default'
11
16
 
12
17
  = locomotive_form_for @content_entry, as: :content_entry, url: content_entry_path(current_site, @content_type.slug, @content_entry), html: { multipart: true, novalidate: true } do |f|
13
18
 
14
19
  = hidden_field_tag :content_locale, current_content_locale
15
20
  = hidden_field_tag :active_tab, ''
16
- = hidden_field_tag :_location, params[:_location]
21
+ = hidden_field_tag :page, params[:page]
22
+ = hidden_field_tag :q, params[:q]
23
+ = hidden_field_tag :_location, params[:_location]
17
24
 
18
25
  = f.action class: 'hide'
19
26
 
@@ -3,11 +3,13 @@
3
3
  - help @content_type.description
4
4
 
5
5
  - content_for :actions do
6
- = link_to t(:back, scope: 'locomotive.content_entries.shared').html_safe, content_entries_path(current_site, @content_type.slug), class: 'btn btn-sm btn-default'
6
+ = link_to t(:back, scope: 'locomotive.content_entries.shared').html_safe, content_entries_path(current_site, @content_type.slug, default_location_params), class: 'btn btn-sm btn-default'
7
7
 
8
8
  = locomotive_form_for @content_entry, as: :content_entry, url: content_entries_path(current_site, @content_type.slug), html: { multipart: true, novalidate: true } do |f|
9
9
 
10
10
  = hidden_field_tag :active_tab, ''
11
+ = hidden_field_tag :page, params[:page]
12
+ = hidden_field_tag :q, params[:q]
11
13
  = hidden_field_tag :_location, params[:_location]
12
14
 
13
15
  = f.action class: 'hide'