locomotivecms 4.0.0.rc0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +1 -1
  4. data/Rakefile +0 -6
  5. data/app/api/locomotive/api/entities/site_entity.rb +1 -1
  6. data/app/api/locomotive/api/forms/content_type_form.rb +1 -0
  7. data/app/api/locomotive/api/forms/page_form.rb +2 -1
  8. data/app/api/locomotive/api/forms/site_form.rb +7 -1
  9. data/app/api/locomotive/api/helpers/persistence_helper.rb +3 -0
  10. data/app/api/locomotive/api/resources/account_resource.rb +15 -3
  11. data/app/api/locomotive/api/resources/content_type_resource.rb +3 -0
  12. data/app/api/locomotive/api/resources/membership_resource.rb +1 -1
  13. data/app/api/locomotive/api/resources/site_resource.rb +6 -0
  14. data/app/assets/javascripts/locomotive/editor.js +1311 -703
  15. data/app/assets/javascripts/locomotive/views/content_entries/edit_view.js.coffee +5 -0
  16. data/app/assets/javascripts/locomotive/views/content_entries/index_view.js.coffee +1 -0
  17. data/app/assets/javascripts/locomotive/views/content_entries/new_view.js.coffee +4 -0
  18. data/app/assets/javascripts/locomotive/views/current_site/edit_view.js.coffee +6 -0
  19. data/app/assets/javascripts/locomotive/views/page_content/edit_view.js.coffee +1 -0
  20. data/app/assets/javascripts/locomotive/views/pages/_form_view.js.coffee +6 -0
  21. data/app/assets/javascripts/locomotive/views/pages/edit_view.js.coffee +4 -0
  22. data/app/assets/javascripts/locomotive/views/pages/list_view.js.coffee +5 -0
  23. data/app/assets/javascripts/locomotive/views/pages/new_view.js.coffee +4 -0
  24. data/app/assets/stylesheets/locomotive/editor.css +390 -127
  25. data/app/assets/stylesheets/locomotive/new/_sidebar.scss +4 -0
  26. data/app/assets/stylesheets/locomotive/old/_list.scss +35 -0
  27. data/app/assets/stylesheets/locomotive/old/_main.scss +6 -6
  28. data/app/controllers/locomotive/concerns/authorization_controller.rb +4 -2
  29. data/app/controllers/locomotive/content_entries_controller.rb +6 -0
  30. data/app/controllers/locomotive/content_entry_impersonations_controller.rb +11 -0
  31. data/app/controllers/locomotive/current_site_controller.rb +0 -1
  32. data/app/controllers/locomotive/dashboard_controller.rb +2 -0
  33. data/app/helpers/locomotive/dashboard_helper.rb +0 -101
  34. data/app/helpers/locomotive/shared/activities_helper.rb +106 -0
  35. data/app/helpers/locomotive/shared/site_metafields_helper.rb +1 -1
  36. data/app/mailers/locomotive/notifications.rb +8 -0
  37. data/app/models/locomotive/account.rb +1 -0
  38. data/app/models/locomotive/concerns/site/cache.rb +2 -0
  39. data/app/models/locomotive/concerns/site/locales.rb +1 -0
  40. data/app/models/locomotive/content_type.rb +8 -7
  41. data/app/models/locomotive/membership.rb +1 -1
  42. data/app/models/locomotive/page.rb +2 -0
  43. data/app/models/locomotive/section.rb +1 -1
  44. data/app/policies/locomotive/account_policy.rb +1 -1
  45. data/app/policies/locomotive/content_asset_policy.rb +11 -3
  46. data/app/policies/locomotive/content_entry_policy.rb +11 -3
  47. data/app/policies/locomotive/page_policy.rb +16 -5
  48. data/app/policies/locomotive/site_policy.rb +18 -4
  49. data/app/policies/locomotive/translation_policy.rb +12 -4
  50. data/app/views/locomotive/content_entries/form/_actions.html.slim +12 -1
  51. data/app/views/locomotive/content_entries/form/_advanced.html.slim +1 -1
  52. data/app/views/locomotive/content_entries/index.html.slim +25 -17
  53. data/app/views/locomotive/current_site/form/_advanced.html.slim +6 -1
  54. data/app/views/locomotive/current_site/form/_url_redirections.html.slim +9 -3
  55. data/app/views/locomotive/layouts/account.html.slim +1 -33
  56. data/app/views/locomotive/pages/form/_main.html.slim +5 -2
  57. data/app/views/locomotive/shared/_head.html.slim +1 -1
  58. data/app/views/locomotive/shared/account/_head.html.slim +33 -0
  59. data/app/views/locomotive/shared/account/_navigation.html.slim +2 -1
  60. data/app/views/locomotive/sites/index.html.slim +4 -3
  61. data/config/locales/en.yml +7 -5
  62. data/config/locales/fr.yml +2 -0
  63. data/config/locales/simple_form.en.yml +3 -0
  64. data/config/webpack/production.js +1 -1
  65. data/lib/generators/locomotive/install/install_generator.rb +15 -2
  66. data/lib/generators/locomotive/install/templates/README +1 -1
  67. data/lib/generators/locomotive/install/templates/mongoid_heroku.yml +1 -1
  68. data/lib/locomotive/steam/middlewares/missing_translations.rb +3 -0
  69. data/lib/locomotive/steam/middlewares/page_editing.rb +7 -0
  70. data/lib/locomotive/steam/services/liquid_parser_with_cache_service.rb +22 -15
  71. data/lib/locomotive/steam_adaptor.rb +1 -1
  72. data/lib/locomotive/version.rb +1 -1
  73. data/lib/tasks/development_tasks.rake +7 -3
  74. data/lib/tasks/locomotive_tasks.rake +10 -0
  75. metadata +43 -16
  76. data/config/initializers/rails_fixes.rb +0 -16
  77. data/lib/locomotive/steam/middlewares/cache.rb +0 -69
@@ -14,6 +14,10 @@
14
14
  overflow: auto;
15
15
  background: $gray-darkest;
16
16
  transition: left $base-transition-speed linear;
17
+
18
+ .draggable:hover {
19
+ cursor: move;
20
+ }
17
21
  }
18
22
 
19
23
  // Sidebar top logo in the sidebar.
@@ -6,6 +6,41 @@
6
6
  .list-groups {
7
7
  margin-bottom: 10px;
8
8
 
9
+ .nav-container.nav-container--scroll {
10
+ padding-left: 15px;
11
+ padding-right: 15px;
12
+
13
+ .nav-tabs {
14
+ position: relative;
15
+ display: flex;
16
+ flex-wrap: nowrap;
17
+ overflow: auto;
18
+ -webkit-overflow-scrolling: touch;
19
+ -ms-overflow-style: -ms-autohiding-scrollbar;
20
+ border: none;
21
+ padding-left: 0px;
22
+ padding-right: 0px;
23
+ padding-bottom: 2px;
24
+
25
+ // don't display the scrollbar
26
+ &::-webkit-scrollbar { display: none; }
27
+
28
+ & > li {
29
+ flex: 0 0 auto;
30
+ z-index: 2;
31
+
32
+ &:last-child {
33
+ width: 9999px;
34
+ height: 2px;
35
+ border-bottom: 2px solid #ccc;
36
+ position: absolute;
37
+ bottom: 2px;
38
+ z-index: 1;
39
+ }
40
+ }
41
+ }
42
+ }
43
+
9
44
  .nav-tabs {
10
45
  padding-left: 15px;
11
46
  padding-right: 15px;
@@ -16,6 +16,12 @@
16
16
  color: rgba($main_title_color, 0.7);
17
17
  }
18
18
 
19
+ .dropdown-menu {
20
+ i {
21
+ margin-right: 1rem;
22
+ }
23
+ }
24
+
19
25
  .actions {
20
26
  text-align: right;
21
27
 
@@ -31,12 +37,6 @@
31
37
  .btn.dropdown-toggle {
32
38
  border-left: 1px solid $btn-primary-border;
33
39
  }
34
-
35
- .dropdown-menu {
36
- i {
37
- margin-right: 1rem;
38
- }
39
- }
40
40
  }
41
41
  }
42
42
  }
@@ -14,10 +14,12 @@ module Locomotive
14
14
  def render_access_denied(exception)
15
15
  ::Locomotive.log "[AccessDenied] #{exception.inspect}"
16
16
 
17
+ message = I18n.t('locomotive.errors.access_denied.message')
18
+
17
19
  if request.xhr?
18
- render json: { error: exception.message }, status: 401, layout: false
20
+ render json: { error: message }, status: 401, layout: false
19
21
  else
20
- flash[:alert] = exception.message
22
+ flash[:alert] = message
21
23
  redirect_to current_site? ? dashboard_path(current_site) : sites_path
22
24
  end
23
25
  end
@@ -80,6 +80,12 @@ module Locomotive
80
80
  respond_with @content_type, location: content_entries_path(current_site, @content_type.slug, default_location_params)
81
81
  end
82
82
 
83
+ def destroy
84
+ authorize @content_entry
85
+ service.bulk_destroy([*params[:id]])
86
+ respond_with @content_type, location: content_entries_path(current_site, @content_type.slug, default_location_params)
87
+ end
88
+
83
89
  private
84
90
 
85
91
  def load_content_type
@@ -12,6 +12,8 @@ module Locomotive
12
12
  # add a flag to notify that the sign in was done by impersonating the entry
13
13
  session[:authenticated_impersonation] = '1'
14
14
 
15
+ # notify signed in
16
+ notify(:signed_in, content_entry, request)
15
17
  redirect_to preview_url(current_site)
16
18
  else
17
19
  redirect_to content_entries_path(current_site, content_type.slug)
@@ -28,5 +30,14 @@ module Locomotive
28
30
  @content_entry ||= content_type.entries.find(params[:content_entry_id])
29
31
  end
30
32
 
33
+ def notify(action, entry, request)
34
+ ActiveSupport::Notifications.instrument("steam.auth.#{action}",
35
+ site: current_site,
36
+ entry: entry,
37
+ locale: locale,
38
+ request: request
39
+ )
40
+ end
41
+
31
42
  end
32
43
  end
@@ -20,7 +20,6 @@ module Locomotive
20
20
  def update
21
21
  authorize @site
22
22
  service.update(@site, site_params)
23
- logger.debug @site.errors.inspect
24
23
  respond_with @site, location: -> { edit_current_site_path(current_site) }
25
24
  end
26
25
 
@@ -1,6 +1,8 @@
1
1
  module Locomotive
2
2
  class DashboardController < BaseController
3
3
 
4
+ helper Locomotive::Shared::ActivitiesHelper
5
+
4
6
  account_required & within_site
5
7
 
6
8
  def show
@@ -12,106 +12,5 @@ module Locomotive
12
12
  end
13
13
  end
14
14
 
15
- # Activity
16
-
17
- def activity_to_icon(activity)
18
- case activity.domain
19
- when 'site' then 'fa-cog'
20
- when 'page' then 'fa-file-alt'
21
- when 'page_content' then 'fa-file-alt'
22
- when 'editable_element' then 'fa-file-alt'
23
- when 'content_entry' then activity.action == 'created_public' ? 'fa-comment' : 'fa-archive'
24
- when 'content_asset' then 'fa-image'
25
- when 'membership' then 'fa-user'
26
- when 'site_metafields' then current_site_metafields_ui[:icon]
27
- end
28
- end
29
-
30
- def render_activity_sentence(activity)
31
- params = activity.parameters
32
- locale = activity.locale
33
-
34
- options = case activity.key
35
- when /\Apage\./ then activity_page_options(params)
36
- when /\Acontent_entry\./ then activity_content_entry_options(params, locale)
37
- when 'page_content.updated' then activity_page_content_options(params, locale)
38
- when 'editable_element.updated_bulk' then activity_bulk_editable_elements_options(params)
39
- when 'content_asset.created_bulk' then { count: activity_emphasize(params[:assets].size) }
40
- when 'content_asset.destroyed' then { name: activity_emphasize(params[:name]) }
41
- when 'membership.created' then { name: activity_emphasize(params[:name]) }
42
- when 'site_metafields.updated' then { label: current_site_metafields_ui[:label].downcase }
43
- end
44
-
45
- activity_key_to_sentence(activity.key, options)
46
- end
47
-
48
- def render_activity_additional_information(activity)
49
- case activity.key
50
- when 'content_asset.created_bulk' then activity_bulk_content_assets(activity.parameters)
51
- # when 'editable_element.updated_bulk' then activity_bulk_editable_elements(activity.parameters)
52
- else nil
53
- end
54
- end
55
-
56
- def activity_key_to_sentence(key, options = nil)
57
- I18n.t(key, (options || {}).merge(scope: 'locomotive.activity')).html_safe
58
- end
59
-
60
- def activity_emphasize(text)
61
- content_tag(:strong, text)
62
- end
63
-
64
- def activity_page_options(params)
65
- if params[:_id]
66
- { page: link_to(params[:title], editable_elements_path(current_site, params[:_id])) }
67
- elsif params[:title]
68
- { page: activity_emphasize(params[:title]) }
69
- else
70
- nil
71
- end
72
- end
73
-
74
- def activity_page_content_options(params, locale)
75
- path = edit_page_content_path(current_site, params[:_id], content_locale: locale)
76
- { page: link_to(truncate(params[:title]), path).html_safe }
77
- end
78
-
79
- def activity_bulk_editable_elements_options(params)
80
- pages = params[:pages].map do |page|
81
- link_to truncate(page[:title]), editable_elements_path(current_site, page[:_id])
82
- end.join(', ').html_safe
83
-
84
- { pages: pages }
85
- end
86
-
87
- def activity_content_entry_options(params, locale)
88
- entry = if params[:_id]
89
- path = edit_content_entry_path(current_site, params[:content_type_slug], params[:_id], content_locale: locale)
90
- link_to(params[:label], path)
91
- elsif params[:label]
92
- activity_emphasize(params[:label])
93
- elsif params[:labels]
94
- activity_emphasize(params[:labels].join(', '))
95
- else
96
- nil
97
- end
98
-
99
- content_type = link_to(params[:content_type], content_entries_path(current_site, params[:content_type_slug]))
100
-
101
- { entry: entry, content_type: content_type }
102
- end
103
-
104
- def activity_bulk_content_assets(params)
105
- list = params[:assets].map do |asset|
106
- if asset[:image] && asset[:id] && current_site.content_assets.where(_id: asset[:id]).exists?
107
- content_tag(:li, link_to(image_tag(Locomotive::Dragonfly.resize_url(asset[:url], '60x60#'), alt: asset[:name]), asset[:url]))
108
- else
109
- content_tag(:li, link_to(truncate(asset[:name], length: 20), asset[:url]))
110
- end
111
- end.join("\n").html_safe
112
-
113
- content_tag(:ul, list, class: 'assets') + content_tag(:div, '', class: 'clearfix')
114
- end
115
-
116
15
  end
117
16
  end
@@ -0,0 +1,106 @@
1
+ module Locomotive
2
+ module Shared
3
+ module ActivitiesHelper
4
+
5
+ def activity_to_icon(activity)
6
+ case activity.domain
7
+ when 'site' then 'fa-cog'
8
+ when 'page' then 'fa-file-alt'
9
+ when 'page_content' then 'fa-file-alt'
10
+ when 'editable_element' then 'fa-file-alt'
11
+ when 'content_entry' then activity.action == 'created_public' ? 'fa-comment' : 'fa-archive'
12
+ when 'content_asset' then 'fa-image'
13
+ when 'membership' then 'fa-user'
14
+ when 'site_metafields' then current_site_metafields_ui[:icon]
15
+ end
16
+ end
17
+
18
+ def render_activity_sentence(activity)
19
+ params = activity.parameters
20
+ locale = activity.locale
21
+
22
+ options = case activity.key
23
+ when /\Apage\./ then activity_page_options(params)
24
+ when /\Acontent_entry\./ then activity_content_entry_options(params, locale)
25
+ when 'page_content.updated' then activity_page_content_options(params, locale)
26
+ when 'editable_element.updated_bulk' then activity_bulk_editable_elements_options(params)
27
+ when 'content_asset.created_bulk' then { count: activity_emphasize(params[:assets].size) }
28
+ when 'content_asset.destroyed' then { name: activity_emphasize(params[:name]) }
29
+ when 'membership.created' then { name: activity_emphasize(params[:name]) }
30
+ when 'site_metafields.updated' then { label: current_site_metafields_ui[:label].downcase }
31
+ end
32
+
33
+ activity_key_to_sentence(activity.key, options)
34
+ end
35
+
36
+ def render_activity_additional_information(activity)
37
+ case activity.key
38
+ when 'content_asset.created_bulk' then activity_bulk_content_assets(activity.parameters)
39
+ # when 'editable_element.updated_bulk' then activity_bulk_editable_elements(activity.parameters)
40
+ else nil
41
+ end
42
+ end
43
+
44
+ def activity_key_to_sentence(key, options = nil)
45
+ I18n.t(key, (options || {}).merge(scope: 'locomotive.activity')).html_safe
46
+ end
47
+
48
+ def activity_emphasize(text)
49
+ content_tag(:strong, text)
50
+ end
51
+
52
+ def activity_page_options(params)
53
+ if params[:_id]
54
+ { page: link_to(params[:title], editable_elements_path(current_site, params[:_id])) }
55
+ elsif params[:title]
56
+ { page: activity_emphasize(params[:title]) }
57
+ else
58
+ nil
59
+ end
60
+ end
61
+
62
+ def activity_page_content_options(params, locale)
63
+ path = edit_page_content_path(current_site, params[:_id], content_locale: locale)
64
+ { page: link_to(truncate(params[:title]), path).html_safe }
65
+ end
66
+
67
+ def activity_bulk_editable_elements_options(params)
68
+ pages = params[:pages].map do |page|
69
+ link_to truncate(page[:title]), editable_elements_path(current_site, page[:_id])
70
+ end.join(', ').html_safe
71
+
72
+ { pages: pages }
73
+ end
74
+
75
+ def activity_content_entry_options(params, locale)
76
+ entry = if params[:_id]
77
+ path = edit_content_entry_path(current_site, params[:content_type_slug], params[:_id], content_locale: locale)
78
+ link_to(params[:label], path)
79
+ elsif params[:label]
80
+ activity_emphasize(params[:label])
81
+ elsif params[:labels]
82
+ activity_emphasize(params[:labels].join(', '))
83
+ else
84
+ nil
85
+ end
86
+
87
+ content_type = link_to(params[:content_type], content_entries_path(current_site, params[:content_type_slug]))
88
+
89
+ { entry: entry, content_type: content_type }
90
+ end
91
+
92
+ def activity_bulk_content_assets(params)
93
+ list = params[:assets].map do |asset|
94
+ if asset[:image] && asset[:id] && current_site.content_assets.where(_id: asset[:id]).exists?
95
+ content_tag(:li, link_to(image_tag(Locomotive::Dragonfly.resize_url(asset[:url], '60x60#'), alt: asset[:name]), asset[:url]))
96
+ else
97
+ content_tag(:li, link_to(truncate(asset[:name], length: 20), asset[:url]))
98
+ end
99
+ end.join("\n").html_safe
100
+
101
+ content_tag(:ul, list, class: 'assets') + content_tag(:div, '', class: 'clearfix')
102
+ end
103
+
104
+ end
105
+ end
106
+ end
@@ -18,7 +18,7 @@ module Locomotive
18
18
  ui[:hint] = current_site_metafields_ui_t(_ui['hint'], t('locomotive.current_site_metafields.index.help', default: ''))
19
19
 
20
20
  # icon in the sidebar
21
- ui[:icon] = "far fa-#{_ui['icon'].present? ? _ui['icon'] : 'newspaper'}"
21
+ ui[:icon] = "fas fa-#{_ui['icon'].present? ? _ui['icon'] : 'newspaper'}"
22
22
  end
23
23
  end
24
24
 
@@ -18,6 +18,14 @@ module Locomotive
18
18
  Locomotive.config.mailer_sender
19
19
  end)
20
20
 
21
+ # attach uploaded files
22
+ if @type.public_submission_email_attachments
23
+ entry.file_custom_fields.each do |name|
24
+ next if (file = entry.send(name)&.file).nil?
25
+ attachments[file.filename] = file.read
26
+ end
27
+ end
28
+
21
29
  mail subject: subject, from: from, to: account.email, reply_to: from
22
30
  end
23
31
 
@@ -26,6 +26,7 @@ module Locomotive
26
26
  field :name
27
27
  field :locale, default: Locomotive.config.default_locale.to_s or 'en'
28
28
  field :super_admin, type: Boolean, default: false
29
+ field :visitor, type: Boolean, default: false
29
30
 
30
31
  ## validations ##
31
32
  validates_presence_of :name
@@ -9,6 +9,8 @@ module Locomotive
9
9
 
10
10
  ## fields ##
11
11
  field :cache_enabled, type: Boolean, default: false
12
+ field :cache_control
13
+ field :cache_vary
12
14
  field :template_version, type: DateTime
13
15
  field :content_version, type: DateTime
14
16
 
@@ -10,6 +10,7 @@ module Locomotive
10
10
  ## fields ##
11
11
  field :locales, type: ::RawArray, default: []
12
12
  field :prefix_default_locale, type: ::Boolean, default: false
13
+ field :bypass_browser_locale, type: ::Boolean, default: false
13
14
 
14
15
  ## validations ##
15
16
  validate :can_not_remove_default_locale
@@ -20,17 +20,18 @@ module Locomotive
20
20
  field :name
21
21
  field :description
22
22
  field :slug
23
- field :label_field_id, type: BSON::ObjectId
23
+ field :label_field_id, type: BSON::ObjectId
24
24
  field :label_field_name
25
25
  field :tree_parent_field_name
26
- field :group_by_field_id, type: BSON::ObjectId
26
+ field :group_by_field_id, type: BSON::ObjectId
27
27
  field :order_by # either a BSON::ObjectId (field id) or a String (:_position, ...etc)
28
- field :order_direction, default: 'asc'
29
- field :public_submission_enabled, type: Boolean, default: false
30
- field :recaptcha_required, type: Boolean, default: false
31
- field :public_submission_accounts, type: Array, default: []
28
+ field :order_direction, default: 'asc'
29
+ field :public_submission_enabled, type: Boolean, default: false
30
+ field :public_submission_email_attachments, type: Boolean, default: false
31
+ field :public_submission_accounts, type: Array, default: []
32
+ field :recaptcha_required, type: Boolean, default: false
32
33
  field :number_of_entries
33
- field :display_settings, type: Hash
34
+ field :display_settings, type: Hash
34
35
 
35
36
  ## associations ##
36
37
  has_many :entries, class_name: 'Locomotive::ContentEntry', dependent: :destroy
@@ -3,7 +3,7 @@ module Locomotive
3
3
 
4
4
  include Locomotive::Mongoid::Document
5
5
 
6
- ROLES = %w(author designer admin)
6
+ ROLES = %w(visitor author designer admin)
7
7
 
8
8
  ## fields ##
9
9
  field :role, default: 'author'