camaleon_cms 2.9.2 → 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 +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 +15 -10
- data/app/apps/plugins/front_cache/config/initializer.rb +1 -1
- data/app/apps/plugins/front_cache/front_cache_helper.rb +33 -9
- 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 +76 -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 +140 -70
- 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 +66 -50
- 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 +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/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 +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 +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 +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 -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 +52 -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 +66 -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 +20 -38
- data/app/models/concerns/camaleon_cms/common_relationships.rb +18 -5
- 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 +33 -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 +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 +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 +123 -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
43
|
per_page: current_site.front_per_page).eager_load(:metas)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
81
|
per_page: current_site.front_per_page).eager_load(:metas)
|
|
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
108
|
per_page: current_site.front_per_page).eager_load(:metas)
|
|
96
|
-
|
|
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 }
|
|
@@ -186,7 +200,9 @@ module CamaleonCms
|
|
|
186
200
|
def render_post(post_or_slug_or_id, from_url = false, status = nil, force_visit = false)
|
|
187
201
|
@post = case post_or_slug_or_id
|
|
188
202
|
when String # slug
|
|
189
|
-
|
|
203
|
+
# find_by_slug is multi-language aware (matches localized slugs like
|
|
204
|
+
# "<!--:en-->sample-post<!--:-->..."), unlike find_by(slug:)
|
|
205
|
+
current_site.the_posts.find_by_slug(post_or_slug_or_id) # rubocop:disable Rails/DynamicFindBy
|
|
190
206
|
when Integer # id
|
|
191
207
|
current_site.the_posts.where(id: post_or_slug_or_id).first
|
|
192
208
|
else # model
|
|
@@ -194,15 +210,16 @@ module CamaleonCms
|
|
|
194
210
|
end
|
|
195
211
|
|
|
196
212
|
@post = @post.try(:decorate)
|
|
197
|
-
if
|
|
198
|
-
if params[:format] == 'html' ||
|
|
213
|
+
if @post.blank? || !(force_visit || @post.can_visit?)
|
|
214
|
+
if params[:format] == 'html' || params[:format].blank?
|
|
199
215
|
page_not_found
|
|
200
216
|
else
|
|
201
|
-
head
|
|
217
|
+
head :not_found
|
|
202
218
|
end
|
|
203
219
|
else
|
|
204
220
|
@object = @post
|
|
205
|
-
|
|
221
|
+
CurrentRequest.frontend_object = @object
|
|
222
|
+
mark_frontend_post_visited(@post)
|
|
206
223
|
@post_type = @post.the_post_type
|
|
207
224
|
@comments = @post.the_comments
|
|
208
225
|
@categories = @post.the_categories
|
|
@@ -213,10 +230,11 @@ module CamaleonCms
|
|
|
213
230
|
rescue StandardError
|
|
214
231
|
nil
|
|
215
232
|
end
|
|
233
|
+
post_template = @post.get_template(@post_type)
|
|
216
234
|
r_file = if lookup_context.template_exists?("page_#{@post.id}")
|
|
217
235
|
"page_#{@post.id}"
|
|
218
|
-
elsif
|
|
219
|
-
|
|
236
|
+
elsif post_template.present? && lookup_context.template_exists?(post_template)
|
|
237
|
+
post_template
|
|
220
238
|
elsif home_page.present? && @post.id.to_s == home_page
|
|
221
239
|
'index'
|
|
222
240
|
elsif lookup_context.template_exists?("post_types/#{@post_type.the_slug}/single")
|
|
@@ -261,17 +279,54 @@ module CamaleonCms
|
|
|
261
279
|
# if url hasn't a locale, then it will use default locale set on application.rb
|
|
262
280
|
def init_frontent
|
|
263
281
|
# preview theme initializing
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
282
|
+
previewing_theme = cama_sign_in? && params[:ccc_theme_preview].present? && can?(:manage, :themes)
|
|
283
|
+
if previewing_theme
|
|
284
|
+
preview_theme = current_site.themes.where(slug: params[:ccc_theme_preview]).first_or_create!.decorate
|
|
285
|
+
@_current_theme = preview_theme
|
|
286
|
+
CurrentRequest.frontend_current_theme = preview_theme
|
|
287
|
+
ensure_preview_site_defaults
|
|
288
|
+
end
|
|
267
289
|
|
|
268
290
|
@_site_options = current_site.options
|
|
269
|
-
|
|
291
|
+
# Only a scalar language param participates in locale resolution — a non-scalar
|
|
292
|
+
# (?locale[]= / ?cama_set_language[]=) would crash to_sym and 500 the request
|
|
293
|
+
set_language = params[:cama_set_language]
|
|
294
|
+
session[:cama_current_language] = set_language.to_sym if set_language.is_a?(String) && set_language.present?
|
|
270
295
|
session[:cama_current_language] = nil if current_site.get_languages.exclude?(session[:cama_current_language])
|
|
271
|
-
|
|
272
|
-
|
|
296
|
+
requested_locale = params[:locale] if params[:locale].is_a?(String)
|
|
297
|
+
I18n.locale = requested_locale || session[:cama_current_language] || current_site.get_languages.first
|
|
298
|
+
# The availability gate renders the site's 404 (directly or via a custom error_404
|
|
299
|
+
# post), so the theme lookup prefixes must already be registered when it fires
|
|
300
|
+
configure_frontend_lookup_prefixes
|
|
301
|
+
theme_init
|
|
302
|
+
return if current_site.get_languages.include?(I18n.locale.to_sym)
|
|
303
|
+
|
|
304
|
+
# The unoffered locale must not style the 404 page or leak into its links
|
|
305
|
+
I18n.locale = current_site.get_languages.first
|
|
306
|
+
page_not_found
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# initialize hooks before to execute action
|
|
310
|
+
def before_hooks
|
|
311
|
+
hooks_run('front_before_load')
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# initialize hooks after executed action
|
|
315
|
+
def after_hooks
|
|
316
|
+
hooks_run('front_after_load')
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# define default options for url helpers
|
|
320
|
+
# control for default locale
|
|
321
|
+
def default_url_options(options = {})
|
|
322
|
+
return options if current_site.get_languages.first.to_s == I18n.locale.to_s
|
|
273
323
|
|
|
274
|
-
|
|
324
|
+
{ locale: I18n.locale }.merge!(options)
|
|
325
|
+
rescue StandardError
|
|
326
|
+
options
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def configure_frontend_lookup_prefixes
|
|
275
330
|
lookup_context.prefixes.delete('frontend')
|
|
276
331
|
lookup_context.prefixes.delete('application')
|
|
277
332
|
lookup_context.prefixes.delete('camaleon_cms/frontend')
|
|
@@ -282,36 +337,51 @@ module CamaleonCms
|
|
|
282
337
|
t =~ %r{themes/(.*)/views}i || t == 'camaleon_cms/default_theme' || t == "themes/#{current_site.id}/views"
|
|
283
338
|
end
|
|
284
339
|
|
|
285
|
-
|
|
286
|
-
|
|
340
|
+
# Per-site theme override: views placed under app/apps/themes/<site_id>/views
|
|
341
|
+
# take precedence over the active theme's views (it is appended first).
|
|
342
|
+
if Dir.exist?(Rails.root.join('app', 'apps', 'themes', current_site.id.to_s).to_s)
|
|
343
|
+
lookup_context.prefixes.append("themes/#{current_site.id}/views")
|
|
344
|
+
end
|
|
287
345
|
lookup_context.prefixes.append("themes/#{current_theme.slug}/views")
|
|
288
346
|
lookup_context.prefixes.append('camaleon_cms/default_theme')
|
|
289
347
|
|
|
290
348
|
lookup_context.prefixes = lookup_context.prefixes.uniq
|
|
291
349
|
lookup_context.use_camaleon_partial_prefixes = true
|
|
292
|
-
theme_init
|
|
293
350
|
end
|
|
294
351
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
352
|
+
def ensure_preview_site_defaults
|
|
353
|
+
preview_required_menu_slugs.each do |slug|
|
|
354
|
+
current_site.nav_menus.where(slug: slug).first_or_create! do |menu|
|
|
355
|
+
menu.name = slug.to_s.humanize
|
|
356
|
+
end
|
|
357
|
+
end
|
|
298
358
|
end
|
|
299
359
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
360
|
+
def preview_required_menu_slugs
|
|
361
|
+
slugs = ['main_menu']
|
|
362
|
+
slugs.concat(extract_theme_menu_slugs(current_theme.settings['path']))
|
|
363
|
+
slugs.uniq
|
|
303
364
|
end
|
|
304
365
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
366
|
+
def extract_theme_menu_slugs(theme_path)
|
|
367
|
+
return [] if theme_path.blank?
|
|
368
|
+
|
|
369
|
+
views_path = File.join(theme_path, 'views')
|
|
370
|
+
return [] unless Dir.exist?(views_path)
|
|
371
|
+
|
|
372
|
+
Dir.glob(File.join(views_path, '**', '*.erb')).flat_map do |file_path|
|
|
373
|
+
extract_menu_slugs_from_template(File.read(file_path))
|
|
374
|
+
end.uniq
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def extract_menu_slugs_from_template(content)
|
|
378
|
+
slugs = []
|
|
379
|
+
slugs.concat(content.scan(/nav_menus\.where\(\s*slug:\s*'([^']+)'\s*\)/).flatten)
|
|
380
|
+
slugs.concat(content.scan(/nav_menus\.where\(\s*slug:\s*"([^"]+)"\s*\)/).flatten)
|
|
381
|
+
content.scan(/nav_menus\.where\(\s*slug:\s*\[([^\]]+)\]\s*\)/).flatten.each do |array_content|
|
|
382
|
+
slugs.concat(array_content.scan(/['"]([^'"]+)['"]/).flatten)
|
|
312
383
|
end
|
|
313
|
-
|
|
314
|
-
options
|
|
384
|
+
slugs
|
|
315
385
|
end
|
|
316
386
|
end
|
|
317
387
|
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),
|