camaleon_cms 2.9.1 → 2.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -5
  3. data/app/apps/plugins/front_cache/admin_controller.rb +1 -0
  4. data/app/apps/plugins/front_cache/front_cache_helper.rb +23 -14
  5. data/app/apps/plugins/visibility_post/visibility_post_helper.rb +1 -1
  6. data/app/apps/themes/default/views/category.html.erb +1 -1
  7. data/app/apps/themes/default/views/post_tag.html.erb +1 -1
  8. data/app/apps/themes/default/views/post_type.html.erb +1 -1
  9. data/app/apps/themes/default/views/search.html.erb +1 -1
  10. data/app/apps/themes/new/views/category.html.erb +1 -1
  11. data/app/apps/themes/new/views/post_tag.html.erb +1 -1
  12. data/app/apps/themes/new/views/post_type.html.erb +1 -1
  13. data/app/apps/themes/new/views/search.html.erb +1 -1
  14. data/app/controllers/camaleon_cms/admin/appearances/nav_menus_controller.rb +22 -5
  15. data/app/controllers/camaleon_cms/admin/appearances/widgets/assign_controller.rb +4 -2
  16. data/app/controllers/camaleon_cms/admin/appearances/widgets/main_controller.rb +3 -3
  17. data/app/controllers/camaleon_cms/admin/appearances/widgets/sidebar_controller.rb +2 -2
  18. data/app/controllers/camaleon_cms/admin/categories_controller.rb +9 -5
  19. data/app/controllers/camaleon_cms/admin/media_controller.rb +18 -5
  20. data/app/controllers/camaleon_cms/admin/post_tags_controller.rb +7 -4
  21. data/app/controllers/camaleon_cms/admin/posts/drafts_controller.rb +1 -1
  22. data/app/controllers/camaleon_cms/admin/posts_controller.rb +6 -3
  23. data/app/controllers/camaleon_cms/admin/sessions_controller.rb +2 -2
  24. data/app/controllers/camaleon_cms/admin/settings/custom_fields_controller.rb +33 -11
  25. data/app/controllers/camaleon_cms/admin/settings/post_types_controller.rb +13 -4
  26. data/app/controllers/camaleon_cms/admin/settings/sites_controller.rb +7 -4
  27. data/app/controllers/camaleon_cms/admin/settings_controller.rb +7 -4
  28. data/app/controllers/camaleon_cms/admin/user_roles_controller.rb +2 -2
  29. data/app/controllers/camaleon_cms/admin/users_controller.rb +16 -12
  30. data/app/controllers/camaleon_cms/admin_controller.rb +8 -0
  31. data/app/controllers/camaleon_cms/apps/plugins_admin_controller.rb +5 -0
  32. data/app/controllers/concerns/camaleon_cms/admin/custom_fields_concern.rb +29 -0
  33. data/app/decorators/camaleon_cms/post_decorator.rb +1 -1
  34. data/app/decorators/camaleon_cms/user_decorator.rb +1 -1
  35. data/app/helpers/camaleon_cms/admin/application_helper.rb +17 -17
  36. data/app/helpers/camaleon_cms/admin/post_type_helper.rb +25 -22
  37. data/app/helpers/camaleon_cms/comment_helper.rb +74 -40
  38. data/app/helpers/camaleon_cms/frontend/content_select_helper.rb +1 -1
  39. data/app/helpers/camaleon_cms/frontend/nav_menu_helper.rb +1 -1
  40. data/app/helpers/camaleon_cms/html_helper.rb +15 -1
  41. data/app/helpers/camaleon_cms/session_helper.rb +13 -1
  42. data/app/helpers/camaleon_cms/site_helper.rb +16 -3
  43. data/app/helpers/camaleon_cms/uploader_helper.rb +102 -51
  44. data/app/models/camaleon_cms/ability.rb +54 -102
  45. data/app/models/camaleon_cms/category.rb +2 -0
  46. data/app/models/camaleon_cms/custom_field.rb +14 -0
  47. data/app/models/camaleon_cms/custom_field_group.rb +38 -1
  48. data/app/models/camaleon_cms/custom_fields_relationship.rb +1 -1
  49. data/app/models/camaleon_cms/nav_menu.rb +2 -0
  50. data/app/models/camaleon_cms/nav_menu_item.rb +2 -0
  51. data/app/models/camaleon_cms/plugin.rb +2 -0
  52. data/app/models/camaleon_cms/post_tag.rb +2 -0
  53. data/app/models/camaleon_cms/post_type.rb +2 -0
  54. data/app/models/camaleon_cms/site.rb +2 -0
  55. data/app/models/camaleon_cms/term_taxonomy.rb +0 -2
  56. data/app/models/camaleon_cms/theme.rb +2 -0
  57. data/app/models/camaleon_cms/user_role.rb +13 -0
  58. data/app/models/camaleon_cms/widget/main.rb +2 -0
  59. data/app/models/camaleon_cms/widget/sidebar.rb +2 -0
  60. data/app/models/camaleon_record.rb +34 -0
  61. data/app/models/concerns/camaleon_cms/custom_fields_read.rb +4 -4
  62. data/app/models/concerns/camaleon_cms/metas.rb +1 -1
  63. data/app/models/current_request.rb +16 -0
  64. data/app/uploaders/camaleon_cms_aws_uploader.rb +8 -1
  65. data/app/validators/camaleon_cms/post_uniq_validator.rb +16 -4
  66. data/app/views/camaleon_cms/admin/appearances/nav_menus/_left_menu_items.html.erb +2 -2
  67. data/app/views/camaleon_cms/admin/appearances/widgets/main/form.html.erb +1 -1
  68. data/app/views/camaleon_cms/admin/categories/index.html.erb +1 -1
  69. data/app/views/camaleon_cms/admin/comments/index.html.erb +2 -2
  70. data/app/views/camaleon_cms/admin/comments/list.html.erb +1 -1
  71. data/app/views/camaleon_cms/admin/post_tags/index.html.erb +1 -1
  72. data/app/views/camaleon_cms/admin/posts/_sidebar.html.erb +1 -1
  73. data/app/views/camaleon_cms/admin/posts/index.html.erb +3 -3
  74. data/app/views/camaleon_cms/admin/search.html.erb +1 -1
  75. data/app/views/camaleon_cms/admin/settings/custom_fields/_render.html.erb +23 -2
  76. data/app/views/camaleon_cms/admin/settings/custom_fields/fields/_select_eval.html.erb +1 -1
  77. data/app/views/camaleon_cms/admin/settings/custom_fields/form.html.erb +1 -1
  78. data/app/views/camaleon_cms/admin/settings/custom_fields/index.html.erb +1 -1
  79. data/app/views/camaleon_cms/admin/settings/post_types/index.html.erb +1 -1
  80. data/app/views/camaleon_cms/admin/settings/sites/index.html.erb +1 -1
  81. data/app/views/camaleon_cms/admin/user_roles/form.html.erb +79 -5
  82. data/app/views/camaleon_cms/admin/user_roles/index.html.erb +1 -1
  83. data/app/views/camaleon_cms/admin/users/index.html.erb +1 -1
  84. data/app/views/layouts/camaleon_cms/admin/_flash_messages.html.erb +2 -2
  85. data/config/initializers/custom_initializers.rb +2 -2
  86. data/config/locales/camaleon_cms/admin/ar.yml +6 -2
  87. data/config/locales/camaleon_cms/admin/de.yml +6 -2
  88. data/config/locales/camaleon_cms/admin/en.yml +6 -2
  89. data/config/locales/camaleon_cms/admin/es.yml +6 -2
  90. data/config/locales/camaleon_cms/admin/fr.yml +6 -2
  91. data/config/locales/camaleon_cms/admin/it.yml +6 -2
  92. data/config/locales/camaleon_cms/admin/nl.yml +7 -2
  93. data/config/locales/camaleon_cms/admin/pt-BR.yml +6 -2
  94. data/config/locales/camaleon_cms/admin/pt.yml +6 -2
  95. data/config/locales/camaleon_cms/admin/ru.yml +6 -2
  96. data/config/locales/camaleon_cms/admin/uk.yml +6 -2
  97. data/config/locales/camaleon_cms/admin/zh-CH.yml +6 -2
  98. data/db/migrate/20150611161134_post_table_into_utf8.rb +14 -14
  99. data/db/migrate/20150926095310_rename_column_posts.rb +3 -3
  100. data/db/migrate/20151212095328_add_confirm_token_to_users.rb +3 -3
  101. data/db/migrate/20160504155652_add_feature_to_posts.rb +1 -1
  102. data/db/migrate/20160504155653_move_first_name_of_users.rb +2 -2
  103. data/db/migrate/20160609121449_add_group_to_custom_field_values.rb +1 -1
  104. data/db/migrate/20161215202255_drop_user_relationship_table.rb +1 -1
  105. data/db/migrate/20180124132318_create_media.rb +1 -1
  106. data/db/migrate/20180704211100_adjust_field_length.rb +1 -1
  107. data/lib/camaleon_cms/version.rb +1 -1
  108. data/lib/ext/string.rb +3 -3
  109. data/lib/plugin_routes.rb +3 -3
  110. data/lib/tasks/custom_fields_roles.rake +56 -0
  111. metadata +64 -5
@@ -4,6 +4,7 @@ module CamaleonCms
4
4
  class PostTypesController < CamaleonCms::Admin::SettingsController
5
5
  before_action :set_post_type, only: %i[show edit update destroy]
6
6
  before_action :set_data_term, only: %i[create update]
7
+
7
8
  add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.content_groups'), :cama_admin_settings_post_types_path
8
9
 
9
10
  def index
@@ -20,7 +21,7 @@ module CamaleonCms
20
21
 
21
22
  def update
22
23
  if @post_type.update(@data_term)
23
- @post_type.set_field_values(params.require(:field_options).permit!) if params[:field_options].present?
24
+ @post_type.set_field_values(cama_permitted_field_options('PostType')) if params[:field_options].present?
24
25
  hooks_run('updated_post_type', { post_type: @post_type })
25
26
  flash[:notice] = t('camaleon_cms.admin.post_type.message.updated')
26
27
  redirect_to action: :index
@@ -32,7 +33,7 @@ module CamaleonCms
32
33
  def create
33
34
  @post_type = current_site.post_types.new(@data_term)
34
35
  if @post_type.save
35
- @post_type.set_field_values(params.require(:field_options).permit!) if params[:field_options].present?
36
+ @post_type.set_field_values(cama_permitted_field_options('PostType')) if params[:field_options].present?
36
37
  hooks_run('created_post_type', { post_type: @post_type })
37
38
  flash[:notice] = t('camaleon_cms.admin.post_type.message.created')
38
39
  redirect_to action: :index
@@ -49,11 +50,19 @@ module CamaleonCms
49
50
  private
50
51
 
51
52
  def set_data_term
52
- data_term = params.require(:post_type).permit!
53
- data_term[:data_options] = params.require(:meta).permit!
53
+ data_term = params.require(:post_type).permit(:name, :slug, :description, :parent_id)
54
+ data_term[:data_options] = params[:meta].present? ? post_type_meta_params : {}
54
55
  @data_term = data_term
55
56
  end
56
57
 
58
+ def post_type_meta_params
59
+ params.require(:meta).permit(:icon, :has_layout, :default_layout, :has_template, :default_template,
60
+ :has_category, :has_single_category, :has_tags, :has_content, :has_summary,
61
+ :has_comments, :has_featured, :has_seo, :has_parent_structure, :has_picture,
62
+ :posts_image_dimension, :posts_thumb_versions, :posts_thumb_size,
63
+ :is_required_picture, :contents_route_format, :default_thumb)
64
+ end
65
+
57
66
  def set_post_type
58
67
  @post_type = current_site.post_types.find_by_id(params[:id])
59
68
  rescue StandardError
@@ -23,11 +23,11 @@ module CamaleonCms
23
23
 
24
24
  def update
25
25
  tmp = @site.slug
26
- if @site.update(params.require(:site).permit!)
26
+ if @site.update(site_params)
27
27
  save_metas(@site)
28
28
  flash[:notice] = t('camaleon_cms.admin.sites.message.updated')
29
29
  if @site.id == Cama::Site.main_site.id && tmp != @site.slug
30
- redirect_to @site.the_admin_url
30
+ redirect_to(cama_admin_path)
31
31
  else
32
32
  redirect_to action: :index
33
33
  end
@@ -43,8 +43,7 @@ module CamaleonCms
43
43
  end
44
44
 
45
45
  def create
46
- site_data = params.require(:site).permit!
47
- @site = CamaleonCms::Site.new(site_data)
46
+ @site = CamaleonCms::Site.new(site_params)
48
47
  if @site.save
49
48
  save_metas(@site)
50
49
  site_after_install(@site, @site.get_theme_slug)
@@ -84,6 +83,10 @@ module CamaleonCms
84
83
  flash[:error] = t('camaleon_cms.admin.sites.message.unauthorized')
85
84
  redirect_to cama_admin_path
86
85
  end
86
+
87
+ def site_params
88
+ params.require(:site).permit(:name, :slug, :description)
89
+ end
87
90
  end
88
91
  end
89
92
  end
@@ -1,8 +1,11 @@
1
1
  module CamaleonCms
2
2
  module Admin
3
3
  class SettingsController < CamaleonCms::AdminController
4
+ include CamaleonCms::Admin::CustomFieldsConcern
5
+
4
6
  before_action :validate_role, except: %i[theme save_theme]
5
7
  before_action :validate_role_theme, only: %i[theme save_theme]
8
+
6
9
  add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.settings')
7
10
 
8
11
  def index
@@ -19,10 +22,10 @@ module CamaleonCms
19
22
  def site_saved
20
23
  @site = current_site
21
24
  cache_slug = @site.slug
22
- if @site.update(params.require(:site).permit!)
25
+ if @site.update(params.require(:site).permit(:name, :slug, :description))
23
26
  @site.set_options(params[:options]) if params[:options].present?
24
27
  @site.set_metas(params[:metas]) if params[:metas].present?
25
- @site.set_field_values(params[:field_options])
28
+ @site.set_field_values(cama_permitted_field_options('Site'))
26
29
  flash[:notice] = t('camaleon_cms.admin.settings.message.site_updated')
27
30
  args = { action: :site }
28
31
  args[:host], args[:port] = @site.get_domain.to_s.split(':') if cache_slug != @site.slug
@@ -62,7 +65,7 @@ module CamaleonCms
62
65
  current_theme.set_field_values(params[:theme_fields]) if params[:theme_fields].present?
63
66
  current_theme.set_options(params[:theme_option]) if params[:theme_option].present?
64
67
  current_theme.set_metas(params[:theme_meta]) if params[:theme_meta].present?
65
- current_theme.set_field_values(params[:field_options])
68
+ current_theme.set_field_values(cama_permitted_field_options('Theme'))
66
69
  hook_run(current_theme.settings, 'on_theme_settings', current_theme) # permit to save extra/custom values by this hook
67
70
  flash[:notice] = t('camaleon_cms.admin.message.updated_success', default: 'Theme updated successfully')
68
71
  redirect_to action: :theme
@@ -74,7 +77,7 @@ module CamaleonCms
74
77
  CamaleonCms::HtmlMailer.sender(params[:email], 'Test', data).deliver_now
75
78
  head :ok
76
79
  rescue StandardError => e
77
- render inline: e.message, status: 502
80
+ render plain: e.message, status: 502
78
81
  end
79
82
 
80
83
  private
@@ -20,7 +20,7 @@ module CamaleonCms
20
20
  end
21
21
 
22
22
  def create
23
- user_role_data = params.require(:user_role).permit!
23
+ user_role_data = params.require(:user_role).permit(:name, :slug, :description)
24
24
  @user_role = current_site.user_roles.new(user_role_data)
25
25
  if @user_role.save
26
26
  @user_role.set_meta("_post_type_#{current_site.id}",
@@ -40,7 +40,7 @@ module CamaleonCms
40
40
  end
41
41
 
42
42
  def update
43
- if @user_role.update(params.require(:user_role).permit!)
43
+ if @user_role.update(params.require(:user_role).permit(:name, :slug, :description))
44
44
  if @user_role.editable?
45
45
  @user_role.set_meta("_post_type_#{current_site.id}",
46
46
  defined?(params[:rol_values][:post_type]) ? params[:rol_values][:post_type] : {})
@@ -1,8 +1,12 @@
1
1
  module CamaleonCms
2
2
  module Admin
3
3
  class UsersController < CamaleonCms::AdminController
4
+ include CamaleonCms::Admin::CustomFieldsConcern
5
+
4
6
  before_action :validate_role, except: %i[profile profile_edit]
7
+
5
8
  add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.users'), :cama_admin_users_url
9
+
6
10
  before_action :set_user, only: %i[show edit update destroy impersonate]
7
11
 
8
12
  def index
@@ -31,8 +35,8 @@ module CamaleonCms
31
35
  r = { user: @user }
32
36
  hooks_run('user_update', r)
33
37
  if @user.update(user_params)
34
- @user.set_metas(params[:meta]) if params[:meta].present?
35
- @user.set_field_values(params[:field_options])
38
+ @user.set_metas(user_meta_params) if params[:meta].present?
39
+ @user.set_field_values(cama_permitted_field_options('User')) if params[:field_options].present?
36
40
  r = { user: @user, message: t('camaleon_cms.admin.users.message.updated'), params: params }
37
41
  hooks_run('user_after_edited', r)
38
42
  flash[:notice] = r[:message]
@@ -93,13 +97,12 @@ module CamaleonCms
93
97
  end
94
98
 
95
99
  def create
96
- user_data = params.require(:user).permit!
97
- @user = current_site.users.new(user_data)
100
+ @user = current_site.users.new(user_params)
98
101
  r = { user: @user }
99
102
  hooks_run('user_create', r)
100
103
  if @user.save
101
- @user.set_metas(params[:meta]) if params[:meta].present?
102
- @user.set_field_values(params[:field_options])
104
+ @user.set_metas(user_meta_params) if params[:meta].present?
105
+ @user.set_field_values(cama_permitted_field_options('User')) if params[:field_options].present?
103
106
  r = { user: @user }
104
107
  hooks_run('user_created', r)
105
108
  flash[:notice] = t('camaleon_cms.admin.users.message.created')
@@ -138,12 +141,13 @@ module CamaleonCms
138
141
  end
139
142
 
140
143
  def user_params
141
- parameters = params.require(:user)
142
- if cama_current_user.role_grantor?(@user)
143
- parameters.permit(:username, :email, :role, :first_name, :last_name)
144
- else
145
- parameters.permit(:username, :email, :first_name, :last_name)
146
- end
144
+ p = params.require(:user).permit(:username, :email, :first_name, :last_name, :password, :password_confirmation)
145
+ p[:role] = params[:user][:role] if cama_current_user.role_grantor?(@user) && params[:user][:role].present?
146
+ p
147
+ end
148
+
149
+ def user_meta_params
150
+ params.require(:meta).permit(:avatar, :slogan)
147
151
  end
148
152
 
149
153
  def set_user
@@ -7,6 +7,7 @@ module CamaleonCms
7
7
  include CamaleonCms::Admin::ApplicationHelper
8
8
  # layout 'camaleon_cms/admin'
9
9
  before_action :cama_authenticate
10
+ before_action :keep_request_attrs
10
11
  before_action :admin_init_actions
11
12
  before_action :admin_logged_actions
12
13
  before_action :admin_before_hooks
@@ -83,6 +84,13 @@ module CamaleonCms
83
84
  hooks_run('admin_before_load')
84
85
  end
85
86
 
87
+ # Set cama_current_user and current_site in CurrentRequest so models can access the current context.
88
+ # CurrentRequest is an ActiveSupport::CurrentAttributes subclass that auto-resets per request.
89
+ def keep_request_attrs
90
+ CurrentRequest.user = cama_current_user
91
+ CurrentRequest.site = current_site
92
+ end
93
+
86
94
  # trigger hooks for admin panel after admin load
87
95
  def admin_after_hooks
88
96
  hooks_run('admin_after_load')
@@ -2,6 +2,7 @@ module CamaleonCms
2
2
  module Apps
3
3
  class PluginsAdminController < CamaleonCms::AdminController
4
4
  before_action :init_plugin
5
+ before_action :authorize_plugin
5
6
 
6
7
  private
7
8
 
@@ -21,6 +22,10 @@ module CamaleonCms
21
22
  lookup_context.prefixes.prepend(params[:controller].sub("plugins/#{plugin_name}",
22
23
  "plugins/#{plugin_name}/views"))
23
24
  end
25
+
26
+ def authorize_plugin
27
+ authorize! :manage, :plugins
28
+ end
24
29
  end
25
30
  end
26
31
  end
@@ -0,0 +1,29 @@
1
+ module CamaleonCms
2
+ module Admin
3
+ module CustomFieldsConcern
4
+ extend ActiveSupport::Concern
5
+
6
+ private
7
+
8
+ # Only permit field_options that match registered custom field slugs
9
+ def cama_permitted_field_options(object_class)
10
+ return {} unless params[:field_options].present?
11
+
12
+ allowed_keys = cama_custom_field_allowed_slugs(object_class)
13
+ return {} if allowed_keys.blank?
14
+
15
+ field_options = params.require(:field_options)
16
+ field_options.permit(field_options.keys.select { |k| k.to_s =~ /\A\d+\z/ }.index_with do
17
+ allowed_keys.index_with { [:id, :group_number, { values: {} }] }
18
+ end).to_h
19
+ end
20
+
21
+ def cama_custom_field_allowed_slugs(object_class)
22
+ CamaleonCms::CustomField.where(
23
+ parent_id: CamaleonCms::CustomField.where(object_class: object_class).select(:id),
24
+ object_class: '_fields'
25
+ ).pluck(:slug).uniq
26
+ end
27
+ end
28
+ end
29
+ end
@@ -4,7 +4,7 @@ module CamaleonCms
4
4
  delegate_all
5
5
 
6
6
  def the_title(locale = nil)
7
- r = { title: object.title.to_s.translate(get_locale(locale)), post: object }
7
+ r = { title: h.h(object.title.to_s.translate(get_locale(locale))), post: object }
8
8
  h.hooks_run('post_the_title', r)
9
9
  r[:title]
10
10
  end
@@ -53,7 +53,7 @@ module CamaleonCms
53
53
  end
54
54
 
55
55
  def role_grantor?(other_user)
56
- h.can?(:manage, :users) && id != other_user.id
56
+ h.can?(:manage, :users) && (other_user.nil? || id != other_user.id)
57
57
  end
58
58
 
59
59
  def self.object_class_name
@@ -9,27 +9,27 @@ module CamaleonCms
9
9
 
10
10
  # render pagination for current items
11
11
  # items is a will pagination object
12
- # sample: <%= raw cama_do_pagination(@posts) %>
12
+ # sample: <%= cama_do_pagination(@posts) %>
13
13
  def cama_do_pagination(items, *will_paginate_options)
14
14
  will_paginate_options = will_paginate_options.extract_options!
15
- custom_class = will_paginate_options[:panel_class]
16
- will_paginate_options.delete(:panel_class)
17
- "<div class='row #{custom_class} pagination_panel cama_ajax_request'>
18
- <div class='col-md-10'>
19
- #{begin
15
+ custom_class = will_paginate_options.delete(:panel_class)
16
+ content_tag(:div, class: "row #{custom_class} pagination_panel cama_ajax_request") do
17
+ concat(content_tag(:div, class: 'col-md-10') do
20
18
  will_paginate(items, will_paginate_options)
21
19
  rescue StandardError
22
20
  ''
23
- end}
24
- </div>
25
- <div class='col-md-2 text-right total-items'>
26
- <strong>#{I18n.t('camaleon_cms.admin.table.total', default: 'Total')}: #{begin
27
- items.total_entries
28
- rescue StandardError
29
- items.count
30
- end} </strong>
31
- </div>
32
- </div>"
21
+ end)
22
+ concat(content_tag(:div, class: 'col-md-2 text-right total-items') do
23
+ content_tag(:strong) do
24
+ total = begin
25
+ items.total_entries
26
+ rescue StandardError
27
+ items.count
28
+ end
29
+ "#{I18n.t('camaleon_cms.admin.table.total', default: 'Total')}: #{total}"
30
+ end
31
+ end)
32
+ end
33
33
  end
34
34
 
35
35
  # return the locale for frontend translations initialized in admin controller
@@ -41,7 +41,7 @@ module CamaleonCms
41
41
 
42
42
  # print code with auto copy
43
43
  def cama_shortcode_print(code)
44
- "<input onmousedown=\"this.clicked = 1;\" readonly onfocus=\"if (!this.clicked) this.select(); else this.clicked = 2;\" onclick=\"if (this.clicked == 2) this.select(); this.clicked = 0;\" class='code_style' tabindex='-1' value=\"#{code}\">"
44
+ content_tag(:input, nil, class: 'code_style', readonly: true, onmousedown: 'this.clicked = 1;', onfocus: 'if (!this.clicked) this.select(); else this.clicked = 2;', onclick: 'if (this.clicked == 2) this.select(); this.clicked = 0;', tabindex: '-1', value: code)
45
45
  end
46
46
  end
47
47
  end
@@ -11,17 +11,16 @@ module CamaleonCms
11
11
  end
12
12
 
13
13
  def post_type_status(status, color = 'default')
14
- "<span class='label label-#{color} label-form'>#{status}</span>"
14
+ content_tag(:span, status, class: "label label-#{color} label-form")
15
15
  end
16
16
 
17
17
  # taxonomies -> (categories || post_tags)
18
18
  def post_type_list_taxonomy(taxonomies, color = 'primary')
19
- html = ''
20
- taxonomies.decorate.each do |f|
21
- html += "<a class='cama_ajax_request' href='#{cama_admin_post_type_taxonomy_posts_path(@post_type.id,
22
- f.taxonomy, f.id)}'><span class='label label-#{color} label-form'>#{f.the_title}</span></a> "
23
- end
24
- html
19
+ taxonomies.decorate.map do |f|
20
+ link_to(cama_admin_post_type_taxonomy_posts_path(@post_type.id, f.taxonomy, f.id), class: 'cama_ajax_request') do
21
+ content_tag(:span, f.the_title, class: "label label-#{color} label-form")
22
+ end
23
+ end.join(' ').html_safe
25
24
  end
26
25
 
27
26
  # sort array of posts to build post's tree
@@ -54,23 +53,27 @@ module CamaleonCms
54
53
  values = [], class_cat = '', required = false)
55
54
  if categories.count < 1
56
55
  return t('camaleon_cms.admin.post_type.message.no_created_html',
57
- taxonomy: taxonomy == 'categories' ? t('camaleon_cms.admin.table.categories') : t('camaleon_cms.admin.table.tags')).to_s
58
- end
59
-
60
- html = "<ul class='#{class_cat}'>"
61
- categories.decorate.each do |f|
62
- html += '<li>'
63
- html += "<label class='class_slug' data-post_link_edit='#{f.the_edit_url}'> "
64
- html += "<input data-error-place='#validation_error_list_#{name}' type='#{type}' name='#{name}[]' #{values.to_i.include?(f.id) ? 'checked' : ''} value='#{f.id}' class = '#{if required
65
- 'required'
66
- end}' />"
67
- html += "#{f.the_title} </label> "
68
- html += post_type_html_inputs(f, 'children', name, type, values, 'children') if f.children.present?
69
- html += '</li>'
56
+ taxonomy: taxonomy == 'categories' ? t('camaleon_cms.admin.table.categories') : t('camaleon_cms.admin.table.tags'))
70
57
  end
71
58
 
72
- html += "</ul><div id='validation_error_list_#{name}'></div>"
73
- html
59
+ content_tag(:ul, class: class_cat) do
60
+ categories.decorate.map do |f|
61
+ content_tag(:li) do
62
+ is_checked = Array(values).map(&:to_s).include?(f.id.to_s)
63
+ input_options = { class: (required ? 'required' : ''), data: { error_place: "#validation_error_list_#{name}" } }
64
+ input_tag = if type == 'radio'
65
+ radio_button_tag("#{name}[]", f.id, is_checked, input_options)
66
+ else
67
+ check_box_tag("#{name}[]", f.id, is_checked, input_options)
68
+ end
69
+ res = content_tag(:label, class: 'class_slug', data: { post_link_edit: f.the_edit_url }) do
70
+ "#{input_tag} #{f.the_title} ".html_safe
71
+ end
72
+ res << post_type_html_inputs(f, 'children', name, type, values, 'children') if f.children.present?
73
+ res
74
+ end
75
+ end.join.html_safe
76
+ end + content_tag(:div, '', id: "validation_error_list_#{name}")
74
77
  end
75
78
  end
76
79
  end
@@ -1,5 +1,7 @@
1
1
  module CamaleonCms
2
2
  module CommentHelper
3
+ LABELS = { 'approved' => 'success', 'pending' => 'warning', 'spam' => 'danger' }.freeze
4
+
3
5
  # return common data to save a new comment
4
6
  # user_id, author, aothor_email, author_ip, approved, :agent
5
7
  def cama_comments_get_common_data
@@ -16,47 +18,79 @@ module CamaleonCms
16
18
  # render as html content all comments recursively
17
19
  # comments: collection of comments
18
20
  def cama_comments_render_html(comments)
19
- res = ''
20
- labels = { 'approved' => 'success', 'pending' => 'warning', 'spam' => 'danger' }
21
- comments.decorate.each do |comment|
21
+ comments.decorate.map do |comment|
22
22
  author = comment.the_author
23
- res << "<div class='media'>
24
- <div class='media-left'>
25
- <a href='#{author.the_admin_profile_url}'>#{image_tag author.the_avatar, class: 'media-object',
26
- style: 'width: 64px; height: 64px;'}</a>
27
- </div>
28
- <div class='media-body'>
29
- <h4 class='media-heading'>#{author.the_name} <small>#{comment.the_created_at}</small> <span class='label label-#{labels[comment.approved]} pull-right'>#{t("camaleon_cms.admin.comments.message.#{comment.approved}")}</span></h4>
30
- <div class='comment_content'>#{sanitize comment.content}</div>
31
- <div class='comment_actions'>
32
- <div class='pull-left'>
33
- <a href='#{cama_admin_post_comment_answer_path(@post.id,
34
- comment.id)}' data-comment-id='#{comment.id}' title='#{t('camaleon_cms.admin.comments.tooltip.reply_comment')}' class='btn btn-info reply btn-xs ajax_modal'><span class='fa fa-mail-reply'></span></a>
35
- #{link_to raw('<i class="fa fa-trash-o"></i>'), { action: :destroy, id: comment.id },
36
- method: :delete, data: { confirm: t('camaleon_cms.admin.message.delete') }, class: 'btn btn-danger btn-xs cama_ajax_request', title: t('camaleon_cms.admin.comments.tooltip.delete_comment').to_s}
37
- </div>
38
- <div class='pull-right'>
39
- <a href='#{url_for({ action: :toggle_status, comment_id: comment.id,
40
- s: 'a' })}' title='#{t('camaleon_cms.admin.comments.tooltip.approved_comment')}' class='#{if comment.approved == 'approved'
41
- 'hidden'
42
- end} btn btn-success approve btn-xs cama_ajax_request'><span class='fa fa-thumbs-o-up'></span></a>
43
- <a href='#{url_for({ action: :toggle_status, comment_id: comment.id,
44
- s: 'p' })}' title='#{t('camaleon_cms.admin.comments.tooltip.comment_pending')}' class='#{if comment.approved == 'pending'
45
- 'hidden'
46
- end} btn btn-primary pending btn-xs cama_ajax_request'><span class='fa fa-warning'></span></a>
47
- <a href='#{url_for({ action: :toggle_status, comment_id: comment.id,
48
- s: 's' })}' title='#{t('camaleon_cms.admin.comments.tooltip.comment_spam')}' class='#{if comment.approved == 'spam'
49
- 'hidden'
50
- end} btn btn-danger spam btn-xs cama_ajax_request'><span class='fa fa-bug'></span></a>
51
- </div>
52
- </div>
53
- <hr>
54
- <div class='clearfix'></div>
55
- #{cama_comments_render_html comment.children}
56
- </div>
57
- </div>"
58
- end
59
- res
23
+ content_tag(:div, class: 'media') do
24
+ [
25
+ content_tag(:div, class: 'media-left') do
26
+ link_to(author.the_admin_profile_url) do
27
+ image_tag(author.the_avatar, class: 'media-object', style: 'width: 64px; height: 64px;')
28
+ end
29
+ end,
30
+ content_tag(:div, class: 'media-body') do
31
+ [
32
+ content_tag(:h4, class: 'media-heading') do
33
+ [
34
+ author.the_name,
35
+ ' ',
36
+ content_tag(:small, comment.the_created_at),
37
+ ' ',
38
+ content_tag(:span, t("camaleon_cms.admin.comments.message.#{comment.approved}"),
39
+ class: "label label-#{LABELS[comment.approved]} pull-right")
40
+ ].join.html_safe
41
+ end,
42
+ content_tag(:div, sanitize(comment.content), class: 'comment_content'),
43
+ content_tag(:div, class: 'comment_actions') do
44
+ [
45
+ content_tag(:div, class: 'pull-left') do
46
+ [
47
+ link_to(
48
+ cama_admin_post_comment_answer_path(@post.id, comment.id),
49
+ data: { comment_id: comment.id },
50
+ title: t('camaleon_cms.admin.comments.tooltip.reply_comment'),
51
+ class: 'btn btn-info reply btn-xs ajax_modal'
52
+ ) { content_tag(:span, '', class: 'fa fa-mail-reply') },
53
+ ' ',
54
+ link_to(
55
+ { action: :destroy, id: comment.id },
56
+ method: :delete,
57
+ data: { confirm: t('camaleon_cms.admin.message.delete') },
58
+ class: 'btn btn-danger btn-xs cama_ajax_request',
59
+ title: t('camaleon_cms.admin.comments.tooltip.delete_comment')
60
+ ) { content_tag(:i, '', class: 'fa fa-trash-o') }
61
+ ].join.html_safe
62
+ end,
63
+ content_tag(:div, class: 'pull-right') do
64
+ [
65
+ link_to(
66
+ url_for({ action: :toggle_status, comment_id: comment.id, s: 'a' }),
67
+ title: t('camaleon_cms.admin.comments.tooltip.approved_comment'),
68
+ class: "#{comment.approved == 'approved' ? 'hidden' : ''} btn btn-success approve btn-xs cama_ajax_request"
69
+ ) { content_tag(:span, '', class: 'fa fa-thumbs-o-up') },
70
+ ' ',
71
+ link_to(
72
+ url_for({ action: :toggle_status, comment_id: comment.id, s: 'p' }),
73
+ title: t('camaleon_cms.admin.comments.tooltip.comment_pending'),
74
+ class: "#{comment.approved == 'pending' ? 'hidden' : ''} btn btn-primary pending btn-xs cama_ajax_request"
75
+ ) { content_tag(:span, '', class: 'fa fa-warning') },
76
+ ' ',
77
+ link_to(
78
+ url_for({ action: :toggle_status, comment_id: comment.id, s: 's' }),
79
+ title: t('camaleon_cms.admin.comments.tooltip.comment_spam'),
80
+ class: "#{comment.approved == 'spam' ? 'hidden' : ''} btn btn-danger spam btn-xs cama_ajax_request"
81
+ ) { content_tag(:span, '', class: 'fa fa-bug') }
82
+ ].join.html_safe
83
+ end
84
+ ].join.html_safe
85
+ end,
86
+ content_tag(:hr),
87
+ content_tag(:div, '', class: 'clearfix'),
88
+ cama_comments_render_html(comment.children)
89
+ ].join.html_safe
90
+ end
91
+ ].join.html_safe
92
+ end
93
+ end.join('').html_safe
60
94
  end
61
95
  end
62
96
  end
@@ -68,7 +68,7 @@ module CamaleonCms
68
68
  # the_content
69
69
  # end
70
70
  def the_content
71
- @object.the_content.html_safe if @object.present?
71
+ sanitize(@object.the_content) if @object.present?
72
72
  end
73
73
 
74
74
  # select url of post
@@ -23,7 +23,7 @@ module CamaleonCms
23
23
  item_class_parent: 'dropdown', # class for all menu items that contain sub items
24
24
  sub_container: 'ul', # type of container for sub items
25
25
  sub_class: 'dropdown-menu', # class for sub container
26
- callback_item: ->(args) {}, # rubocop:disable Lint/ShadowingOuterLocalVariable
26
+ callback_item: ->(args) {},
27
27
  # callback executed for each item (args = { menu_item, link, level, settings, has_children, link_attrs = "", index}).
28
28
  # menu_item: (Object) Menu object
29
29
  # link: (Hash) link data: {link: '', name: ''}
@@ -140,7 +140,21 @@ module CamaleonCms
140
140
 
141
141
  # execute translation for value if this value is like: t(admin.my_text) ==> My Text
142
142
  def cama_print_i18n_value(value)
143
- value.start_with?('t(') ? eval(value.sub('t(', 'I18n.t(')) : value
143
+ return value unless value.is_a?(String)
144
+ return value unless value.start_with?('t(') && value.end_with?(')')
145
+
146
+ # Use an exclusive end index to strip the trailing ')' without nil-coercion.
147
+ key = value[2...-1].strip
148
+ # If the expression uses matching single/double quotes, unwrap the key before translation;
149
+ # the quoted form still only accepts simple i18n key characters: a-z, A-Z, 0-9, _, ., and -.
150
+ quoted_key_match = key.match(/\A(['"])([a-zA-Z0-9_.-]+)\1\z/)
151
+ key = quoted_key_match[2] if quoted_key_match
152
+
153
+ # Only translate simple i18n keys so arbitrary Ruby is never evaluated.
154
+ # Allowed chars: a-z, A-Z, 0-9, _, ., and -.
155
+ return value unless key.match?(/\A[a-zA-Z0-9_.-]+\z/)
156
+
157
+ I18n.t(key)
144
158
  end
145
159
  end
146
160
  end
@@ -26,7 +26,7 @@ module CamaleonCms
26
26
  if redirect_url.present?
27
27
  redirect_to redirect_url
28
28
  elsif (return_to = cookies.delete(:return_to)).present?
29
- redirect_to return_to
29
+ redirect_to safe_redirect_url(return_to) || cama_admin_dashboard_path
30
30
  else
31
31
  redirect_to cama_admin_dashboard_path
32
32
  end
@@ -167,6 +167,18 @@ module CamaleonCms
167
167
 
168
168
  private
169
169
 
170
+ # validate redirect url to prevent open redirect attacks
171
+ def safe_redirect_url(url)
172
+ return if url.blank?
173
+
174
+ uri = URI.parse(url)
175
+ return if uri.host.present? && uri.host != request.host
176
+
177
+ url
178
+ rescue URI::InvalidURIError
179
+ nil
180
+ end
181
+
170
182
  # calculate the current user for API
171
183
  def cama_calc_api_current_user
172
184
  begin