camaleon_cms 2.9.1 → 2.9.3
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.
- checksums.yaml +4 -4
- data/README.md +29 -9
- data/Rakefile +3 -16
- data/app/apps/plugins/attack/admin_controller.rb +9 -6
- data/app/apps/plugins/attack/attack_helper.rb +35 -25
- data/app/apps/plugins/authoring_post/authoring_post_helper.rb +23 -16
- data/app/apps/plugins/front_cache/admin_controller.rb +16 -10
- data/app/apps/plugins/front_cache/config/initializer.rb +1 -1
- data/app/apps/plugins/front_cache/front_cache_helper.rb +57 -24
- data/app/apps/plugins/visibility_post/config/config.json +1 -0
- data/app/apps/plugins/visibility_post/config/routes_front.txt +1 -0
- data/app/apps/plugins/visibility_post/front_controller.rb +23 -0
- data/app/apps/plugins/visibility_post/visibility_post_helper.rb +141 -70
- data/app/apps/themes/camaleon_first/main_helper.rb +18 -12
- data/app/apps/themes/default/assets/css/style.css.scss +24 -0
- data/app/apps/themes/default/assets/genericons/genericons.css +3 -8
- data/app/apps/themes/default/default_helper.rb +2 -1
- data/app/apps/themes/default/views/category.html.erb +1 -1
- data/app/apps/themes/default/views/layouts/index.html.erb +3 -1
- data/app/apps/themes/default/views/post_tag.html.erb +1 -1
- data/app/apps/themes/default/views/post_type.html.erb +1 -1
- data/app/apps/themes/default/views/search.html.erb +1 -1
- data/app/apps/themes/new/assets/css/styles.css.scss +24 -0
- data/app/apps/themes/new/custom_helper.rb +5 -9
- data/app/apps/themes/new/views/category.html.erb +1 -1
- data/app/apps/themes/new/views/layouts/_header.html.erb +3 -1
- data/app/apps/themes/new/views/post_tag.html.erb +1 -1
- data/app/apps/themes/new/views/post_type.html.erb +1 -1
- data/app/apps/themes/new/views/search.html.erb +1 -1
- data/app/assets/javascripts/camaleon_cms/admin/_data.js +16 -0
- data/app/assets/javascripts/camaleon_cms/admin/_post.js +5 -2
- data/app/assets/javascripts/camaleon_cms/admin/nav_menu.js +14 -3
- data/app/assets/javascripts/camaleon_cms/admin/uploader/_media_manager.js +5 -0
- data/app/controllers/camaleon_cms/admin/appearances/nav_menus_controller.rb +29 -10
- data/app/controllers/camaleon_cms/admin/appearances/themes_controller.rb +8 -5
- data/app/controllers/camaleon_cms/admin/appearances/widgets/assign_controller.rb +9 -2
- data/app/controllers/camaleon_cms/admin/appearances/widgets/main_controller.rb +4 -3
- data/app/controllers/camaleon_cms/admin/appearances/widgets/sidebar_controller.rb +2 -2
- data/app/controllers/camaleon_cms/admin/categories_controller.rb +27 -8
- data/app/controllers/camaleon_cms/admin/comments_controller.rb +0 -1
- data/app/controllers/camaleon_cms/admin/installers_controller.rb +32 -1
- data/app/controllers/camaleon_cms/admin/media_controller.rb +84 -19
- data/app/controllers/camaleon_cms/admin/plugins_controller.rb +1 -3
- data/app/controllers/camaleon_cms/admin/post_tags_controller.rb +15 -6
- data/app/controllers/camaleon_cms/admin/posts/drafts_controller.rb +40 -11
- data/app/controllers/camaleon_cms/admin/posts_controller.rb +58 -22
- data/app/controllers/camaleon_cms/admin/sessions_controller.rb +238 -45
- data/app/controllers/camaleon_cms/admin/settings/custom_fields_controller.rb +103 -18
- data/app/controllers/camaleon_cms/admin/settings/post_types_controller.rb +17 -5
- data/app/controllers/camaleon_cms/admin/settings/sites_controller.rb +33 -7
- data/app/controllers/camaleon_cms/admin/settings_controller.rb +11 -6
- data/app/controllers/camaleon_cms/admin/user_roles_controller.rb +9 -7
- data/app/controllers/camaleon_cms/admin/users_controller.rb +89 -18
- data/app/controllers/camaleon_cms/admin_controller.rb +144 -26
- data/app/controllers/camaleon_cms/apps/plugins_admin_controller.rb +5 -0
- data/app/controllers/camaleon_cms/camaleon_controller.rb +37 -92
- data/app/controllers/camaleon_cms/frontend_controller.rb +140 -70
- data/app/controllers/concerns/camaleon_cms/admin/custom_fields_concern.rb +44 -0
- data/app/controllers/concerns/camaleon_cms/frontend_concern.rb +46 -17
- data/app/controllers/concerns/camaleon_cms/frontend_visited_state_concern.rb +72 -0
- data/app/controllers/concerns/camaleon_cms/hook_lifecycle_concern.rb +104 -0
- data/app/controllers/concerns/camaleon_cms/request_context_concern.rb +113 -0
- data/app/controllers/concerns/camaleon_cms/runtime_admin_menu_concern.rb +237 -0
- data/app/controllers/concerns/camaleon_cms/runtime_captcha_image_concern.rb +13 -0
- data/app/controllers/concerns/camaleon_cms/runtime_html_content_concern.rb +143 -0
- data/app/controllers/concerns/camaleon_cms/runtime_shortcode_theme_concern.rb +246 -0
- data/app/controllers/concerns/camaleon_cms/runtime_state_concern.rb +13 -0
- data/app/controllers/concerns/camaleon_cms/runtime_uploader_concern.rb +54 -0
- data/app/controllers/concerns/camaleon_cms/session_captcha_runtime_concern.rb +19 -0
- data/app/controllers/concerns/camaleon_cms/session_runtime_concern.rb +40 -0
- data/app/decorators/camaleon_cms/application_decorator.rb +11 -8
- data/app/decorators/camaleon_cms/category_decorator.rb +3 -2
- data/app/decorators/camaleon_cms/post_comment_decorator.rb +13 -5
- data/app/decorators/camaleon_cms/post_decorator.rb +67 -51
- data/app/decorators/camaleon_cms/post_type_decorator.rb +1 -1
- data/app/decorators/camaleon_cms/site_decorator.rb +29 -23
- data/app/decorators/camaleon_cms/term_taxonomy_decorator.rb +21 -10
- data/app/decorators/camaleon_cms/theme_decorator.rb +3 -3
- data/app/decorators/camaleon_cms/user_decorator.rb +26 -5
- data/app/helpers/camaleon_cms/admin/application_helper.rb +21 -24
- data/app/helpers/camaleon_cms/admin/breadcrumb_helper.rb +1 -1
- data/app/helpers/camaleon_cms/admin/custom_fields_helper.rb +24 -23
- data/app/helpers/camaleon_cms/admin/menus_helper.rb +249 -139
- data/app/helpers/camaleon_cms/admin/post_type_helper.rb +51 -31
- data/app/helpers/camaleon_cms/camaleon_helper.rb +31 -39
- data/app/helpers/camaleon_cms/captcha_helper.rb +73 -53
- data/app/helpers/camaleon_cms/comment_helper.rb +87 -41
- data/app/helpers/camaleon_cms/content_helper.rb +13 -6
- data/app/helpers/camaleon_cms/email_helper.rb +7 -3
- data/app/helpers/camaleon_cms/frontend/content_select_helper.rb +34 -20
- data/app/helpers/camaleon_cms/frontend/nav_menu_helper.rb +144 -67
- data/app/helpers/camaleon_cms/frontend/seo_helper.rb +39 -28
- data/app/helpers/camaleon_cms/frontend/site_helper.rb +32 -12
- data/app/helpers/camaleon_cms/hooks_helper.rb +14 -4
- data/app/helpers/camaleon_cms/html_helper.rb +73 -38
- data/app/helpers/camaleon_cms/plugins_helper.rb +23 -24
- data/app/helpers/camaleon_cms/session_helper.rb +195 -39
- data/app/helpers/camaleon_cms/short_code_helper.rb +181 -85
- data/app/helpers/camaleon_cms/site_helper.rb +43 -9
- data/app/helpers/camaleon_cms/theme_helper.rb +35 -19
- data/app/helpers/camaleon_cms/uploader_helper.rb +19 -422
- data/app/helpers/camaleon_cms/user_roles_helper.rb +5 -4
- data/app/mailers/camaleon_cms/html_mailer.rb +4 -4
- data/app/models/camaleon_cms/ability.rb +65 -105
- data/app/models/camaleon_cms/category.rb +7 -5
- data/app/models/camaleon_cms/custom_field.rb +29 -6
- data/app/models/camaleon_cms/custom_field_group.rb +75 -21
- data/app/models/camaleon_cms/custom_fields_relationship.rb +133 -2
- data/app/models/camaleon_cms/media.rb +8 -2
- data/app/models/camaleon_cms/meta.rb +2 -2
- data/app/models/camaleon_cms/nav_menu.rb +6 -2
- data/app/models/camaleon_cms/nav_menu_item.rb +18 -8
- data/app/models/camaleon_cms/plugin.rb +4 -3
- data/app/models/camaleon_cms/post.rb +113 -27
- data/app/models/camaleon_cms/post_comment.rb +10 -6
- data/app/models/camaleon_cms/post_default.rb +42 -12
- data/app/models/camaleon_cms/post_relationship.rb +6 -4
- data/app/models/camaleon_cms/post_tag.rb +4 -3
- data/app/models/camaleon_cms/post_type.rb +22 -12
- data/app/models/camaleon_cms/site.rb +40 -28
- data/app/models/camaleon_cms/term_relationship.rb +10 -7
- data/app/models/camaleon_cms/term_taxonomy.rb +51 -5
- data/app/models/camaleon_cms/theme.rb +3 -3
- data/app/models/camaleon_cms/user.rb +17 -5
- data/app/models/camaleon_cms/user_role.rb +77 -4
- data/app/models/camaleon_cms/widget/assigned.rb +11 -6
- data/app/models/camaleon_cms/widget/main.rb +13 -7
- data/app/models/camaleon_cms/widget/sidebar.rb +3 -6
- data/app/models/camaleon_record.rb +123 -5
- data/app/models/concerns/camaleon_cms/categories_tags_for_posts.rb +20 -38
- data/app/models/concerns/camaleon_cms/common_relationships.rb +18 -5
- data/app/models/concerns/camaleon_cms/custom_fields_read.rb +115 -58
- data/app/models/concerns/camaleon_cms/metas.rb +32 -7
- data/app/models/concerns/camaleon_cms/normalize_attrs.rb +2 -8
- data/app/models/concerns/camaleon_cms/site_default_settings.rb +120 -16
- data/app/models/concerns/camaleon_cms/user_methods.rb +44 -19
- data/app/models/current_request.rb +23 -0
- data/app/uploaders/camaleon_cms_aws_uploader.rb +39 -11
- data/app/uploaders/camaleon_cms_local_uploader.rb +76 -19
- data/app/uploaders/camaleon_cms_uploader.rb +23 -10
- data/app/validators/camaleon_cms/post_uniq_validator.rb +21 -8
- data/app/validators/camaleon_cms/uniq_validator.rb +1 -1
- data/app/validators/camaleon_cms/user_url_validator.rb +215 -36
- data/app/views/camaleon_cms/admin/appearances/nav_menus/_left_menu_items.html.erb +2 -2
- data/app/views/camaleon_cms/admin/appearances/widgets/main/form.html.erb +1 -1
- data/app/views/camaleon_cms/admin/categories/index.html.erb +3 -3
- data/app/views/camaleon_cms/admin/comments/index.html.erb +2 -2
- data/app/views/camaleon_cms/admin/comments/list.html.erb +1 -1
- data/app/views/camaleon_cms/admin/installers/form.html.erb +5 -0
- data/app/views/camaleon_cms/admin/installers/welcome.html.erb +30 -6
- data/app/views/camaleon_cms/admin/plugins/_plugins_list.html.erb +2 -2
- data/app/views/camaleon_cms/admin/post_tags/index.html.erb +3 -3
- data/app/views/camaleon_cms/admin/posts/_sidebar.html.erb +2 -2
- data/app/views/camaleon_cms/admin/posts/form.html.erb +3 -2
- data/app/views/camaleon_cms/admin/posts/index.html.erb +6 -6
- data/app/views/camaleon_cms/admin/search.html.erb +1 -1
- data/app/views/camaleon_cms/admin/sessions/back_to_parent.html.erb +39 -0
- data/app/views/camaleon_cms/admin/sessions/logout_confirm.html.erb +27 -0
- data/app/views/camaleon_cms/admin/settings/_email_settings.html.erb +4 -1
- data/app/views/camaleon_cms/admin/settings/custom_fields/_get_items.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/custom_fields/_render.html.erb +42 -6
- data/app/views/camaleon_cms/admin/settings/custom_fields/_render_category_simple.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/custom_fields/fields/_select_eval.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/custom_fields/form.html.erb +5 -4
- data/app/views/camaleon_cms/admin/settings/custom_fields/index.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/post_types/index.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/shortcodes.html.erb +4 -4
- data/app/views/camaleon_cms/admin/settings/site.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/sites/form.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/sites/index.html.erb +1 -1
- data/app/views/camaleon_cms/admin/user_roles/form.html.erb +102 -7
- data/app/views/camaleon_cms/admin/user_roles/index.html.erb +1 -1
- data/app/views/camaleon_cms/admin/users/form.html.erb +8 -4
- data/app/views/camaleon_cms/admin/users/index.html.erb +2 -2
- data/app/views/camaleon_cms/default_theme/partials/_render_custom_field.html.erb +8 -2
- data/app/views/camaleon_cms/default_theme/sitemap.html.erb +1 -1
- data/app/views/camaleon_cms/default_theme/sitemap.xml.builder +1 -1
- data/app/views/layouts/camaleon_cms/admin/_flash_messages.html.erb +2 -2
- data/app/views/layouts/camaleon_cms/admin/_header.html.erb +1 -1
- data/config/initializers/action_view.rb +42 -5
- data/config/initializers/active_record_extension.rb +13 -27
- data/config/initializers/assets.rb +52 -23
- data/config/initializers/custom_initializers.rb +3 -3
- data/config/locales/camaleon_cms/admin/ar.yml +7 -2
- data/config/locales/camaleon_cms/admin/de.yml +7 -2
- data/config/locales/camaleon_cms/admin/en.yml +39 -2
- data/config/locales/camaleon_cms/admin/es.yml +7 -2
- data/config/locales/camaleon_cms/admin/fr.yml +7 -2
- data/config/locales/camaleon_cms/admin/it.yml +7 -2
- data/config/locales/camaleon_cms/admin/nl.yml +8 -2
- data/config/locales/camaleon_cms/admin/pt-BR.yml +7 -2
- data/config/locales/camaleon_cms/admin/pt.yml +7 -2
- data/config/locales/camaleon_cms/admin/ru.yml +7 -2
- data/config/locales/camaleon_cms/admin/uk.yml +7 -2
- data/config/locales/camaleon_cms/admin/zh-CH.yml +7 -2
- data/config/routes/admin.rb +33 -15
- data/config/routes/frontend.rb +21 -15
- data/db/migrate/20150611161134_post_table_into_utf8.rb +14 -14
- data/db/migrate/20150926095310_rename_column_posts.rb +3 -3
- data/db/migrate/20151212095328_add_confirm_token_to_users.rb +3 -3
- data/db/migrate/20160504155652_add_feature_to_posts.rb +1 -1
- data/db/migrate/20160504155653_move_first_name_of_users.rb +2 -2
- data/db/migrate/20160609121449_add_group_to_custom_field_values.rb +1 -1
- data/db/migrate/20161215202255_drop_user_relationship_table.rb +1 -1
- data/db/migrate/20180124132318_create_media.rb +1 -1
- data/db/migrate/20180704211100_adjust_field_length.rb +1 -1
- data/lib/camaleon_cms/assets_precompile.rb +86 -0
- data/lib/camaleon_cms/captcha_image_generation.rb +65 -0
- data/lib/camaleon_cms/content_security.rb +158 -0
- data/lib/camaleon_cms/engine.rb +47 -9
- data/lib/camaleon_cms/media_security_headers.rb +27 -0
- data/lib/camaleon_cms/setup_token.rb +69 -0
- data/lib/camaleon_cms/svg_content_checker.rb +123 -0
- data/lib/camaleon_cms/task_reporter.rb +14 -0
- data/lib/camaleon_cms/unsafe_markup.rb +207 -0
- data/lib/camaleon_cms/uploader_content_security.rb +248 -0
- data/lib/camaleon_cms/uploader_image_processing.rb +166 -0
- data/lib/camaleon_cms/uploader_path_security.rb +146 -0
- data/lib/camaleon_cms/uploader_pipeline.rb +350 -0
- data/lib/camaleon_cms/uploader_support.rb +86 -0
- data/lib/camaleon_cms/version.rb +1 -1
- data/lib/camaleon_cms.rb +11 -0
- data/lib/ext/hash.rb +28 -2
- data/lib/ext/string.rb +13 -8
- data/lib/ext/translator.rb +10 -2
- data/lib/generators/camaleon_cms/gem_plugin_template/app/controllers/plugins/my_plugin/admin_controller.rb +5 -1
- data/lib/generators/camaleon_cms/install_generator.rb +7 -4
- data/lib/generators/camaleon_cms/install_template/plugin_routes.rb +5 -12
- data/lib/generators/camaleon_cms/theme_generator.rb +5 -10
- data/lib/generators/camaleon_cms/theme_template/views/layouts/index.html.erb +3 -1
- data/lib/plugin_routes.rb +412 -368
- data/lib/tasks/camaleon_cms/camaleon_tasks.rake +2 -1
- data/lib/tasks/cross_site_field_groups.rake +74 -0
- data/lib/tasks/custom_fields_roles.rake +58 -0
- data/lib/tasks/orphaned_comments.rake +42 -0
- data/lib/tasks/private_upload_acls.rake +31 -0
- data/lib/tasks/site_custom_field_groups.rake +36 -0
- data/lib/tasks/unsafe_stored_content.rake +42 -0
- data/lib/tasks/unsafe_stored_uploads.rake +70 -0
- data/lib/tasks/user_field_groups.rake +27 -0
- metadata +185 -7
|
@@ -236,10 +236,13 @@ function cama_init_post(obj) {
|
|
|
236
236
|
return false;
|
|
237
237
|
});
|
|
238
238
|
|
|
239
|
-
/* Disabled until fix to reload only category fields and not all
|
|
239
|
+
/* Disabled until fix to reload only category fields and not all.
|
|
240
|
+
NOTE (M6): custom_fields#list performs its category write only on a CSRF-verified POST, so
|
|
241
|
+
this must stay $.post when re-enabled (jquery_ujs attaches the CSRF token) — with $.get the
|
|
242
|
+
fields would still render but the post's categories would silently not be saved.
|
|
240
243
|
$form.on("change", ".list-categories input", function () {
|
|
241
244
|
showLoading();
|
|
242
|
-
$.
|
|
245
|
+
$.post(
|
|
243
246
|
$form.find("#post_add_new_category").data('fields-reload-url'), {
|
|
244
247
|
categories: $form.find("#post_right_bar .list-categories input[name='categories[]']:checked").map(function(i, el){ return $(this).val(); }).get(),
|
|
245
248
|
post_id: post_id
|
|
@@ -111,9 +111,20 @@ $(function() {
|
|
|
111
111
|
return false
|
|
112
112
|
|
|
113
113
|
showLoading()
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
// Security (audit M6): the route is DELETE-only. jquery_ujs' ajax prefilter attaches the
|
|
115
|
+
// X-CSRF-Token header to every same-origin request (it gates on crossDomain, not the verb), so
|
|
116
|
+
// this DELETE carries the token.
|
|
117
|
+
$.ajax({
|
|
118
|
+
url: link.attr('href'),
|
|
119
|
+
type: 'DELETE',
|
|
120
|
+
success() {
|
|
121
|
+
link.closest('.dd-item').remove()
|
|
122
|
+
},
|
|
123
|
+
// Hide the overlay on every outcome -- a failed DELETE (e.g. a stale CSRF token after the
|
|
124
|
+
// session rotated) would otherwise leave the full-screen spinner blocking the admin.
|
|
125
|
+
complete() {
|
|
126
|
+
hideLoading()
|
|
127
|
+
}
|
|
117
128
|
})
|
|
118
129
|
return false
|
|
119
130
|
})
|
|
@@ -143,6 +143,11 @@ window.cama_init_media = function(mediaPanel) {
|
|
|
143
143
|
const customFileData = function() {
|
|
144
144
|
const r = window.cama_media_get_custom_params()
|
|
145
145
|
r.skip_auto_crop = true
|
|
146
|
+
// Security (audit M7): the uploadFile plugin posts multipart data outside the UJS ajax
|
|
147
|
+
// prefilter, so the CSRF token must ride as a form field (media#upload no longer skips it).
|
|
148
|
+
// Read the meta tag directly so this does not depend on any UJS library being loaded.
|
|
149
|
+
const csrfMeta = document.querySelector('meta[name="csrf-token"]')
|
|
150
|
+
r.authenticity_token = csrfMeta && csrfMeta.getAttribute('content')
|
|
146
151
|
return r
|
|
147
152
|
}
|
|
148
153
|
|
|
@@ -2,7 +2,7 @@ module CamaleonCms
|
|
|
2
2
|
module Admin
|
|
3
3
|
module Appearances
|
|
4
4
|
class NavMenusController < CamaleonCms::AdminController
|
|
5
|
-
include CamaleonCms::
|
|
5
|
+
include CamaleonCms::Admin::CustomFieldsConcern
|
|
6
6
|
|
|
7
7
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.appearance')
|
|
8
8
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.menus')
|
|
@@ -10,9 +10,9 @@ module CamaleonCms
|
|
|
10
10
|
|
|
11
11
|
def index
|
|
12
12
|
@nav_menu = if params[:id].present?
|
|
13
|
-
current_site.nav_menus.
|
|
13
|
+
current_site.nav_menus.find_by(id: params[:id])
|
|
14
14
|
elsif params[:slug].present?
|
|
15
|
-
current_site.nav_menus.find_by_slug(params[:slug])
|
|
15
|
+
current_site.nav_menus.find_by_slug(params[:slug]) # rubocop:disable Rails/DynamicFindBy
|
|
16
16
|
else
|
|
17
17
|
current_site.nav_menus.first
|
|
18
18
|
end
|
|
@@ -27,7 +27,7 @@ module CamaleonCms
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def create
|
|
30
|
-
nav_menu = current_site.nav_menus.new(
|
|
30
|
+
nav_menu = current_site.nav_menus.new(nav_menu_params)
|
|
31
31
|
nav_menu.save
|
|
32
32
|
flash[:notice] = t('.created_menu', default: 'Created Menu')
|
|
33
33
|
redirect_to action: :index, id: nav_menu.id
|
|
@@ -35,7 +35,7 @@ module CamaleonCms
|
|
|
35
35
|
|
|
36
36
|
def update
|
|
37
37
|
nav_menu = current_site.nav_menus.find(params[:id])
|
|
38
|
-
nav_menu.update(
|
|
38
|
+
nav_menu.update(nav_menu_params)
|
|
39
39
|
flash[:notice] = t('.updated_menu', default: 'Menu updated')
|
|
40
40
|
redirect_to action: :index, id: nav_menu.id
|
|
41
41
|
end
|
|
@@ -59,7 +59,7 @@ module CamaleonCms
|
|
|
59
59
|
|
|
60
60
|
def save_custom_settings
|
|
61
61
|
@nav_menu_item = current_site.nav_menu_items.find(params[:id])
|
|
62
|
-
@nav_menu_item.set_field_values(
|
|
62
|
+
@nav_menu_item.set_field_values(cama_permitted_field_options('NavMenuItem'))
|
|
63
63
|
head :ok
|
|
64
64
|
end
|
|
65
65
|
|
|
@@ -77,7 +77,7 @@ module CamaleonCms
|
|
|
77
77
|
@nav_menu = current_site.nav_menus.find(params[:nav_menu_id])
|
|
78
78
|
item = current_site.nav_menu_items.find(params[:id])
|
|
79
79
|
item.update_menu_item(parse_external_menu(params))
|
|
80
|
-
item.set_options(
|
|
80
|
+
item.set_options(permitted_external_options) if params[:options].present?
|
|
81
81
|
render partial: 'menu_items', locals: { items: [item], nav_menu: @nav_menu }
|
|
82
82
|
end
|
|
83
83
|
|
|
@@ -90,7 +90,10 @@ module CamaleonCms
|
|
|
90
90
|
# update the reorder of items
|
|
91
91
|
def reorder_items(items = nil, parent_id = nil, is_root = true)
|
|
92
92
|
items = params[:items] if items.nil?
|
|
93
|
-
|
|
93
|
+
# Security (audit M10): resolve the destination menu through the current site (like
|
|
94
|
+
# #add_items) -- a raw params[:nav_menu_id] let an item be re-homed under another site's
|
|
95
|
+
# nav menu. Nested calls pass an explicit parent item id, so only the root needs scoping.
|
|
96
|
+
parent_id = current_site.nav_menus.find(params[:nav_menu_id]).id if parent_id.nil?
|
|
94
97
|
items.each do |index, _item|
|
|
95
98
|
item = current_site.nav_menu_items.find(_item['id'])
|
|
96
99
|
item.update(parent_id: parent_id, term_order: index)
|
|
@@ -106,13 +109,13 @@ module CamaleonCms
|
|
|
106
109
|
external_params = params[:external]
|
|
107
110
|
if external_params.present?
|
|
108
111
|
external_item = @nav_menu.append_menu_item(parse_external_menu(external_params))
|
|
109
|
-
external_item.set_options(external_params
|
|
112
|
+
external_item.set_options(permitted_external_options(external_params)) if external_params[:options].present?
|
|
110
113
|
items << external_item
|
|
111
114
|
end
|
|
112
115
|
|
|
113
116
|
if params[:custom_items].present? # custom menu items
|
|
114
117
|
params[:custom_items].each_value do |custom_item|
|
|
115
|
-
type = custom_item['kind'].
|
|
118
|
+
type = custom_item['kind'].presence || 'external'
|
|
116
119
|
items << @nav_menu.append_menu_item({ label: custom_item['label'], link: custom_item['url'], type: type })
|
|
117
120
|
end
|
|
118
121
|
end
|
|
@@ -136,6 +139,22 @@ module CamaleonCms
|
|
|
136
139
|
def parse_external_menu(_params)
|
|
137
140
|
{ label: _params[:external_label], link: _params[:external_url], type: 'external', target: _params[:target] }
|
|
138
141
|
end
|
|
142
|
+
|
|
143
|
+
# Strong parameters for nav_menu
|
|
144
|
+
def nav_menu_params
|
|
145
|
+
params.require(:nav_menu).permit(:name, :slug)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Only permit external menu options that match registered custom field slug
|
|
149
|
+
def permitted_external_options(external_params = nil)
|
|
150
|
+
opts = external_params ? external_params[:options] : params[:options]
|
|
151
|
+
return {} if opts.blank?
|
|
152
|
+
|
|
153
|
+
allowed_keys = cama_custom_field_allowed_slugs('NavMenuItem')
|
|
154
|
+
return {} if allowed_keys.blank?
|
|
155
|
+
|
|
156
|
+
opts.permit(*allowed_keys).to_h
|
|
157
|
+
end
|
|
139
158
|
end
|
|
140
159
|
end
|
|
141
160
|
end
|
|
@@ -7,9 +7,8 @@ module CamaleonCms
|
|
|
7
7
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.appearance')
|
|
8
8
|
def index
|
|
9
9
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.themes')
|
|
10
|
-
PluginRoutes.reload
|
|
11
10
|
authorize! :manage, :themes
|
|
12
|
-
return
|
|
11
|
+
return if params[:set].blank?
|
|
13
12
|
|
|
14
13
|
site_install_theme(params[:set])
|
|
15
14
|
flash.now[:notice] = t('camaleon_cms.admin.themes.message.updated')
|
|
@@ -18,9 +17,13 @@ module CamaleonCms
|
|
|
18
17
|
|
|
19
18
|
def load_data
|
|
20
19
|
file = Rails.root.join('app', 'apps', 'themes', current_site.get_theme_slug, 'data.json')
|
|
21
|
-
@messages = load_file_content_to_db(
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
@messages = load_file_content_to_db(
|
|
21
|
+
file,
|
|
22
|
+
{
|
|
23
|
+
post_types: 1, clear_post_type: 1, nav_menus: 1, clear_nav_menus: 1, slider_basic: 1,
|
|
24
|
+
clear_slider_basic: 1, theme_import: 1
|
|
25
|
+
}
|
|
26
|
+
)
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
def preview
|
|
@@ -3,6 +3,8 @@ module CamaleonCms
|
|
|
3
3
|
module Appearances
|
|
4
4
|
module Widgets
|
|
5
5
|
class AssignController < CamaleonCms::AdminController
|
|
6
|
+
include CamaleonCms::Admin::CustomFieldsConcern
|
|
7
|
+
|
|
6
8
|
before_action :check_permission_role
|
|
7
9
|
before_action :find_sidebar
|
|
8
10
|
before_action :find_assigned_sidebar, only: %i[update destroy]
|
|
@@ -15,8 +17,13 @@ module CamaleonCms
|
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def update
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
# sidebar_id (post_parent) and widget_id (visibility) identify the tenant-scoped sidebar and
|
|
21
|
+
# widget this assignment belongs to; they are set from @sidebar/@widget on create and must
|
|
22
|
+
# not be reassigned from the request, or the assignment could be re-pointed into another
|
|
23
|
+
# site's sidebar or widget (audit finding H9). Reordering has its own current-site-scoped
|
|
24
|
+
# action (sidebar#reorder).
|
|
25
|
+
if @assigned.update(params.require(:assign).permit(:title, :content, :item_order))
|
|
26
|
+
@assigned.set_field_values(cama_permitted_field_options('Main'))
|
|
20
27
|
flash[:notice] = t('camaleon_cms.admin.widgets.assign.updated')
|
|
21
28
|
else
|
|
22
29
|
flash[:error] = t('camaleon_cms.admin.widgets.assign.error_updated')
|
|
@@ -22,8 +22,9 @@ module CamaleonCms
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def create
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
@widget = current_site
|
|
26
|
+
.widgets.new(params.require(:widget_main).permit(:name, :slug, :description, :excerpt, :renderer))
|
|
27
|
+
@widget.status = 'simple'
|
|
27
28
|
if @widget.save!
|
|
28
29
|
flash[:notice] = t('camaleon_cms.admin.widgets.message.created')
|
|
29
30
|
else
|
|
@@ -33,7 +34,7 @@ module CamaleonCms
|
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def update
|
|
36
|
-
if @widget.update!(params.require(:widget_main).permit
|
|
37
|
+
if @widget.update!(params.require(:widget_main).permit(:name, :slug, :description, :excerpt, :renderer))
|
|
37
38
|
flash[:notice] = t('camaleon_cms.admin.widgets.message.updated')
|
|
38
39
|
else
|
|
39
40
|
flash[:error] = t('camaleon_cms.admin.widgets.message.error_updated')
|
|
@@ -12,7 +12,7 @@ module CamaleonCms
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def create
|
|
15
|
-
@sidebar = current_site.sidebars.new(params.require(:widget_sidebar).permit
|
|
15
|
+
@sidebar = current_site.sidebars.new(params.require(:widget_sidebar).permit(:name, :slug, :description))
|
|
16
16
|
if @sidebar.save
|
|
17
17
|
flash[:notice] = t('camaleon_cms.admin.widgets.sidebar.created')
|
|
18
18
|
else
|
|
@@ -26,7 +26,7 @@ module CamaleonCms
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def update
|
|
29
|
-
if @sidebar.update(params.require(:widget_sidebar).permit
|
|
29
|
+
if @sidebar.update(params.require(:widget_sidebar).permit(:name, :slug, :description))
|
|
30
30
|
flash[:notice] = t('camaleon_cms.admin.widgets.sidebar.updated')
|
|
31
31
|
else
|
|
32
32
|
flash[:error] = t('camaleon_cms.admin.widgets.sidebar.error_updated')
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
module Admin
|
|
3
3
|
class CategoriesController < CamaleonCms::AdminController
|
|
4
|
+
include CamaleonCms::Admin::CustomFieldsConcern
|
|
5
|
+
|
|
4
6
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.contents')
|
|
7
|
+
|
|
5
8
|
before_action :set_post_type
|
|
6
9
|
before_action :set_category, only: %w[show edit update destroy]
|
|
10
|
+
before_action :validate_category_parent, only: %w[create update]
|
|
7
11
|
|
|
8
12
|
def index
|
|
9
13
|
@categories = @post_type.categories
|
|
@@ -20,9 +24,9 @@ module CamaleonCms
|
|
|
20
24
|
def update
|
|
21
25
|
hooks_run('update_category', { category: @category, post_type: @post_type })
|
|
22
26
|
|
|
23
|
-
if @category.update(params.require(:category).permit
|
|
27
|
+
if @category.update(params.require(:category).permit(:name, :slug, :description, :parent_id))
|
|
24
28
|
@category.set_options(params[:meta])
|
|
25
|
-
@category.set_field_values(
|
|
29
|
+
@category.set_field_values(cama_permitted_field_options('PostType_Category'))
|
|
26
30
|
hooks_run('updated_category', { category: @category, post_type: @post_type })
|
|
27
31
|
flash[:notice] = t('camaleon_cms.admin.post_type.message.updated')
|
|
28
32
|
redirect_to action: :index
|
|
@@ -32,12 +36,12 @@ module CamaleonCms
|
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
def create
|
|
35
|
-
@category = @post_type.categories.new(params.require(:category).permit
|
|
39
|
+
@category = @post_type.categories.new(params.require(:category).permit(:name, :slug, :description, :parent_id))
|
|
36
40
|
hooks_run('create_category', { category: @category, post_type: @post_type })
|
|
37
41
|
|
|
38
42
|
if @category.save
|
|
39
43
|
@category.set_options(params[:meta])
|
|
40
|
-
@category.set_field_values(
|
|
44
|
+
@category.set_field_values(cama_permitted_field_options('PostType_Category'))
|
|
41
45
|
hooks_run('created_category', { category: @category, post_type: @post_type })
|
|
42
46
|
flash[:notice] = t('camaleon_cms.admin.post_type.message.created')
|
|
43
47
|
redirect_to action: :index
|
|
@@ -48,8 +52,11 @@ module CamaleonCms
|
|
|
48
52
|
|
|
49
53
|
# return html category list used to reload categories list in post editor form
|
|
50
54
|
def list
|
|
51
|
-
render inline: post_type_html_inputs(
|
|
52
|
-
|
|
55
|
+
render inline: post_type_html_inputs(
|
|
56
|
+
@post_type, 'categories', 'categories',
|
|
57
|
+
@post_type.get_option('has_single_category', false) ? 'radio' : 'checkbox',
|
|
58
|
+
params[:categories] || [], 'categorychecklist', true
|
|
59
|
+
)
|
|
53
60
|
end
|
|
54
61
|
|
|
55
62
|
def destroy
|
|
@@ -59,10 +66,21 @@ module CamaleonCms
|
|
|
59
66
|
|
|
60
67
|
private
|
|
61
68
|
|
|
69
|
+
# The parent dropdown offers the post type itself or one of its own categories. A parent_id naming
|
|
70
|
+
# anything else would re-home the category under another post type or site, so it is rejected
|
|
71
|
+
# (audit finding H8). full_categories is scoped to this post type's site and id.
|
|
72
|
+
def validate_category_parent
|
|
73
|
+
submitted = params.dig(:category, :parent_id)
|
|
74
|
+
return if submitted.blank? || submitted.to_s == @post_type.id.to_s
|
|
75
|
+
return if @post_type.full_categories.exists?(id: submitted)
|
|
76
|
+
|
|
77
|
+
raise CanCan::AccessDenied, t('camaleon_cms.admin.post_type.message.error')
|
|
78
|
+
end
|
|
79
|
+
|
|
62
80
|
# define parent post type
|
|
63
81
|
def set_post_type
|
|
64
82
|
begin
|
|
65
|
-
@post_type = current_site.post_types.
|
|
83
|
+
@post_type = current_site.post_types.find_by(id: params[:post_type_id]).decorate
|
|
66
84
|
rescue StandardError
|
|
67
85
|
flash[:error] = t('camaleon_cms.admin.request_error_message')
|
|
68
86
|
redirect_to cama_admin_path, { error: 'Error Post Type' }
|
|
@@ -73,7 +91,8 @@ module CamaleonCms
|
|
|
73
91
|
end
|
|
74
92
|
|
|
75
93
|
def set_category
|
|
76
|
-
@category =
|
|
94
|
+
@category = @post_type.categories.find_by(id: params[:id])
|
|
95
|
+
render_404 unless @category
|
|
77
96
|
rescue StandardError
|
|
78
97
|
flash[:error] = t('camaleon_cms.admin.post_type.message.error')
|
|
79
98
|
redirect_to cama_admin_path
|
|
@@ -2,7 +2,6 @@ require 'will_paginate/array'
|
|
|
2
2
|
module CamaleonCms
|
|
3
3
|
module Admin
|
|
4
4
|
class CommentsController < CamaleonCms::AdminController
|
|
5
|
-
include CamaleonCms::CommentHelper
|
|
6
5
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.comments'), :cama_admin_comments_url
|
|
7
6
|
before_action :validate_role
|
|
8
7
|
before_action :set_post, except: :list
|
|
@@ -5,9 +5,13 @@ module CamaleonCms
|
|
|
5
5
|
skip_before_action :cama_before_actions
|
|
6
6
|
skip_after_action :cama_after_actions
|
|
7
7
|
before_action :installer_verification, except: :welcome
|
|
8
|
+
before_action :require_setup_token, only: :save
|
|
8
9
|
layout 'camaleon_cms/login'
|
|
9
10
|
|
|
10
11
|
def index
|
|
12
|
+
# Ensure the setup token exists (and is logged) so a remote operator can read it and enter it
|
|
13
|
+
# below. Local installs are exempt (require_setup_token), so no token is generated for them.
|
|
14
|
+
CamaleonCms::SetupToken.value if CamaleonCms::SetupToken.required? && !request.local?
|
|
11
15
|
@site ||= CamaleonCms::Site.new
|
|
12
16
|
@site.slug = request.original_url.to_s.parse_domain
|
|
13
17
|
render 'form'
|
|
@@ -17,6 +21,10 @@ module CamaleonCms
|
|
|
17
21
|
@site = CamaleonCms::Site.new(params[:site].permit(:slug, :name)).decorate
|
|
18
22
|
if @site.save
|
|
19
23
|
site_after_install(@site, params[:theme])
|
|
24
|
+
CamaleonCms::SetupToken.clear! # single-use: the installer is closed now that a site exists
|
|
25
|
+
# Carry the generated admin password to the welcome page for a single, session-scoped display.
|
|
26
|
+
flash[:generated_admin_password] = @site.generated_admin_password
|
|
27
|
+
session[:cama_installer_welcome] = true
|
|
20
28
|
flash[:notice] = t('camaleon_cms.admin.sites.message.created')
|
|
21
29
|
redirect_to action: :welcome
|
|
22
30
|
else
|
|
@@ -24,11 +32,34 @@ module CamaleonCms
|
|
|
24
32
|
end
|
|
25
33
|
end
|
|
26
34
|
|
|
27
|
-
def welcome
|
|
35
|
+
def welcome
|
|
36
|
+
# Only the operator who just completed setup may see the credentials; the marker is one-time.
|
|
37
|
+
return redirect_to cama_admin_login_url unless session.delete(:cama_installer_welcome)
|
|
38
|
+
|
|
39
|
+
@generated_admin_password = flash[:generated_admin_password]
|
|
40
|
+
end
|
|
28
41
|
|
|
29
42
|
def installer_verification
|
|
30
43
|
redirect_to cama_root_url unless CamaleonCms::Site.count == 0
|
|
31
44
|
end
|
|
45
|
+
|
|
46
|
+
# Refuse to provision unless the request carries the setup token (readable only from the server's
|
|
47
|
+
# environment or filesystem). Only enforced while no site exists; afterwards installer_verification
|
|
48
|
+
# has already closed the installer.
|
|
49
|
+
def require_setup_token
|
|
50
|
+
return unless CamaleonCms::SetupToken.required?
|
|
51
|
+
# Loopback requests (a local console/dev install, or an SSH tunnel) already prove host access,
|
|
52
|
+
# so the token is required only for remote requests. request.local? is true only when the
|
|
53
|
+
# computed client IP is loopback, so a real remote client behind a proxy that forwards
|
|
54
|
+
# X-Forwarded-For is still gated. See harden-installer-default-admin.
|
|
55
|
+
return if request.local?
|
|
56
|
+
return if CamaleonCms::SetupToken.valid?(params[:setup_token])
|
|
57
|
+
|
|
58
|
+
flash[:error] = t('camaleon_cms.admin.installer.invalid_setup_token',
|
|
59
|
+
default: 'A valid setup token is required to install. See the server log or ' \
|
|
60
|
+
'tmp/camaleon_setup_token.')
|
|
61
|
+
redirect_to cama_admin_installers_path
|
|
62
|
+
end
|
|
32
63
|
end
|
|
33
64
|
end
|
|
34
65
|
end
|
|
@@ -3,55 +3,101 @@ module CamaleonCms
|
|
|
3
3
|
module Admin
|
|
4
4
|
class MediaController < CamaleonCms::AdminController
|
|
5
5
|
skip_before_action :admin_logged_actions, except: %i[index download_private_file], raise: false
|
|
6
|
-
|
|
6
|
+
# Security (audit M6/M7): upload writes files (and, via saved_avatar-style flows, model state),
|
|
7
|
+
# so it must not skip CSRF. The multipart uploader (jQuery uploadFile) posts outside jquery_ujs'
|
|
8
|
+
# ajax prefilter, so it carries the token as an authenticity_token form field instead (see
|
|
9
|
+
# _media_manager.js). The url-upload path posts through media#actions, already CSRF-protected.
|
|
7
10
|
before_action :init_media_vars, except: :download_private_file
|
|
11
|
+
before_action :verify_media_authorization
|
|
8
12
|
|
|
9
13
|
# render media section
|
|
10
14
|
def index
|
|
11
|
-
authorize! :manage, :media
|
|
12
15
|
@show_file_actions = true
|
|
13
|
-
@files = @tree.paginate(page: params[:page], per_page: 100)
|
|
16
|
+
@files = cama_uploader.cama_prepare_browser_page(@tree.paginate(page: params[:page], per_page: 100))
|
|
14
17
|
@next_page = @files.current_page < @files.total_pages ? @files.current_page + 1 : nil
|
|
15
18
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.media')
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
# crop a image to save as a new file
|
|
22
|
+
#
|
|
23
|
+
# Unlike crop_url (which is URL-only and prefixes the site URL onto relative
|
|
24
|
+
# inputs), crop accepts three cp_img_path shapes: an http(s) URL, a data:
|
|
25
|
+
# URI, or a server filesystem path (see the cama_tmp_upload examples). Only
|
|
26
|
+
# http(s) URLs are validated here; data: URIs and filesystem paths pass
|
|
27
|
+
# through to cama_tmp_upload, whose cama_canonical_upload_path guard confines
|
|
28
|
+
# them to the public/tmp roots. A bare relative path is therefore treated as
|
|
29
|
+
# a filesystem path, not prefixed with the site URL — intentional divergence.
|
|
19
30
|
def crop
|
|
20
|
-
|
|
31
|
+
cp_img_path = params[:cp_img_path].to_s
|
|
32
|
+
if (url_error = cama_upload_url_error(cp_img_path))
|
|
33
|
+
return render(plain: helpers.sanitize(url_error))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Security (object-level authorization): setting another user's avatar is a user-management
|
|
37
|
+
# write, so gate a non-self saved_avatar target on :manage, :users -- the same capability
|
|
38
|
+
# UsersController#update already requires. Decide from the parameter, before resolving the
|
|
39
|
+
# target (so a denied caller cannot use the response as a same-site existence oracle) and
|
|
40
|
+
# before the upload/crop (so no file work happens on a denied request). A caller may always
|
|
41
|
+
# set their own avatar with the media permission the endpoint already requires.
|
|
42
|
+
# (Mirrors UsersController#profile's self-vs-other gate. The .to_s compare and the blank-
|
|
43
|
+
# saved_avatar skip -- a plain crop, not a user write -- are intentional and fail-closed; do
|
|
44
|
+
# not "align" this to users_controller's .to_i, which coerces non-canonical ids.)
|
|
45
|
+
if params[:saved_avatar].present? && params[:saved_avatar].to_s != cama_current_user.id.to_s
|
|
46
|
+
authorize! :manage, :users
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
tmp = cama_tmp_upload(cp_img_path, formats: params[:formats], name: params[:name])
|
|
50
|
+
return render(plain: helpers.sanitize(tmp[:error])) if tmp[:error].present?
|
|
51
|
+
|
|
52
|
+
path_image = tmp[:file_path]
|
|
21
53
|
crop_path = cama_crop_image(path_image, params[:ic_w], params[:ic_h], params[:ic_x], params[:ic_y])
|
|
22
54
|
res = upload_file(crop_path, { remove_source: true })
|
|
23
|
-
|
|
24
|
-
|
|
55
|
+
# A failed upload returns { error: ... } with no url. Surface it like the action's other
|
|
56
|
+
# error paths instead of writing a nil avatar meta and rendering an empty 200 body (which
|
|
57
|
+
# let the avatar flow store "").
|
|
58
|
+
return render(plain: helpers.sanitize(res[:error])) if res[:error].present?
|
|
59
|
+
|
|
60
|
+
# Security (audit Low): resolve the avatar target through the current site and nil-safely.
|
|
61
|
+
# The object-level gate above (:manage, :users for a non-self target) is the primary control
|
|
62
|
+
# that stops a media manager from writing another user's avatar; this site-scoping is defense
|
|
63
|
+
# in depth -- for an authorized :manage,:users holder a foreign id is a no-op here, where an
|
|
64
|
+
# unscoped User.find would reach across sites and 500 on a bad id.
|
|
65
|
+
if params[:saved_avatar].present?
|
|
66
|
+
current_site.users.find_by(id: params[:saved_avatar])&.set_meta('avatar', res['url'])
|
|
67
|
+
end
|
|
68
|
+
render plain: res['url'].to_s
|
|
25
69
|
end
|
|
26
70
|
|
|
27
71
|
# download private files
|
|
28
72
|
def download_private_file
|
|
29
73
|
cama_uploader.enable_private_mode!
|
|
30
74
|
|
|
31
|
-
|
|
75
|
+
sanitize_private_filename!
|
|
76
|
+
return render(plain: 'Invalid file', status: :forbidden) unless @private_file_path
|
|
77
|
+
|
|
78
|
+
fetched = cama_uploader.fetch_file(@private_file_path)
|
|
32
79
|
|
|
33
|
-
return render plain: helpers.sanitize(
|
|
80
|
+
return render plain: helpers.sanitize(fetched[:error]) if fetched.is_a?(Hash) && fetched[:error].present?
|
|
34
81
|
|
|
35
|
-
send_file
|
|
82
|
+
send_file fetched, disposition: 'inline'
|
|
36
83
|
end
|
|
37
84
|
|
|
38
85
|
# render media for modal content
|
|
39
86
|
def ajax
|
|
40
87
|
@show_file_actions = true if current_site.get_option('file_actions_in_modals') == 'yes'
|
|
41
88
|
@tree = cama_uploader.search(params[:search]) if params[:search].present?
|
|
42
|
-
@files = @tree.paginate(page: params[:page], per_page: 100)
|
|
89
|
+
@files = cama_uploader.cama_prepare_browser_page(@tree.paginate(page: params[:page], per_page: 100))
|
|
43
90
|
@next_page = @files.current_page < @files.total_pages ? @files.current_page + 1 : nil
|
|
44
91
|
if params[:partial].present?
|
|
45
92
|
render json: { next_page: @next_page,
|
|
46
93
|
html: render_to_string(partial: 'render_file_item', locals: { files: @files }) }
|
|
47
|
-
|
|
48
|
-
render 'index', layout: false
|
|
94
|
+
elsif params[:partial].blank?
|
|
95
|
+
render 'index', layout: false
|
|
49
96
|
end
|
|
50
97
|
end
|
|
51
98
|
|
|
52
99
|
# do background actions in fog
|
|
53
100
|
def actions
|
|
54
|
-
authorize! :manage, :media if params[:media_action] != 'crop_url'
|
|
55
101
|
params[:folder] = params[:folder].gsub('//', '/') if params[:folder].present?
|
|
56
102
|
|
|
57
103
|
case params[:media_action]
|
|
@@ -66,9 +112,9 @@ module CamaleonCms
|
|
|
66
112
|
when 'crop_url'
|
|
67
113
|
user_url = params[:url].to_s
|
|
68
114
|
user_url = "#{current_site.the_url(locale: nil)}#{user_url}" unless user_url.start_with?('data:', 'http')
|
|
69
|
-
|
|
70
|
-
r = if
|
|
71
|
-
{ error:
|
|
115
|
+
url_error = cama_upload_url_error(user_url)
|
|
116
|
+
r = if url_error
|
|
117
|
+
{ error: url_error }
|
|
72
118
|
else
|
|
73
119
|
cama_tmp_upload(user_url, formats: params[:formats], name: params[:name])
|
|
74
120
|
end
|
|
@@ -91,9 +137,17 @@ module CamaleonCms
|
|
|
91
137
|
params[:dimension] = nil if params[:skip_auto_crop].present?
|
|
92
138
|
f = { error: 'File not found.' }
|
|
93
139
|
if params[:file_upload].present?
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
140
|
+
if params[:file_upload].is_a?(String) && (url_error = cama_upload_url_error(params[:file_upload]))
|
|
141
|
+
return render plain: helpers.sanitize(url_error)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
f = upload_file(
|
|
145
|
+
params[:file_upload],
|
|
146
|
+
{
|
|
147
|
+
folder: params[:folder], dimension: params['dimension'], formats: params[:formats],
|
|
148
|
+
versions: params[:versions], thumb_size: params[:thumb_size]
|
|
149
|
+
}.merge!(settings)
|
|
150
|
+
)
|
|
97
151
|
end
|
|
98
152
|
|
|
99
153
|
if f[:error].present?
|
|
@@ -105,6 +159,17 @@ module CamaleonCms
|
|
|
105
159
|
|
|
106
160
|
private
|
|
107
161
|
|
|
162
|
+
def verify_media_authorization
|
|
163
|
+
authorize! :manage, :media
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Sanitizes the private file parameter to prevent path traversal.
|
|
167
|
+
# Sets @private_file_path if valid, nil otherwise.
|
|
168
|
+
def sanitize_private_filename!
|
|
169
|
+
name = File.basename(params[:file].to_s)
|
|
170
|
+
@private_file_path = ("private/#{name}" if name.present? && name == params[:file].to_s)
|
|
171
|
+
end
|
|
172
|
+
|
|
108
173
|
# init basic media variables
|
|
109
174
|
def init_media_vars
|
|
110
175
|
# @cama_uploader = CamaleonCmsLocalUploader.new({current_site: current_site, private: true})
|
|
@@ -3,9 +3,7 @@ module CamaleonCms
|
|
|
3
3
|
class PluginsController < CamaleonCms::AdminController
|
|
4
4
|
before_action :validate_role
|
|
5
5
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.plugins')
|
|
6
|
-
def index
|
|
7
|
-
PluginRoutes.reload
|
|
8
|
-
end
|
|
6
|
+
def index; end
|
|
9
7
|
|
|
10
8
|
def toggle
|
|
11
9
|
status = params[:status].to_bool
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
module Admin
|
|
3
3
|
class PostTagsController < CamaleonCms::AdminController
|
|
4
|
+
include CamaleonCms::Admin::CustomFieldsConcern
|
|
5
|
+
|
|
4
6
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.contents')
|
|
7
|
+
|
|
5
8
|
before_action :set_post_type
|
|
6
9
|
before_action :set_post_tag, only: %w[show edit update destroy]
|
|
7
10
|
|
|
@@ -27,9 +30,9 @@ module CamaleonCms
|
|
|
27
30
|
def update
|
|
28
31
|
args = { post_tag: @post_tag, post_type: @post_type }
|
|
29
32
|
hooks_run('before_update_post_tag', args)
|
|
30
|
-
if @post_tag.update(
|
|
33
|
+
if @post_tag.update(post_tag_params)
|
|
31
34
|
@post_tag.set_options(params[:meta]) if params[:meta].present?
|
|
32
|
-
@post_tag.set_field_values(
|
|
35
|
+
@post_tag.set_field_values(cama_permitted_field_options('PostType_PostTag'))
|
|
33
36
|
hooks_run('after_update_post_tag', args)
|
|
34
37
|
flash[:notice] = t('camaleon_cms.admin.post_type.message.updated')
|
|
35
38
|
redirect_to action: :index
|
|
@@ -40,12 +43,12 @@ module CamaleonCms
|
|
|
40
43
|
|
|
41
44
|
# render post tag create form
|
|
42
45
|
def create
|
|
43
|
-
@post_tag = @post_type.post_tags.new(
|
|
46
|
+
@post_tag = @post_type.post_tags.new(post_tag_params)
|
|
44
47
|
args = { post_tag: @post_tag, post_type: @post_type }
|
|
45
48
|
hooks_run('before_create_post_tag', args)
|
|
46
49
|
if @post_tag.save
|
|
47
50
|
@post_tag.set_options(params[:meta]) if params[:meta].present?
|
|
48
|
-
@post_tag.set_field_values(
|
|
51
|
+
@post_tag.set_field_values(cama_permitted_field_options('PostType_PostTag'))
|
|
49
52
|
hooks_run('after_create_post_tag', args)
|
|
50
53
|
flash[:notice] = t('camaleon_cms.admin.post_type.message.created')
|
|
51
54
|
redirect_to action: :index
|
|
@@ -70,15 +73,21 @@ module CamaleonCms
|
|
|
70
73
|
|
|
71
74
|
private
|
|
72
75
|
|
|
76
|
+
# parent_id is the owning post type's id, set from the @post_type association on create; it is not
|
|
77
|
+
# accepted from the request so a tag cannot be moved under another post type or site (H8).
|
|
78
|
+
def post_tag_params
|
|
79
|
+
params.require(:post_tag).permit(:name, :slug, :description)
|
|
80
|
+
end
|
|
81
|
+
|
|
73
82
|
def set_post_type
|
|
74
|
-
@post_type = current_site.post_types.
|
|
83
|
+
@post_type = current_site.post_types.find_by(id: params[:post_type_id]).decorate
|
|
75
84
|
authorize! :post_tags, @post_type
|
|
76
85
|
add_breadcrumb @post_type.the_title, @post_type.the_admin_url
|
|
77
86
|
add_breadcrumb t('camaleon_cms.admin.post_type.post_tags'), url_for({ action: :index })
|
|
78
87
|
end
|
|
79
88
|
|
|
80
89
|
def set_post_tag
|
|
81
|
-
@post_tag = @post_type.post_tags.
|
|
90
|
+
@post_tag = @post_type.post_tags.find_by(id: params[:id])
|
|
82
91
|
rescue StandardError
|
|
83
92
|
flash[:error] = t('camaleon_cms.admin.post_type.message.error')
|
|
84
93
|
redirect_to cama_admin_path
|