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.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/app/api/locomotive/api/entities/content_type_entity.rb +1 -0
- data/app/api/locomotive/api/forms/content_type_form.rb +1 -0
- data/app/api/locomotive/api/resources/content_type_resource.rb +1 -0
- data/app/assets/javascripts/locomotive/editor.js +3028 -3112
- data/app/assets/javascripts/locomotive/views/content_assets/edit_image_view.js.coffee +1 -0
- data/app/assets/javascripts/locomotive/views/inputs/rte/edit_table_view.js.coffee +1 -0
- data/app/assets/javascripts/locomotive/views/inputs/rte/file_view.js.coffee +1 -0
- data/app/assets/javascripts/locomotive/views/inputs/rte/link_view.js.coffee +1 -0
- data/app/assets/javascripts/locomotive/views/inputs/rte/table_view.js.coffee +1 -0
- data/app/assets/javascripts/locomotive/views/inputs/rte_view.js.coffee.erb +2 -1
- data/app/assets/stylesheets/locomotive/editor.css +31 -4
- data/app/controllers/locomotive/content_entries_controller.rb +8 -2
- data/app/controllers/locomotive/content_entry_impersonations_controller.rb +32 -0
- data/app/helpers/locomotive/content_types_helper.rb +2 -2
- data/app/mailers/locomotive/notifications.rb +8 -3
- data/app/models/locomotive/concerns/content_entry/authentication.rb +13 -0
- data/app/models/locomotive/concerns/page/editable_elements.rb +1 -1
- data/app/models/locomotive/content_entry.rb +1 -0
- data/app/models/locomotive/content_type.rb +1 -0
- data/app/services/locomotive/content_entry_service.rb +8 -9
- data/app/uploaders/locomotive/theme_asset_uploader.rb +1 -1
- data/app/views/locomotive/content_entries/_list.html.slim +1 -1
- data/app/views/locomotive/content_entries/edit.html.slim +10 -3
- data/app/views/locomotive/content_entries/new.html.slim +3 -1
- data/config/routes.rb +2 -0
- data/lib/generators/locomotive/install/install_generator.rb +2 -10
- data/lib/locomotive/dependencies.rb +1 -0
- data/lib/locomotive/steam/services/api_content_entry_service.rb +2 -2
- data/lib/locomotive/steam/services/api_entry_submission_service.rb +13 -3
- data/lib/locomotive/version.rb +1 -1
- data/lib/tasks/locomotive_tasks.rake +29 -0
- data/vendor/assets/javascripts/locomotive/wysihtml5x-toolbar.js +0 -1
- 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
|
|
@@ -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
|
-
|
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,
|
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
|
@@ -6,7 +6,7 @@ module Locomotive
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
9
|
-
embeds_many
|
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
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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)
|
@@ -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
|
|
|
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 :
|
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'
|