camaleon_cms 2.9.2 → 2.9.4
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 +17 -4
- 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 +14 -10
- data/app/apps/plugins/front_cache/config/initializer.rb +1 -1
- data/app/apps/plugins/front_cache/config/locales/translation.yml +0 -7
- data/app/apps/plugins/front_cache/front_cache_helper.rb +109 -37
- data/app/apps/plugins/front_cache/views/admin/settings.html.erb +0 -5
- 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/layouts/index.html.erb +3 -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/layouts/_header.html.erb +3 -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 +8 -6
- data/app/controllers/camaleon_cms/admin/appearances/themes_controller.rb +8 -5
- data/app/controllers/camaleon_cms/admin/appearances/widgets/assign_controller.rb +7 -2
- data/app/controllers/camaleon_cms/admin/appearances/widgets/main_controller.rb +2 -1
- data/app/controllers/camaleon_cms/admin/categories_controller.rb +19 -4
- 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 +66 -14
- data/app/controllers/camaleon_cms/admin/plugins_controller.rb +1 -3
- data/app/controllers/camaleon_cms/admin/post_tags_controller.rb +10 -4
- data/app/controllers/camaleon_cms/admin/posts/drafts_controller.rb +40 -11
- data/app/controllers/camaleon_cms/admin/posts_controller.rb +55 -22
- data/app/controllers/camaleon_cms/admin/sessions_controller.rb +237 -44
- data/app/controllers/camaleon_cms/admin/settings/custom_fields_controller.rb +80 -13
- data/app/controllers/camaleon_cms/admin/settings/post_types_controller.rb +5 -2
- data/app/controllers/camaleon_cms/admin/settings/sites_controller.rb +26 -3
- data/app/controllers/camaleon_cms/admin/settings_controller.rb +5 -3
- data/app/controllers/camaleon_cms/admin/user_roles_controller.rb +7 -5
- data/app/controllers/camaleon_cms/admin/users_controller.rb +78 -11
- data/app/controllers/camaleon_cms/admin_controller.rb +136 -26
- data/app/controllers/camaleon_cms/camaleon_controller.rb +37 -92
- data/app/controllers/camaleon_cms/frontend_controller.rb +146 -74
- data/app/controllers/concerns/camaleon_cms/admin/custom_fields_concern.rb +20 -5
- 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 +68 -50
- data/app/decorators/camaleon_cms/post_type_decorator.rb +1 -1
- data/app/decorators/camaleon_cms/site_decorator.rb +33 -25
- 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 +5 -8
- 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 +34 -17
- 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 +19 -7
- 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/application_helper.rb +6 -1
- data/app/helpers/camaleon_cms/frontend/content_select_helper.rb +34 -20
- data/app/helpers/camaleon_cms/frontend/nav_menu_helper.rb +143 -66
- 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 +58 -37
- data/app/helpers/camaleon_cms/plugins_helper.rb +23 -24
- data/app/helpers/camaleon_cms/session_helper.rb +188 -44
- data/app/helpers/camaleon_cms/short_code_helper.rb +181 -85
- data/app/helpers/camaleon_cms/site_helper.rb +34 -13
- data/app/helpers/camaleon_cms/theme_helper.rb +35 -19
- data/app/helpers/camaleon_cms/uploader_helper.rb +19 -473
- 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 +15 -7
- data/app/models/camaleon_cms/category.rb +6 -6
- data/app/models/camaleon_cms/custom_field.rb +15 -6
- data/app/models/camaleon_cms/custom_field_group.rb +39 -22
- data/app/models/camaleon_cms/custom_fields_relationship.rb +140 -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 +5 -3
- data/app/models/camaleon_cms/nav_menu_item.rb +16 -8
- data/app/models/camaleon_cms/plugin.rb +3 -4
- data/app/models/camaleon_cms/post.rb +137 -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 -5
- data/app/models/camaleon_cms/post_type.rb +21 -13
- data/app/models/camaleon_cms/site.rb +39 -29
- data/app/models/camaleon_cms/term_relationship.rb +10 -7
- data/app/models/camaleon_cms/term_taxonomy.rb +58 -4
- data/app/models/camaleon_cms/theme.rb +2 -4
- data/app/models/camaleon_cms/user.rb +17 -5
- data/app/models/camaleon_cms/user_role.rb +81 -6
- data/app/models/camaleon_cms/widget/assigned.rb +11 -6
- data/app/models/camaleon_cms/widget/main.rb +12 -8
- data/app/models/camaleon_cms/widget/sidebar.rb +3 -8
- data/app/models/camaleon_record.rb +89 -5
- data/app/models/concerns/camaleon_cms/categories_tags_for_posts.rb +36 -42
- data/app/models/concerns/camaleon_cms/common_relationships.rb +18 -5
- data/app/models/concerns/camaleon_cms/content_shortcode_gate.rb +73 -0
- data/app/models/concerns/camaleon_cms/custom_fields_read.rb +111 -54
- data/app/models/concerns/camaleon_cms/metas.rb +31 -6
- 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 +8 -1
- data/app/uploaders/camaleon_cms_aws_uploader.rb +31 -10
- 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 +5 -4
- 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/categories/index.html.erb +2 -2
- 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 +2 -2
- data/app/views/camaleon_cms/admin/posts/_sidebar.html.erb +1 -1
- data/app/views/camaleon_cms/admin/posts/form.html.erb +3 -2
- data/app/views/camaleon_cms/admin/posts/index.html.erb +5 -5
- 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 +19 -4
- 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/form.html.erb +4 -3
- 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/user_roles/form.html.erb +24 -3
- data/app/views/camaleon_cms/admin/users/form.html.erb +8 -4
- data/app/views/camaleon_cms/admin/users/index.html.erb +1 -1
- 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/_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 +1 -1
- data/config/locales/camaleon_cms/admin/ar.yml +1 -0
- data/config/locales/camaleon_cms/admin/de.yml +1 -0
- data/config/locales/camaleon_cms/admin/en.yml +36 -0
- data/config/locales/camaleon_cms/admin/es.yml +1 -0
- data/config/locales/camaleon_cms/admin/fr.yml +1 -0
- data/config/locales/camaleon_cms/admin/it.yml +1 -0
- data/config/locales/camaleon_cms/admin/nl.yml +1 -0
- data/config/locales/camaleon_cms/admin/pt-BR.yml +1 -0
- data/config/locales/camaleon_cms/admin/pt.yml +1 -0
- data/config/locales/camaleon_cms/admin/ru.yml +1 -0
- data/config/locales/camaleon_cms/admin/uk.yml +1 -0
- data/config/locales/camaleon_cms/admin/zh-CH.yml +1 -0
- data/config/routes/admin.rb +33 -15
- data/config/routes/frontend.rb +21 -15
- 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 +45 -21
- data/lib/camaleon_cms/media_security_headers.rb +27 -0
- data/lib/camaleon_cms/setup_token.rb +69 -0
- data/lib/camaleon_cms/shortcode_registry.rb +102 -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 +23 -0
- data/lib/ext/hash.rb +28 -2
- data/lib/ext/string.rb +10 -5
- 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 +15 -13
- 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 +125 -4
|
@@ -5,21 +5,24 @@ module CamaleonCms
|
|
|
5
5
|
add_flash_types :notice
|
|
6
6
|
add_flash_types :info
|
|
7
7
|
|
|
8
|
+
# Controller-context helper surface (ct, cama_t, cama_cache_fetch, ...): plugin hook
|
|
9
|
+
# functions run on the controller and actions like FrontendController#search call these.
|
|
8
10
|
include CamaleonCms::CamaleonHelper
|
|
9
|
-
include CamaleonCms::
|
|
10
|
-
include CamaleonCms::
|
|
11
|
-
include CamaleonCms::
|
|
12
|
-
include CamaleonCms::
|
|
13
|
-
include CamaleonCms::
|
|
14
|
-
include CamaleonCms::
|
|
15
|
-
include CamaleonCms::
|
|
16
|
-
include CamaleonCms::
|
|
17
|
-
include CamaleonCms::ContentHelper
|
|
18
|
-
include CamaleonCms::CaptchaHelper
|
|
19
|
-
include CamaleonCms::UploaderHelper
|
|
20
|
-
include CamaleonCms::EmailHelper
|
|
11
|
+
include CamaleonCms::SessionRuntimeConcern
|
|
12
|
+
include CamaleonCms::RequestContextConcern
|
|
13
|
+
include CamaleonCms::HookLifecycleConcern
|
|
14
|
+
include CamaleonCms::RuntimeShortcodeThemeConcern
|
|
15
|
+
include CamaleonCms::RuntimeHtmlContentConcern
|
|
16
|
+
include CamaleonCms::RuntimeAdminMenuConcern
|
|
17
|
+
include CamaleonCms::RuntimeCaptchaImageConcern
|
|
18
|
+
include CamaleonCms::RuntimeUploaderConcern
|
|
21
19
|
include Mobu::DetectMobile
|
|
20
|
+
delegate :tag, :content_tag, :safe_join, :image_tag, :link_to, :sanitize, to: :helpers
|
|
22
21
|
|
|
22
|
+
# Preserve backwards-compatible plugin helper inclusion: any helper module a
|
|
23
|
+
# plugin declares in its config.json is mixed in here so plugins can call
|
|
24
|
+
# those methods from controllers (not only from views). Documented in
|
|
25
|
+
# `docs/ai/plans/helper-concern-architecture-refactor.md`.
|
|
23
26
|
PluginRoutes.all_helpers.each { |h| include h.constantize }
|
|
24
27
|
|
|
25
28
|
before_action :cama_site_check_existence, except: [:captcha]
|
|
@@ -35,13 +38,24 @@ module CamaleonCms
|
|
|
35
38
|
|
|
36
39
|
# show page error
|
|
37
40
|
def render_error(status = 404, exception = nil, message = '')
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
error_msg = params[:error_msg]
|
|
42
|
+
Rails.logger.debug do
|
|
43
|
+
original_url = begin
|
|
44
|
+
request.original_url
|
|
45
|
+
rescue StandardError
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
'Camaleon CMS - 404 url: ' \
|
|
49
|
+
"#{original_url} ==> message: #{exception&.message} ==> #{error_msg} ==> #{caller.inspect}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
@message = if Rails.env.production?
|
|
53
|
+
''
|
|
54
|
+
else
|
|
55
|
+
"#{message} " \
|
|
56
|
+
"#{error_msg || (exception.present? ? "#{exception.message}<br><br>#{caller.inspect}" : '')}"
|
|
57
|
+
end
|
|
58
|
+
|
|
45
59
|
respond_to do |format|
|
|
46
60
|
format.html { render "camaleon_cms/#{status}", status: status }
|
|
47
61
|
format.any { head status }
|
|
@@ -69,18 +83,9 @@ module CamaleonCms
|
|
|
69
83
|
# initializing before and after contents
|
|
70
84
|
cama_content_init
|
|
71
85
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
request.env.except!('HTTP_X_FORWARDED_HOST') if request.env['HTTP_X_FORWARDED_HOST'] # just drop the variable
|
|
77
|
-
|
|
78
|
-
views_dir = 'app/apps/'
|
|
79
|
-
prepend_view_path(File.join($camaleon_engine_dir, views_dir).to_s)
|
|
80
|
-
prepend_view_path(Rails.root.join(views_dir).to_s)
|
|
81
|
-
|
|
82
|
-
CamaleonCms::PostDefault.current_user = cama_current_user
|
|
83
|
-
CamaleonCms::PostDefault.current_site = current_site
|
|
86
|
+
initialize_hook_skip_list
|
|
87
|
+
run_app_before_load_hooks
|
|
88
|
+
configure_runtime_request_context
|
|
84
89
|
end
|
|
85
90
|
|
|
86
91
|
# initialize ability for current user
|
|
@@ -89,67 +94,7 @@ module CamaleonCms
|
|
|
89
94
|
end
|
|
90
95
|
|
|
91
96
|
def cama_after_actions
|
|
92
|
-
|
|
93
|
-
hooks_run('app_after_load')
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# redirect to sessions login form when the session was expired.
|
|
97
|
-
def auth_session_error
|
|
98
|
-
redirect_to cama_root_path
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# check if current site exist, if not, this will be redirected to main domain
|
|
102
|
-
# Also, check current site status
|
|
103
|
-
def cama_site_check_existence
|
|
104
|
-
if !current_site.present?
|
|
105
|
-
if Cama::Site.main_site.present?
|
|
106
|
-
url = Cama::Site.main_site.decorate.the_url
|
|
107
|
-
# TODO: Remove this condition when Rails 6.x won't be supported
|
|
108
|
-
if Rails.gem_version >= Gem::Version.new('7.0.0')
|
|
109
|
-
redirect_to url, allow_other_host: true
|
|
110
|
-
else
|
|
111
|
-
redirect_to url
|
|
112
|
-
end
|
|
113
|
-
else
|
|
114
|
-
redirect_to cama_admin_installers_path
|
|
115
|
-
end
|
|
116
|
-
elsif (cama_current_user.present? && !cama_current_user.admin?) || !cama_current_user.present?
|
|
117
|
-
# inactive page control
|
|
118
|
-
if current_site.is_inactive?
|
|
119
|
-
if request.original_url.to_s.match(%r{\A#{current_site.the_url}admin(/|\z)})
|
|
120
|
-
if cama_current_user.present?
|
|
121
|
-
cama_logout_user
|
|
122
|
-
flash[:error] = 'Site is Inactive'
|
|
123
|
-
end
|
|
124
|
-
else
|
|
125
|
-
p = current_site.posts.find_by_id(current_site.get_option('page_inactive')).try(:decorate)
|
|
126
|
-
if p
|
|
127
|
-
redirect_to(p.the_url) unless params == { 'controller' => 'camaleon_cms/frontend', 'action' => 'post',
|
|
128
|
-
'slug' => p.the_slug }
|
|
129
|
-
else
|
|
130
|
-
render html: 'This site was inactivated. Please contact to administrator.'
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# maintenance page and IP's control
|
|
136
|
-
if current_site.is_maintenance? && !current_site.get_option('maintenance_ips',
|
|
137
|
-
'').split(',').include?(request.remote_ip)
|
|
138
|
-
p = current_site.posts.find_by_id(current_site.get_option('page_maintenance')).try(:decorate)
|
|
139
|
-
if p
|
|
140
|
-
redirect_to(p.the_url) if params != { 'controller' => 'camaleon_cms/frontend', 'action' => 'post',
|
|
141
|
-
'slug' => p.the_slug }
|
|
142
|
-
else
|
|
143
|
-
render html: 'This site is in maintenance mode. Please contact to administrator.'
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
unless ApplicationController.method_defined?(:current_user)
|
|
150
|
-
def current_user
|
|
151
|
-
cama_current_user
|
|
152
|
-
end
|
|
97
|
+
run_app_after_load_hooks
|
|
153
98
|
end
|
|
154
99
|
end
|
|
155
100
|
end
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class FrontendController < CamaleonCms::CamaleonController
|
|
3
3
|
before_action :init_frontent
|
|
4
|
+
include CamaleonCms::FrontendVisitedStateConcern
|
|
4
5
|
include CamaleonCms::FrontendConcern
|
|
5
6
|
include CamaleonCms::Frontend::ApplicationHelper
|
|
6
7
|
layout proc { |controller|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
args = {
|
|
9
|
+
layout: (params[:cama_ajax_request].present? ? 'cama_ajax' : PluginRoutes.static_system_info['default_layout']),
|
|
10
|
+
controller: controller
|
|
11
|
+
}
|
|
12
|
+
hooks_run('front_default_layout', args)
|
|
13
|
+
args[:layout]
|
|
14
|
+
}
|
|
13
15
|
before_action :before_hooks
|
|
14
16
|
after_action :after_hooks
|
|
15
|
-
# rescue_from ActiveRecord::RecordNotFound, with: :page_not_found
|
|
16
|
-
|
|
17
17
|
# home page for frontend
|
|
18
18
|
def index
|
|
19
|
-
|
|
19
|
+
mark_frontend_home_visited
|
|
20
20
|
if @_site_options[:home_page].present?
|
|
21
21
|
render_post(@_site_options[:home_page].to_i)
|
|
22
22
|
else
|
|
@@ -29,28 +29,38 @@ module CamaleonCms
|
|
|
29
29
|
# render category list
|
|
30
30
|
def category
|
|
31
31
|
begin
|
|
32
|
-
|
|
32
|
+
if params[:category_slug].present?
|
|
33
|
+
@category ||= current_site.the_full_categories.find_by_slug(params[:category_slug]).decorate # rubocop:disable Rails/DynamicFindBy
|
|
34
|
+
end
|
|
33
35
|
@category ||= current_site.the_full_categories.find(params[:category_id]).decorate
|
|
34
36
|
@post_type = @category.the_post_type
|
|
35
37
|
rescue StandardError
|
|
36
38
|
return page_not_found
|
|
37
39
|
end
|
|
38
|
-
@
|
|
40
|
+
mark_frontend_category_visited(@category)
|
|
39
41
|
@children = @category.children.no_empty.decorate
|
|
40
42
|
@posts = @category.the_posts.paginate(page: params[:page],
|
|
41
|
-
per_page: current_site.front_per_page)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
per_page: current_site.front_per_page)
|
|
44
|
+
category_slug = @category.the_slug
|
|
45
|
+
|
|
46
|
+
# specific template category with specific slug within a post-type
|
|
47
|
+
r_file = lookup_context.template_exists?("category_#{category_slug}") ? "category_#{category_slug}" : nil
|
|
48
|
+
post_type_slug = "post_types/#{@post_type.the_slug}"
|
|
49
|
+
if r_file.blank?
|
|
50
|
+
r_file = lookup_context.template_exists?("#{post_type_slug}/category") ? "#{post_type_slug}/category" : nil
|
|
45
51
|
end
|
|
46
|
-
|
|
47
|
-
r_file = lookup_context.template_exists?("categories/#{
|
|
52
|
+
if r_file.blank?
|
|
53
|
+
r_file = if lookup_context.template_exists?("categories/#{category_slug}")
|
|
54
|
+
"categories/#{category_slug}"
|
|
55
|
+
else
|
|
56
|
+
'category'
|
|
57
|
+
end
|
|
48
58
|
end
|
|
49
59
|
|
|
50
|
-
layout_ = if lookup_context.template_exists?("layouts
|
|
51
|
-
"
|
|
52
|
-
elsif lookup_context.template_exists?("layouts/categories/#{
|
|
53
|
-
"categories/#{
|
|
60
|
+
layout_ = if lookup_context.template_exists?("layouts/#{post_type_slug}/category")
|
|
61
|
+
"#{post_type_slug}/category"
|
|
62
|
+
elsif lookup_context.template_exists?("layouts/categories/#{category_slug}")
|
|
63
|
+
"categories/#{category_slug}"
|
|
54
64
|
end
|
|
55
65
|
r = { category: @category, layout: layout_, render: r_file }
|
|
56
66
|
hooks_run('on_render_category', r)
|
|
@@ -60,18 +70,20 @@ module CamaleonCms
|
|
|
60
70
|
# render contents from post type
|
|
61
71
|
def post_type
|
|
62
72
|
begin
|
|
63
|
-
@post_type = current_site.post_types.find_by_slug(params[:post_type_slug]).decorate
|
|
73
|
+
@post_type = current_site.post_types.find_by_slug(params[:post_type_slug]).decorate # rubocop:disable Rails/DynamicFindBy
|
|
64
74
|
rescue StandardError
|
|
65
75
|
return page_not_found
|
|
66
76
|
end
|
|
67
77
|
@object = @post_type
|
|
68
|
-
|
|
78
|
+
CurrentRequest.frontend_object = @object
|
|
79
|
+
mark_frontend_post_type_visited(@post_type)
|
|
69
80
|
@posts = @post_type.the_posts.paginate(page: params[:page],
|
|
70
|
-
per_page: current_site.front_per_page)
|
|
81
|
+
per_page: current_site.front_per_page)
|
|
71
82
|
@categories = @post_type.categories.no_empty.eager_load(:metas).decorate
|
|
72
83
|
@post_tags = @post_type.post_tags.eager_load(:metas)
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
post_type_slug = "post_types/#{@post_type.the_slug}"
|
|
85
|
+
r_file = lookup_context.template_exists?(post_type_slug) ? post_type_slug : 'post_type'
|
|
86
|
+
layout_ = lookup_context.template_exists?("layouts/#{post_type_slug}") ? post_type_slug : nil
|
|
75
87
|
r = { post_type: @post_type, layout: layout_, render: r_file }
|
|
76
88
|
hooks_run('on_render_post_type', r)
|
|
77
89
|
render r[:render], (!r[:layout].nil? ? { layout: r[:layout] } : {})
|
|
@@ -81,7 +93,7 @@ module CamaleonCms
|
|
|
81
93
|
def post_tag
|
|
82
94
|
begin
|
|
83
95
|
@post_tag = if params[:post_tag_slug].present?
|
|
84
|
-
current_site.post_tags.find_by_slug(params[:post_tag_slug]).decorate
|
|
96
|
+
current_site.post_tags.find_by_slug(params[:post_tag_slug]).decorate # rubocop:disable Rails/DynamicFindBy
|
|
85
97
|
else
|
|
86
98
|
current_site.post_tags.find(params[:post_tag_id]).decorate
|
|
87
99
|
end
|
|
@@ -90,10 +102,12 @@ module CamaleonCms
|
|
|
90
102
|
return page_not_found
|
|
91
103
|
end
|
|
92
104
|
@object = @post_tag
|
|
93
|
-
|
|
105
|
+
CurrentRequest.frontend_object = @object
|
|
106
|
+
mark_frontend_tag_visited(@post_tag)
|
|
94
107
|
@posts = @post_tag.the_posts.paginate(page: params[:page],
|
|
95
|
-
per_page: current_site.front_per_page)
|
|
96
|
-
|
|
108
|
+
per_page: current_site.front_per_page)
|
|
109
|
+
slug_post_tag = "post_types/#{@post_type.the_slug}/post_tag"
|
|
110
|
+
r_file = lookup_context.template_exists?(slug_post_tag) ? slug_post_tag : 'post_tag'
|
|
97
111
|
layout_ = lookup_context.template_exists?('layouts/post_tag') ? 'post_tag' : nil
|
|
98
112
|
r = { post_tag: @post_tag, layout: layout_, render: r_file }
|
|
99
113
|
hooks_run('on_render_post_tag', r)
|
|
@@ -103,19 +117,17 @@ module CamaleonCms
|
|
|
103
117
|
# search contents
|
|
104
118
|
def search
|
|
105
119
|
breadcrumb_add(ct('search'))
|
|
106
|
-
|
|
107
|
-
|
|
120
|
+
post_type_slugs = params[:post_type_slugs]
|
|
121
|
+
items = post_type_slugs.present? ? current_site.the_posts(post_type_slugs.split(',')) : current_site.the_posts
|
|
122
|
+
mark_frontend_search_visited
|
|
108
123
|
@param_search = params[:q]
|
|
109
124
|
layout_ = lookup_context.template_exists?('layouts/search') ? 'search' : nil
|
|
110
125
|
r = { layout: layout_, render: 'search', posts: nil }
|
|
111
126
|
hooks_run('on_render_search', r)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
items.where('LOWER(title) LIKE ? OR LOWER(content_filtered) LIKE ?',
|
|
117
|
-
"%#{params[:q]}%", "%#{params[:q]}%")
|
|
118
|
-
end
|
|
127
|
+
q_params = params[:q].to_s.downcase
|
|
128
|
+
params[:q] = q_params
|
|
129
|
+
@posts = r[:posts] ||
|
|
130
|
+
items.where('LOWER(title) LIKE ? OR LOWER(content_filtered) LIKE ?', "%#{q_params}%", "%#{q_params}%")
|
|
119
131
|
@posts_size = @posts.size
|
|
120
132
|
@posts = @posts.paginate(page: params[:page], per_page: current_site.front_per_page)
|
|
121
133
|
render r[:render], (!r[:layout].nil? ? { layout: r[:layout] } : {})
|
|
@@ -124,7 +136,7 @@ module CamaleonCms
|
|
|
124
136
|
# ajax requests
|
|
125
137
|
def ajax
|
|
126
138
|
r = { render_file: nil, render_text: '', layout: nil }
|
|
127
|
-
|
|
139
|
+
mark_frontend_ajax_visited
|
|
128
140
|
hooks_run('on_ajax', r)
|
|
129
141
|
if r[:render_file]
|
|
130
142
|
render r[:render_file], (!r[:layout].nil? ? { layout: r[:layout] } : {})
|
|
@@ -150,7 +162,8 @@ module CamaleonCms
|
|
|
150
162
|
return page_not_found
|
|
151
163
|
end
|
|
152
164
|
@object = @user
|
|
153
|
-
|
|
165
|
+
CurrentRequest.frontend_object = @object
|
|
166
|
+
mark_frontend_profile_visited(@user)
|
|
154
167
|
layout_ = lookup_context.template_exists?('layouts/profile') ? 'profile' : nil
|
|
155
168
|
r = { user: @user, layout: layout_, render: 'profile' }
|
|
156
169
|
hooks_run('on_render_profile', r)
|
|
@@ -168,6 +181,7 @@ module CamaleonCms
|
|
|
168
181
|
def draft_render
|
|
169
182
|
post_draft = current_site.posts.drafts.find(params[:draft_id])
|
|
170
183
|
@object = post_draft
|
|
184
|
+
CurrentRequest.frontend_object = @object
|
|
171
185
|
|
|
172
186
|
# let a hook override the ability for certain roles see drafts
|
|
173
187
|
args = { permitted: false }
|
|
@@ -184,25 +198,30 @@ module CamaleonCms
|
|
|
184
198
|
# post_or_slug_or_id: slug_post | id post | post object
|
|
185
199
|
# from_url: true/false => true (true, permit eval hooks "on_render_post")
|
|
186
200
|
def render_post(post_or_slug_or_id, from_url = false, status = nil, force_visit = false)
|
|
201
|
+
# eager: false -- rendering one post must not pay the listing preloads (with_eager)
|
|
202
|
+
visible_posts = verify_front_visibility(current_site.posts, eager: false)
|
|
187
203
|
@post = case post_or_slug_or_id
|
|
188
204
|
when String # slug
|
|
189
|
-
|
|
205
|
+
# find_by_slug is multi-language aware (matches localized slugs like
|
|
206
|
+
# "<!--:en-->sample-post<!--:-->..."), unlike find_by(slug:)
|
|
207
|
+
visible_posts.find_by_slug(post_or_slug_or_id) # rubocop:disable Rails/DynamicFindBy
|
|
190
208
|
when Integer # id
|
|
191
|
-
|
|
209
|
+
visible_posts.where(id: post_or_slug_or_id).first
|
|
192
210
|
else # model
|
|
193
211
|
post_or_slug_or_id
|
|
194
212
|
end
|
|
195
213
|
|
|
196
214
|
@post = @post.try(:decorate)
|
|
197
|
-
if
|
|
198
|
-
if params[:format] == 'html' ||
|
|
215
|
+
if @post.blank? || !(force_visit || @post.can_visit?)
|
|
216
|
+
if params[:format] == 'html' || params[:format].blank?
|
|
199
217
|
page_not_found
|
|
200
218
|
else
|
|
201
|
-
head
|
|
219
|
+
head :not_found
|
|
202
220
|
end
|
|
203
221
|
else
|
|
204
222
|
@object = @post
|
|
205
|
-
|
|
223
|
+
CurrentRequest.frontend_object = @object
|
|
224
|
+
mark_frontend_post_visited(@post)
|
|
206
225
|
@post_type = @post.the_post_type
|
|
207
226
|
@comments = @post.the_comments
|
|
208
227
|
@categories = @post.the_categories
|
|
@@ -213,10 +232,11 @@ module CamaleonCms
|
|
|
213
232
|
rescue StandardError
|
|
214
233
|
nil
|
|
215
234
|
end
|
|
235
|
+
post_template = @post.get_template(@post_type)
|
|
216
236
|
r_file = if lookup_context.template_exists?("page_#{@post.id}")
|
|
217
237
|
"page_#{@post.id}"
|
|
218
|
-
elsif
|
|
219
|
-
|
|
238
|
+
elsif post_template.present? && lookup_context.template_exists?(post_template)
|
|
239
|
+
post_template
|
|
220
240
|
elsif home_page.present? && @post.id.to_s == home_page
|
|
221
241
|
'index'
|
|
222
242
|
elsif lookup_context.template_exists?("post_types/#{@post_type.the_slug}/single")
|
|
@@ -261,17 +281,54 @@ module CamaleonCms
|
|
|
261
281
|
# if url hasn't a locale, then it will use default locale set on application.rb
|
|
262
282
|
def init_frontent
|
|
263
283
|
# preview theme initializing
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
284
|
+
previewing_theme = cama_sign_in? && params[:ccc_theme_preview].present? && can?(:manage, :themes)
|
|
285
|
+
if previewing_theme
|
|
286
|
+
preview_theme = current_site.themes.where(slug: params[:ccc_theme_preview]).first_or_create!.decorate
|
|
287
|
+
@_current_theme = preview_theme
|
|
288
|
+
CurrentRequest.frontend_current_theme = preview_theme
|
|
289
|
+
ensure_preview_site_defaults
|
|
290
|
+
end
|
|
267
291
|
|
|
268
292
|
@_site_options = current_site.options
|
|
269
|
-
|
|
293
|
+
# Only a scalar language param participates in locale resolution — a non-scalar
|
|
294
|
+
# (?locale[]= / ?cama_set_language[]=) would crash to_sym and 500 the request
|
|
295
|
+
set_language = params[:cama_set_language]
|
|
296
|
+
session[:cama_current_language] = set_language.to_sym if set_language.is_a?(String) && set_language.present?
|
|
270
297
|
session[:cama_current_language] = nil if current_site.get_languages.exclude?(session[:cama_current_language])
|
|
271
|
-
|
|
272
|
-
|
|
298
|
+
requested_locale = params[:locale] if params[:locale].is_a?(String)
|
|
299
|
+
I18n.locale = requested_locale || session[:cama_current_language] || current_site.get_languages.first
|
|
300
|
+
# The availability gate renders the site's 404 (directly or via a custom error_404
|
|
301
|
+
# post), so the theme lookup prefixes must already be registered when it fires
|
|
302
|
+
configure_frontend_lookup_prefixes
|
|
303
|
+
theme_init
|
|
304
|
+
return if current_site.get_languages.include?(I18n.locale.to_sym)
|
|
305
|
+
|
|
306
|
+
# The unoffered locale must not style the 404 page or leak into its links
|
|
307
|
+
I18n.locale = current_site.get_languages.first
|
|
308
|
+
page_not_found
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# initialize hooks before to execute action
|
|
312
|
+
def before_hooks
|
|
313
|
+
hooks_run('front_before_load')
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# initialize hooks after executed action
|
|
317
|
+
def after_hooks
|
|
318
|
+
hooks_run('front_after_load')
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# define default options for url helpers
|
|
322
|
+
# control for default locale
|
|
323
|
+
def default_url_options(options = {})
|
|
324
|
+
return options if current_site.get_languages.first.to_s == I18n.locale.to_s
|
|
273
325
|
|
|
274
|
-
|
|
326
|
+
{ locale: I18n.locale }.merge!(options)
|
|
327
|
+
rescue StandardError
|
|
328
|
+
options
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def configure_frontend_lookup_prefixes
|
|
275
332
|
lookup_context.prefixes.delete('frontend')
|
|
276
333
|
lookup_context.prefixes.delete('application')
|
|
277
334
|
lookup_context.prefixes.delete('camaleon_cms/frontend')
|
|
@@ -282,36 +339,51 @@ module CamaleonCms
|
|
|
282
339
|
t =~ %r{themes/(.*)/views}i || t == 'camaleon_cms/default_theme' || t == "themes/#{current_site.id}/views"
|
|
283
340
|
end
|
|
284
341
|
|
|
285
|
-
|
|
286
|
-
|
|
342
|
+
# Per-site theme override: views placed under app/apps/themes/<site_id>/views
|
|
343
|
+
# take precedence over the active theme's views (it is appended first).
|
|
344
|
+
if Dir.exist?(Rails.root.join('app', 'apps', 'themes', current_site.id.to_s).to_s)
|
|
345
|
+
lookup_context.prefixes.append("themes/#{current_site.id}/views")
|
|
346
|
+
end
|
|
287
347
|
lookup_context.prefixes.append("themes/#{current_theme.slug}/views")
|
|
288
348
|
lookup_context.prefixes.append('camaleon_cms/default_theme')
|
|
289
349
|
|
|
290
350
|
lookup_context.prefixes = lookup_context.prefixes.uniq
|
|
291
351
|
lookup_context.use_camaleon_partial_prefixes = true
|
|
292
|
-
theme_init
|
|
293
352
|
end
|
|
294
353
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
354
|
+
def ensure_preview_site_defaults
|
|
355
|
+
preview_required_menu_slugs.each do |slug|
|
|
356
|
+
current_site.nav_menus.where(slug: slug).first_or_create! do |menu|
|
|
357
|
+
menu.name = slug.to_s.humanize
|
|
358
|
+
end
|
|
359
|
+
end
|
|
298
360
|
end
|
|
299
361
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
362
|
+
def preview_required_menu_slugs
|
|
363
|
+
slugs = ['main_menu']
|
|
364
|
+
slugs.concat(extract_theme_menu_slugs(current_theme.settings['path']))
|
|
365
|
+
slugs.uniq
|
|
303
366
|
end
|
|
304
367
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
368
|
+
def extract_theme_menu_slugs(theme_path)
|
|
369
|
+
return [] if theme_path.blank?
|
|
370
|
+
|
|
371
|
+
views_path = File.join(theme_path, 'views')
|
|
372
|
+
return [] unless Dir.exist?(views_path)
|
|
373
|
+
|
|
374
|
+
Dir.glob(File.join(views_path, '**', '*.erb')).flat_map do |file_path|
|
|
375
|
+
extract_menu_slugs_from_template(File.read(file_path))
|
|
376
|
+
end.uniq
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def extract_menu_slugs_from_template(content)
|
|
380
|
+
slugs = []
|
|
381
|
+
slugs.concat(content.scan(/nav_menus\.where\(\s*slug:\s*'([^']+)'\s*\)/).flatten)
|
|
382
|
+
slugs.concat(content.scan(/nav_menus\.where\(\s*slug:\s*"([^"]+)"\s*\)/).flatten)
|
|
383
|
+
content.scan(/nav_menus\.where\(\s*slug:\s*\[([^\]]+)\]\s*\)/).flatten.each do |array_content|
|
|
384
|
+
slugs.concat(array_content.scan(/['"]([^'"]+)['"]/).flatten)
|
|
312
385
|
end
|
|
313
|
-
|
|
314
|
-
options
|
|
386
|
+
slugs
|
|
315
387
|
end
|
|
316
388
|
end
|
|
317
389
|
end
|
|
@@ -5,19 +5,34 @@ module CamaleonCms
|
|
|
5
5
|
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
# Only permit
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
# Only permit field values whose slug is registered under object_class. param_key selects
|
|
9
|
+
# which request param carries the payload, so sibling params (the theme form's
|
|
10
|
+
# theme_fields) get the same allowed-slugs permit as the default field_options.
|
|
11
|
+
def cama_permitted_field_options(object_class, param_key: :field_options)
|
|
12
|
+
field_options = params[param_key]
|
|
13
|
+
# A non-hash payload (a scalar `field_options=foo`, or an array) carries no groups to permit,
|
|
14
|
+
# so treat it as empty instead of calling #keys/#permit on it. Guarding here rather than
|
|
15
|
+
# `params.require` avoids a NoMethodError -> 500 an authenticated caller could trigger with a
|
|
16
|
+
# malformed param (the same crash every set_field_values caller shared).
|
|
17
|
+
return {} unless field_options.is_a?(ActionController::Parameters)
|
|
11
18
|
|
|
12
19
|
allowed_keys = cama_custom_field_allowed_slugs(object_class)
|
|
13
20
|
return {} if allowed_keys.blank?
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
field_options.permit(field_options.keys.select { |k| k.to_s =~ /\A\d+\z/ }.index_with do
|
|
22
|
+
permitted = field_options.permit(field_options.keys.select { |k| k.to_s =~ /\A\d+\z/ }.index_with do
|
|
17
23
|
allowed_keys.index_with { [:id, :group_number, { values: {} }] }
|
|
18
24
|
end).to_h
|
|
25
|
+
# Drop groups left empty after filtering. set_field_values deletes every existing value
|
|
26
|
+
# before writing, so handing it a non-blank-but-empty payload (a group whose submitted
|
|
27
|
+
# slugs were all unregistered) would wipe the object's stored values and write nothing.
|
|
28
|
+
permitted.reject { |_group, fields| fields.blank? }
|
|
19
29
|
end
|
|
20
30
|
|
|
31
|
+
# The allow-list covers fields registered directly under object_class. For a post
|
|
32
|
+
# ('PostType_Post') this intentionally spans only the post type's own groups -- not per-post
|
|
33
|
+
# ('Post') or category-inherited ('Category_Post') groups the edit form also renders. It
|
|
34
|
+
# matches PostsController#save_post_with_fields, so the main post save and drafts confine field
|
|
35
|
+
# writes identically; values for those sibling scopes are deliberately not written this way.
|
|
21
36
|
def cama_custom_field_allowed_slugs(object_class)
|
|
22
37
|
CamaleonCms::CustomField.where(
|
|
23
38
|
parent_id: CamaleonCms::CustomField.where(object_class: object_class).select(:id),
|