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
|
@@ -4,10 +4,25 @@ module CamaleonCms
|
|
|
4
4
|
flash[:error] = "Error: #{exception.message}"
|
|
5
5
|
redirect_to cama_admin_dashboard_path
|
|
6
6
|
end
|
|
7
|
-
|
|
7
|
+
# Security (scan-and-reject policy, audit M17): custom-field values save after their parent
|
|
8
|
+
# through `custom_field_values.create!`, so a value the gate refuses arrives here as
|
|
9
|
+
# RecordInvalid. Surface the refusal as a flash error naming the field instead of a 500; the
|
|
10
|
+
# parent's own attributes were already saved, only the refused value rows are rolled back.
|
|
11
|
+
rescue_from ActiveRecord::RecordInvalid do |exception|
|
|
12
|
+
raise exception unless exception.record.is_a?(CamaleonCms::CustomFieldsRelationship)
|
|
13
|
+
|
|
14
|
+
flash[:error] = exception.record.errors.full_messages.to_sentence
|
|
15
|
+
redirect_back fallback_location: cama_admin_dashboard_path
|
|
16
|
+
end
|
|
8
17
|
# layout 'camaleon_cms/admin'
|
|
18
|
+
# Admin responses are user-specific and must never be served from a browser or shared cache;
|
|
19
|
+
# without this a stale render (e.g. an editor that failed to initialize on a cold boot) can
|
|
20
|
+
# reappear after a restart until a manual reload. Set first so it applies to auth redirects too.
|
|
21
|
+
before_action :cama_prevent_response_caching
|
|
9
22
|
before_action :cama_authenticate
|
|
23
|
+
before_action :keep_request_attrs
|
|
10
24
|
before_action :admin_init_actions
|
|
25
|
+
before_action :enforce_password_change
|
|
11
26
|
before_action :admin_logged_actions
|
|
12
27
|
before_action :admin_before_hooks
|
|
13
28
|
after_action :admin_after_hooks
|
|
@@ -15,6 +30,7 @@ module CamaleonCms
|
|
|
15
30
|
params[:cama_ajax_request].present? ? 'camaleon_cms/admin/_ajax' : 'camaleon_cms/admin'
|
|
16
31
|
}
|
|
17
32
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.dashboard', default: 'Dashboard'), :cama_admin_path
|
|
33
|
+
helper_method :cama_get_i18n_frontend
|
|
18
34
|
|
|
19
35
|
# render admin dashboard
|
|
20
36
|
def index
|
|
@@ -41,55 +57,157 @@ module CamaleonCms
|
|
|
41
57
|
# render search results
|
|
42
58
|
# receive params[:q]
|
|
43
59
|
# receive params[:kind]: define de type of the results type (content|category|tag) => default content
|
|
44
|
-
# if this is
|
|
60
|
+
# if this is receiving a param[:ajax], then will render only results view
|
|
45
61
|
def search
|
|
46
62
|
add_breadcrumb I18n.t('camaleon_cms.admin.button.search')
|
|
47
|
-
params[:kind] = 'content'
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
params[:kind] = 'content' if params[:kind].blank?
|
|
64
|
+
# A crafted ?q[]=x / ?q[a]=b arrives as an Array / Parameters, which has no #downcase; treat any
|
|
65
|
+
# non-String q as an empty query instead of 500ing.
|
|
66
|
+
params[:q] = params[:q].is_a?(String) ? params[:q].downcase : ''
|
|
67
|
+
# Security (audit 2026-08-11 M12): the action had no authorization and no status filter, so any
|
|
68
|
+
# admin-area user (e.g. a client with no content rights) could enumerate every post title/slug in
|
|
69
|
+
# every status -- draft, pending, private, trash -- plus post types, categories and tags they
|
|
70
|
+
# cannot access. Scope every kind to the post types the caller may manage: content and post types
|
|
71
|
+
# by :posts, categories and tags by their own :categories / :post_tags abilities (the roles UI
|
|
72
|
+
# grants manage_categories / manage_tags independently of any post edit right).
|
|
73
|
+
table_name = case params[:kind]
|
|
74
|
+
when 'post_type'
|
|
75
|
+
base_query = current_site.post_types.where(id: cama_admin_searchable_post_type_ids)
|
|
76
|
+
Cama::PostType.table_name
|
|
77
|
+
when 'category'
|
|
78
|
+
pt_ids = cama_admin_searchable_post_type_ids(:categories)
|
|
79
|
+
# A category's post type lives in post_type_id (the status column) at every nesting
|
|
80
|
+
# level; parent_id points at the parent *category* for children, so filtering by it
|
|
81
|
+
# would drop every nested category.
|
|
82
|
+
base_query = current_site.full_categories.where(post_type_id: pt_ids)
|
|
83
|
+
Cama::Category.table_name
|
|
84
|
+
when 'tag'
|
|
85
|
+
pt_ids = cama_admin_searchable_post_type_ids(:post_tags)
|
|
86
|
+
base_query = current_site.post_tags.where(parent_id: pt_ids)
|
|
87
|
+
Cama::PostTag.table_name
|
|
88
|
+
else
|
|
89
|
+
base_query = cama_admin_searchable_posts
|
|
90
|
+
Cama::Post.table_name
|
|
91
|
+
end
|
|
92
|
+
@items = base_query.where(
|
|
93
|
+
items_sql_by_name(table_name), "%#{params[:q]}%", "%#{params[:q]}%", "%#{params[:q]}%"
|
|
94
|
+
)
|
|
95
|
+
|
|
67
96
|
@items = @items.paginate(page: params[:page], per_page: current_site.admin_per_page)
|
|
68
97
|
end
|
|
69
98
|
|
|
99
|
+
# Decorators use this helper while rendering admin pages to keep public URLs in
|
|
100
|
+
# the site's frontend language instead of the admin interface language.
|
|
101
|
+
def cama_get_i18n_frontend
|
|
102
|
+
@cama_i18n_frontend
|
|
103
|
+
end
|
|
104
|
+
|
|
70
105
|
private
|
|
71
106
|
|
|
107
|
+
# Send `Cache-Control: no-store` on every admin response so no admin page is ever cached by the
|
|
108
|
+
# browser or an intermediary. Admin pages are per-user and dynamic; caching them risks a stale
|
|
109
|
+
# render being reused (the cold-boot blank-editor symptom is one such case).
|
|
110
|
+
def cama_prevent_response_caching
|
|
111
|
+
response.headers['Cache-Control'] = 'no-store'
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Actions reachable while an administrator still owes a password change: the profile screen, its
|
|
115
|
+
# submit, and the AJAX password-change endpoint. Everything else in the admin panel is redirected
|
|
116
|
+
# to the change-password screen until the marker is cleared. Sign-out lives on SessionsController
|
|
117
|
+
# (not an AdminController subclass), so it is out of this gate's reach and always reachable.
|
|
118
|
+
PASSWORD_CHANGE_EXEMPT = { 'camaleon_cms/admin/users' => %w[profile profile_edit update updated_ajax] }.freeze
|
|
119
|
+
|
|
120
|
+
# Force a newly provisioned administrator (minted with a generated password, see
|
|
121
|
+
# harden-installer-default-admin) to set their own password before using the admin panel.
|
|
122
|
+
def enforce_password_change
|
|
123
|
+
return if cama_current_user.blank?
|
|
124
|
+
return if cama_current_user.get_meta('must_change_password').blank?
|
|
125
|
+
return if PASSWORD_CHANGE_EXEMPT[controller_path]&.include?(action_name)
|
|
126
|
+
|
|
127
|
+
flash[:alert] = t('camaleon_cms.admin.users.message.must_change_password',
|
|
128
|
+
default: 'Please choose a new password before continuing.')
|
|
129
|
+
redirect_to cama_admin_profile_edit_path
|
|
130
|
+
end
|
|
131
|
+
|
|
72
132
|
# initialize all vars and methods for admin panel
|
|
73
133
|
def admin_init_actions
|
|
74
134
|
I18n.locale = current_site.get_admin_language
|
|
75
|
-
@_admin_menus
|
|
135
|
+
# Alias the legacy @_admin_menus ivar onto the live menu store so a hook using the WordPress-style
|
|
136
|
+
# `@_admin_menus.delete('comments')` removal idiom mutates the same hash admin_menu_draw reads. The
|
|
137
|
+
# menu-insert methods mutate in place (.replace), so this reference stays valid. Regression M19.
|
|
138
|
+
@_admin_menus = CurrentRequest.admin_menu_items = {}
|
|
76
139
|
@_admin_breadcrumb = []
|
|
77
140
|
@_extra_models_for_fields = []
|
|
141
|
+
# Cache the site's frontend language for decorators and plugins that still
|
|
142
|
+
# need to distinguish admin requests from frontend visitor requests.
|
|
78
143
|
@cama_i18n_frontend = current_site.get_languages.first
|
|
79
144
|
end
|
|
80
145
|
|
|
81
146
|
# trigger hooks for admin panel before admin load
|
|
82
147
|
def admin_before_hooks
|
|
83
|
-
|
|
148
|
+
run_hook_lifecycle('admin_before_load')
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Set cama_current_user and current_site in CurrentRequest so models can access the current context.
|
|
152
|
+
# CurrentRequest is an ActiveSupport::CurrentAttributes subclass that auto-resets per request.
|
|
153
|
+
def keep_request_attrs
|
|
154
|
+
CurrentRequest.user = cama_current_user
|
|
155
|
+
CurrentRequest.site = current_site
|
|
84
156
|
end
|
|
85
157
|
|
|
86
158
|
# trigger hooks for admin panel after admin load
|
|
87
159
|
def admin_after_hooks
|
|
88
|
-
|
|
160
|
+
run_hook_lifecycle('admin_after_load')
|
|
89
161
|
end
|
|
90
162
|
|
|
91
163
|
def admin_logged_actions
|
|
92
|
-
admin_menus_add_commons if !request.xhr? ||
|
|
164
|
+
admin_menus_add_commons if !request.xhr? || params[:cama_ajax_request].blank? # initialize admin sidebar menus
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Post types on which the caller holds `action`: :posts for content and the post-type kind,
|
|
168
|
+
# :categories / :post_tags for the taxonomy kinds. Admins hold every ability on every type, so
|
|
169
|
+
# skip the per-record sweep (and the redundant IN filter it would feed) for them.
|
|
170
|
+
def cama_admin_searchable_post_type_ids(action = :posts)
|
|
171
|
+
return current_site.post_types.ids if can?(:manage, :all)
|
|
172
|
+
|
|
173
|
+
cama_admin_searchable_post_types(action).map(&:id)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def cama_admin_searchable_post_types(action)
|
|
177
|
+
current_site.post_types.select { |pt| can?(action, pt) }
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Posts the caller may see in admin search: the accessible post types narrowed by the shared
|
|
181
|
+
# admin visibility rule. edit_other implies :posts, so the visibility sweep only rechecks the
|
|
182
|
+
# already-loaded listable subset; admins skip the scoping entirely.
|
|
183
|
+
def cama_admin_searchable_posts
|
|
184
|
+
return current_site.posts if can?(:manage, :all)
|
|
185
|
+
|
|
186
|
+
listable = cama_admin_searchable_post_types(:posts)
|
|
187
|
+
cama_admin_visible_posts(current_site.posts.where(post_type_id: listable.map(&:id)), listable)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Restrict `scope` to the posts the caller may see in admin listings across `post_types`: every
|
|
191
|
+
# post on the types where they hold edit_other, only their own elsewhere. Admins see everything.
|
|
192
|
+
# Single source of the visibility rule for PostsController#index (per post type) and admin search
|
|
193
|
+
# (across types) -- these drifting apart is how the M12 disclosure shipped, so change it here, not
|
|
194
|
+
# at a call site.
|
|
195
|
+
def cama_admin_visible_posts(scope, post_types)
|
|
196
|
+
return scope if can?(:manage, :all)
|
|
197
|
+
|
|
198
|
+
edit_other_ids = post_types.select { |pt| can?(:edit_other, pt) }.map(&:id)
|
|
199
|
+
return scope if edit_other_ids.length == post_types.length
|
|
200
|
+
|
|
201
|
+
scope.where(post_type_id: edit_other_ids).or(scope.where(user_id: cama_current_user.id))
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def items_sql_by_name(table_name)
|
|
205
|
+
lower_name = "LOWER(#{table_name}"
|
|
206
|
+
if table_name == Cama::Post.table_name
|
|
207
|
+
return "#{lower_name}.title) LIKE ? OR #{lower_name}.slug) LIKE ? OR #{lower_name}.content_filtered) LIKE ?"
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
"#{lower_name}.name) LIKE ? OR #{lower_name}.slug) LIKE ? OR #{lower_name}.description) LIKE ?"
|
|
93
211
|
end
|
|
94
212
|
end
|
|
95
213
|
end
|
|
@@ -2,6 +2,7 @@ module CamaleonCms
|
|
|
2
2
|
module Apps
|
|
3
3
|
class PluginsAdminController < CamaleonCms::AdminController
|
|
4
4
|
before_action :init_plugin
|
|
5
|
+
before_action :authorize_plugin
|
|
5
6
|
|
|
6
7
|
private
|
|
7
8
|
|
|
@@ -21,6 +22,10 @@ module CamaleonCms
|
|
|
21
22
|
lookup_context.prefixes.prepend(params[:controller].sub("plugins/#{plugin_name}",
|
|
22
23
|
"plugins/#{plugin_name}/views"))
|
|
23
24
|
end
|
|
25
|
+
|
|
26
|
+
def authorize_plugin
|
|
27
|
+
authorize! :manage, :plugins
|
|
28
|
+
end
|
|
24
29
|
end
|
|
25
30
|
end
|
|
26
31
|
end
|
|
@@ -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
|