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
|
@@ -4,31 +4,15 @@ module CamaleonCms
|
|
|
4
4
|
# verify if current user is logged in, if not, then return nil
|
|
5
5
|
# return html link
|
|
6
6
|
def cama_edit_link(url, title = nil, attrs = {})
|
|
7
|
-
return ''
|
|
7
|
+
return '' if cama_current_user.blank?
|
|
8
8
|
return '' unless cama_current_user.admin?
|
|
9
9
|
|
|
10
10
|
attrs = { target: '_blank', style: 'font-size:11px !important;cursor:pointer;' }.merge(attrs)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def cama_requestAction(controller, action, params = {})
|
|
17
|
-
controller.class_eval do
|
|
18
|
-
def params=(params)
|
|
19
|
-
@params = params
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def params
|
|
23
|
-
@params
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
c = controller.new
|
|
27
|
-
c.request = @_request
|
|
28
|
-
c.response = @_response
|
|
29
|
-
c.params = params
|
|
30
|
-
c.send(action)
|
|
31
|
-
c.response.body
|
|
11
|
+
link_to(
|
|
12
|
+
safe_join(['→ ', title || ct('edit', default: 'Edit')]),
|
|
13
|
+
url,
|
|
14
|
+
attrs
|
|
15
|
+
)
|
|
32
16
|
end
|
|
33
17
|
|
|
34
18
|
# theme common translation text
|
|
@@ -44,26 +28,37 @@ module CamaleonCms
|
|
|
44
28
|
I18n.translate("camaleon_cms.common.#{key}", **args)
|
|
45
29
|
end
|
|
46
30
|
|
|
47
|
-
#
|
|
31
|
+
# Public compatibility API for plugins such as camaleon-ecommerce, which use
|
|
32
|
+
# this predicate to switch from visitor pricing/locale behavior to admin mode.
|
|
33
|
+
# This still matters while previewing frontend content from admin, for example
|
|
34
|
+
# a theme preview that renders ecommerce helpers before the request reaches the
|
|
35
|
+
# public frontend controller stack.
|
|
48
36
|
def cama_is_admin_request?
|
|
49
|
-
|
|
37
|
+
respond_to?(:cama_get_i18n_frontend) && cama_get_i18n_frontend.present?
|
|
50
38
|
end
|
|
51
39
|
|
|
52
40
|
# generate loop categories html sitemap links
|
|
53
41
|
# this is a helper for sitemap generator to print categories, sub categories and post contents in html list format
|
|
54
|
-
def cama_sitemap_cats_generator(cats)
|
|
42
|
+
def cama_sitemap_cats_generator(cats, skip_config = {})
|
|
43
|
+
skip_config = {} unless skip_config.is_a?(Hash)
|
|
44
|
+
# merge! alone is not enough: an on_render_sitemap hook that sets one of these keys to nil
|
|
45
|
+
# overwrites the default with nil and brings back the very nil.include? crash the defaults
|
|
46
|
+
# exist to prevent. compact drops those before they win.
|
|
47
|
+
skip_config = { skip_cat_ids: [], skip_post_ids: [] }.merge!(skip_config.compact)
|
|
55
48
|
res = []
|
|
56
49
|
cats.decorate.each do |cat|
|
|
57
|
-
next if
|
|
50
|
+
next if skip_config[:skip_cat_ids].include?(cat.id)
|
|
58
51
|
|
|
59
52
|
res_posts = []
|
|
60
53
|
cat.the_posts.decorate.each do |post|
|
|
61
|
-
next if
|
|
54
|
+
next if skip_config[:skip_post_ids].include?(post.id)
|
|
62
55
|
|
|
63
|
-
|
|
56
|
+
post_href = ERB::Util.html_escape(post.the_url)
|
|
57
|
+
res_posts << "<li><a href='#{post_href}'>#{post.the_title}</a></li>"
|
|
64
58
|
end
|
|
65
|
-
|
|
66
|
-
res <<
|
|
59
|
+
cat_href = ERB::Util.html_escape(cat.the_url)
|
|
60
|
+
res << "<li><h4><a href='#{cat_href}'>#{cat.the_title}</a></h4><ul>#{res_posts.join('')}</ul></li>"
|
|
61
|
+
res << cama_sitemap_cats_generator(cat.the_categories, skip_config)
|
|
67
62
|
end
|
|
68
63
|
res.join('')
|
|
69
64
|
end
|
|
@@ -79,22 +74,19 @@ module CamaleonCms
|
|
|
79
74
|
cache
|
|
80
75
|
end
|
|
81
76
|
|
|
82
|
-
# =Deprecated
|
|
83
|
-
def cama_draw_timer
|
|
84
|
-
@_cama_timer ||= Time.current
|
|
85
|
-
puts "***************************************** timer: #{((Time.current - @_cama_timer) * 24 * 60 * 60).to_i} (#{caller.first})"
|
|
86
|
-
@_cama_timer = Time.current
|
|
87
|
-
end
|
|
88
|
-
|
|
89
77
|
# return normal translation with default value with translation of english
|
|
90
78
|
def cama_t(key, args = {})
|
|
91
|
-
args[:default] = I18n.t(key, **args.dup.merge(locale: :en))
|
|
79
|
+
args[:default] = I18n.t(key, **args.dup.merge(locale: :en)) if args[:default].blank?
|
|
92
80
|
I18n.t(key, **args)
|
|
93
81
|
end
|
|
94
82
|
|
|
95
83
|
# function that converts string into plural format
|
|
84
|
+
# `SafeBuffer#pluralize` returns a plain String, so callers composing the result with other
|
|
85
|
+
# markup lose the safe flag before they can use it. The safeness of the input is propagated --
|
|
86
|
+
# never added, so an unsafe input stays unsafe and no caller becomes less safe than before.
|
|
96
87
|
def cama_pluralize_text(text)
|
|
97
|
-
text.try(:pluralize)
|
|
88
|
+
res = text.try(:pluralize)
|
|
89
|
+
text.try(:html_safe?) ? res.try(:html_safe) : res
|
|
98
90
|
end
|
|
99
91
|
end
|
|
100
92
|
end
|
|
@@ -1,78 +1,112 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
module CaptchaHelper
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
text = cama_rand_str(len)
|
|
9
|
-
session[:cama_captcha] = [] unless session[:cama_captcha].present?
|
|
10
|
-
session[:cama_captcha] << text
|
|
11
|
-
img.combine_options do |c|
|
|
12
|
-
c.gravity 'Center'
|
|
13
|
-
c.fill('#FFFFFF')
|
|
14
|
-
c.draw "text 0,5 #{text}"
|
|
15
|
-
c.font File.join($camaleon_engine_dir.present? ? $camaleon_engine_dir : Rails.root.to_s, 'lib', 'captcha',
|
|
16
|
-
'bumpyroad.ttf')
|
|
17
|
-
c.pointsize '30'
|
|
18
|
-
end
|
|
19
|
-
img
|
|
20
|
-
end
|
|
3
|
+
# Challenge/image generation (including the length clamp and its constants) lives in
|
|
4
|
+
# CamaleonCms::CaptchaImageGeneration, shared with the controller-stack
|
|
5
|
+
# CamaleonCms::RuntimeCaptchaImageConcern; verification and the under-attack counters
|
|
6
|
+
# below exist only here.
|
|
7
|
+
include CamaleonCms::CaptchaImageGeneration
|
|
21
8
|
|
|
22
9
|
# build a captcha tag (image with captcha)
|
|
23
10
|
# img_args: attributes for image_tag
|
|
24
11
|
# input_args: attributes for input field
|
|
25
12
|
def cama_captcha_tag(len = 5, img_args = { alt: '' }, input_args = {}, bootstrap_group_mode = false)
|
|
26
|
-
|
|
13
|
+
# Symbolize so string-keyed args work: the :placeholder / :style reads below use Symbol keys,
|
|
14
|
+
# so a caller's String key would be missed — the default placeholder would then be added under
|
|
15
|
+
# the Symbol key and render as a duplicate attribute.
|
|
16
|
+
img_args = img_args.to_h.symbolize_keys
|
|
17
|
+
input_args = input_args.to_h.symbolize_keys
|
|
18
|
+
if input_args[:placeholder].blank?
|
|
27
19
|
input_args[:placeholder] =
|
|
28
|
-
I18n.t('camaleon_cms.captcha_placeholder',
|
|
29
|
-
default: 'Please enter the text of the image')
|
|
20
|
+
I18n.t('camaleon_cms.captcha_placeholder', default: 'Please enter the text of the image')
|
|
30
21
|
end
|
|
31
|
-
img_args[
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
img_args[:onclick] = "this.src = \"#{cama_captcha_url(len: len)}\"+\"&t=\"+(new Date().getTime());"
|
|
23
|
+
# Keep a caller-supplied style; the pointer cursor is required for click-to-refresh, so prepend it.
|
|
24
|
+
img_args[:style] = ['cursor: pointer;', img_args[:style].presence].compact.join(' ')
|
|
25
|
+
|
|
26
|
+
helpers = ActionController::Base.helpers
|
|
27
|
+
img = helpers.image_tag(cama_captcha_url(len: len, t: Time.current.to_i), img_args)
|
|
28
|
+
input = helpers.tag(:input, type: 'text', name: 'captcha', **input_args)
|
|
29
|
+
|
|
37
30
|
if bootstrap_group_mode
|
|
38
|
-
|
|
31
|
+
span = helpers.content_tag(:span, img, class: 'input-group-btn', style: 'vertical-align: top;')
|
|
32
|
+
helpers.content_tag(:div, helpers.safe_join([span, input]), class: 'input-group input-group-captcha')
|
|
39
33
|
else
|
|
40
|
-
|
|
34
|
+
helpers.content_tag(:div, helpers.safe_join([img, input]), class: 'input-group-captcha')
|
|
41
35
|
end
|
|
42
36
|
end
|
|
43
37
|
|
|
44
|
-
# verify captcha value
|
|
38
|
+
# verify captcha value against the single active challenge and consume it on success, so a solved
|
|
39
|
+
# captcha is single-use and a blank submission can never match (H3).
|
|
45
40
|
def cama_captcha_verified?
|
|
46
|
-
|
|
41
|
+
submitted = (params[:cama_captcha] || params[:captcha]).to_s.upcase
|
|
42
|
+
return false if submitted.blank?
|
|
43
|
+
return false unless Array(session[:cama_captcha]).include?(submitted)
|
|
44
|
+
|
|
45
|
+
session.delete(:cama_captcha)
|
|
46
|
+
true
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
# ************************* captcha in attack helpers ***************************#
|
|
50
|
+
# Failed attempts are counted BOTH per session and per client IP. The per-IP counter lives in
|
|
51
|
+
# Rails.cache and is the server-side signal an attacker cannot reset by dropping the session
|
|
52
|
+
# cookie (audit finding H1); it rolls off CAMA_ATTACK_WINDOW after the last failure.
|
|
53
|
+
CAMA_ATTACK_WINDOW = 15.minutes
|
|
54
|
+
|
|
50
55
|
# check if the current visitor was submitted 5+ times
|
|
51
56
|
# key: a string to represent a url or form view
|
|
52
57
|
# key must be the same as the form "captcha_tags_if_under_attack(key, ...)"
|
|
53
58
|
def cama_captcha_under_attack?(key)
|
|
54
59
|
session["cama_captcha_#{key}"] ||= 0
|
|
55
|
-
|
|
60
|
+
max = current_site.get_option('max_try_attack', 5).to_i
|
|
61
|
+
session["cama_captcha_#{key}"].to_i > max || cama_captcha_attack_ip_count(key) > max
|
|
56
62
|
end
|
|
57
63
|
|
|
58
|
-
#
|
|
59
|
-
#
|
|
64
|
+
# per-IP failed-attempt count for this key (0 when the counter is unset). Reads raw so the
|
|
65
|
+
# value round-trips as a bare integer on Redis/Memcached (see cama_captcha_increment_attack).
|
|
66
|
+
# Assumes a request + current_site context, which the admin login flow always has.
|
|
67
|
+
def cama_captcha_attack_ip_count(key)
|
|
68
|
+
Rails.cache.read(cama_captcha_attack_ip_key(key), raw: true).to_i
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# verify the captcha only when this key is under attack; reports solely whether the
|
|
72
|
+
# current challenge was solved. The attack counter is cleared only by an explicit
|
|
73
|
+
# cama_captcha_reset_attack once the protected action itself succeeds (as the login
|
|
74
|
+
# flows do), so solving a captcha can no longer buy captcha-free attempts for an
|
|
75
|
+
# otherwise failing action. When the key is not under attack, the pending challenge
|
|
76
|
+
# is left unconsumed for whatever form it belongs to.
|
|
60
77
|
def captcha_verify_if_under_attack(key)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
78
|
+
return true unless cama_captcha_under_attack?(key)
|
|
79
|
+
|
|
80
|
+
cama_captcha_verified?
|
|
64
81
|
end
|
|
65
82
|
|
|
66
|
-
# increment attempts for key by 1
|
|
83
|
+
# increment attempts for key by 1 (both the per-session and the per-IP counter)
|
|
67
84
|
def cama_captcha_increment_attack(key)
|
|
68
85
|
session["cama_captcha_#{key}"] ||= 0
|
|
69
86
|
session["cama_captcha_#{key}"] = session["cama_captcha_#{key}"].to_i + 1
|
|
87
|
+
# Per-IP counter: an ATOMIC cache increment, not a read-then-write. Concurrent failures from
|
|
88
|
+
# one IP would otherwise race on read+write and lose updates, undercounting a burst and
|
|
89
|
+
# deferring both the captcha gate and the hard lockout. `raw: true` keeps the value a bare
|
|
90
|
+
# integer so Redis/Memcached INCR operates on it (a no-op on Memory/File stores); refreshing
|
|
91
|
+
# the TTL on every increment keeps the rolling window alive during a sustained attack.
|
|
92
|
+
cache_key = cama_captcha_attack_ip_key(key)
|
|
93
|
+
counted = Rails.cache.increment(cache_key, 1, expires_in: CAMA_ATTACK_WINDOW, raw: true)
|
|
94
|
+
# Older FileStore/MemoryStore (Rails < 7.1) return nil for a missing key instead of seeding it;
|
|
95
|
+
# seed it then. This one-time seed's own race is harmless (count 1 vs 2 is far from any
|
|
96
|
+
# threshold) and every subsequent increment is atomic.
|
|
97
|
+
Rails.cache.write(cache_key, 1, expires_in: CAMA_ATTACK_WINDOW, raw: true) if counted.nil?
|
|
70
98
|
end
|
|
71
99
|
|
|
72
|
-
# reset the attacks counter for key
|
|
100
|
+
# reset the attacks counter for key (both the per-session and the per-IP counter)
|
|
73
101
|
# key: a string to represent a url or form view
|
|
74
102
|
def cama_captcha_reset_attack(key)
|
|
75
103
|
session["cama_captcha_#{key}"] = 0
|
|
104
|
+
Rails.cache.delete(cama_captcha_attack_ip_key(key))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# cache key for the per-IP attack counter, scoped to the site and the form key
|
|
108
|
+
def cama_captcha_attack_ip_key(key)
|
|
109
|
+
"cama_captcha_attack:#{current_site.id}:#{request.remote_ip}:#{key}"
|
|
76
110
|
end
|
|
77
111
|
|
|
78
112
|
# return a number of attempts for key
|
|
@@ -86,19 +120,5 @@ module CamaleonCms
|
|
|
86
120
|
def cama_captcha_tags_if_under_attack(key, captcha_parmas = [5, {}, { class: 'form-control required' }])
|
|
87
121
|
cama_captcha_tag(*captcha_parmas) if cama_captcha_under_attack?(key)
|
|
88
122
|
end
|
|
89
|
-
|
|
90
|
-
private
|
|
91
|
-
|
|
92
|
-
# generate random string for captcha
|
|
93
|
-
# len: length of characters, default 6
|
|
94
|
-
def cama_rand_str(len = 6)
|
|
95
|
-
alphabets = [('A'..'Z').to_a].flatten!
|
|
96
|
-
alphanumerics = [('A'..'Z').to_a, ('1'..'9').to_a].flatten!
|
|
97
|
-
str = alphabets[rand(alphabets.size)]
|
|
98
|
-
(len.to_i - 1).times do
|
|
99
|
-
str << alphanumerics[rand(alphanumerics.size)]
|
|
100
|
-
end
|
|
101
|
-
str
|
|
102
|
-
end
|
|
103
123
|
end
|
|
104
124
|
end
|
|
@@ -17,7 +17,10 @@ module CamaleonCms
|
|
|
17
17
|
|
|
18
18
|
# render as html content all comments recursively
|
|
19
19
|
# comments: collection of comments
|
|
20
|
-
def cama_comments_render_html(comments)
|
|
20
|
+
def cama_comments_render_html(comments, post_id = nil)
|
|
21
|
+
if post_id.nil? && respond_to?(:controller) && controller.respond_to?(:instance_variable_get)
|
|
22
|
+
post_id = controller.instance_variable_get(:@post)&.id
|
|
23
|
+
end
|
|
21
24
|
comments.decorate.map do |comment|
|
|
22
25
|
author = comment.the_author
|
|
23
26
|
content_tag(:div, class: 'media') do
|
|
@@ -31,7 +34,10 @@ module CamaleonCms
|
|
|
31
34
|
[
|
|
32
35
|
content_tag(:h4, class: 'media-heading') do
|
|
33
36
|
[
|
|
34
|
-
author
|
|
37
|
+
# Escape the author name: User first_name/last_name are no longer sanitized at
|
|
38
|
+
# save time and this heading is marked html_safe below, so a commenter-controlled
|
|
39
|
+
# name would otherwise be injected as raw HTML into the moderator's page.
|
|
40
|
+
ERB::Util.html_escape(author.the_name),
|
|
35
41
|
' ',
|
|
36
42
|
content_tag(:small, comment.the_created_at),
|
|
37
43
|
' ',
|
|
@@ -45,7 +51,7 @@ module CamaleonCms
|
|
|
45
51
|
content_tag(:div, class: 'pull-left') do
|
|
46
52
|
[
|
|
47
53
|
link_to(
|
|
48
|
-
cama_admin_post_comment_answer_path(
|
|
54
|
+
cama_admin_post_comment_answer_path(post_id, comment.id),
|
|
49
55
|
data: { comment_id: comment.id },
|
|
50
56
|
title: t('camaleon_cms.admin.comments.tooltip.reply_comment'),
|
|
51
57
|
class: 'btn btn-info reply btn-xs ajax_modal'
|
|
@@ -64,20 +70,26 @@ module CamaleonCms
|
|
|
64
70
|
[
|
|
65
71
|
link_to(
|
|
66
72
|
url_for({ action: :toggle_status, comment_id: comment.id, s: 'a' }),
|
|
73
|
+
method: :patch,
|
|
67
74
|
title: t('camaleon_cms.admin.comments.tooltip.approved_comment'),
|
|
68
|
-
class: "#{comment.approved == 'approved' ? 'hidden' : ''}
|
|
75
|
+
class: "#{comment.approved == 'approved' ? 'hidden' : ''} " \
|
|
76
|
+
'btn btn-success approve btn-xs cama_ajax_request'
|
|
69
77
|
) { content_tag(:span, '', class: 'fa fa-thumbs-o-up') },
|
|
70
78
|
' ',
|
|
71
79
|
link_to(
|
|
72
80
|
url_for({ action: :toggle_status, comment_id: comment.id, s: 'p' }),
|
|
81
|
+
method: :patch,
|
|
73
82
|
title: t('camaleon_cms.admin.comments.tooltip.comment_pending'),
|
|
74
|
-
class: "#{comment.approved == 'pending' ? 'hidden' : ''}
|
|
83
|
+
class: "#{comment.approved == 'pending' ? 'hidden' : ''} " \
|
|
84
|
+
'btn btn-primary pending btn-xs cama_ajax_request'
|
|
75
85
|
) { content_tag(:span, '', class: 'fa fa-warning') },
|
|
76
86
|
' ',
|
|
77
87
|
link_to(
|
|
78
88
|
url_for({ action: :toggle_status, comment_id: comment.id, s: 's' }),
|
|
89
|
+
method: :patch,
|
|
79
90
|
title: t('camaleon_cms.admin.comments.tooltip.comment_spam'),
|
|
80
|
-
class: "#{comment.approved == 'spam' ? 'hidden' : ''}
|
|
91
|
+
class: "#{comment.approved == 'spam' ? 'hidden' : ''} " \
|
|
92
|
+
'btn btn-danger spam btn-xs cama_ajax_request'
|
|
81
93
|
) { content_tag(:span, '', class: 'fa fa-bug') }
|
|
82
94
|
].join.html_safe
|
|
83
95
|
end
|
|
@@ -85,7 +97,7 @@ module CamaleonCms
|
|
|
85
97
|
end,
|
|
86
98
|
content_tag(:hr),
|
|
87
99
|
content_tag(:div, '', class: 'clearfix'),
|
|
88
|
-
cama_comments_render_html(comment.children)
|
|
100
|
+
cama_comments_render_html(comment.children, post_id)
|
|
89
101
|
].join.html_safe
|
|
90
102
|
end
|
|
91
103
|
].join.html_safe
|
|
@@ -2,34 +2,41 @@ module CamaleonCms
|
|
|
2
2
|
module ContentHelper
|
|
3
3
|
# initialize content variables
|
|
4
4
|
def cama_content_init
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
state = cama_content_state
|
|
6
|
+
state[:before_content] = []
|
|
7
|
+
state[:after_content] = []
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
# prepend content for admin or frontend (after <body>)
|
|
10
11
|
# sample: cama_content_prepend(<div>my prepend content</div>)
|
|
11
12
|
def cama_content_prepend(content)
|
|
12
|
-
|
|
13
|
+
cama_content_state[:before_content] << content
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
# append content for admin or frontend (before </body>)
|
|
16
17
|
# sample: cama_content_prepend(<div>my after content</div>)
|
|
17
18
|
def cama_content_append(content)
|
|
18
|
-
|
|
19
|
+
cama_content_state[:after_content] << content
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
# draw all before contents registered by cama_content_prepend
|
|
22
23
|
def cama_content_before_draw
|
|
23
|
-
|
|
24
|
+
cama_content_state[:before_content].join('')
|
|
24
25
|
rescue StandardError
|
|
25
26
|
''
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
# draw all after contents registered by cama_content_append
|
|
29
30
|
def cama_content_after_draw
|
|
30
|
-
|
|
31
|
+
cama_content_state[:after_content].join('')
|
|
31
32
|
rescue StandardError
|
|
32
33
|
''
|
|
33
34
|
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def cama_content_state
|
|
39
|
+
CurrentRequest.content_helper_state ||= { before_content: [], after_content: [] }
|
|
40
|
+
end
|
|
34
41
|
end
|
|
35
42
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
module EmailHelper
|
|
3
3
|
include CamaleonCms::HooksHelper
|
|
4
|
-
# send
|
|
4
|
+
# send an email
|
|
5
5
|
# email: email to
|
|
6
6
|
# subject: Subject of the email
|
|
7
7
|
# content: content of the email
|
|
@@ -20,7 +20,8 @@ module CamaleonCms
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# short method of send_email
|
|
23
|
-
# args: content='', from=nil, attachs=[], url_base='', current_site, template_name, layout_name, extra_data,
|
|
23
|
+
# args: content='', from=nil, attachs=[], url_base='', current_site, template_name, layout_name, extra_data,
|
|
24
|
+
# format, cc_to
|
|
24
25
|
def cama_send_email(email_to, subject, args = {})
|
|
25
26
|
args = { url_base: cama_root_url, current_site: current_site, subject: subject }.merge(args)
|
|
26
27
|
args[:attachments] = args[:attachs] if args[:attachs].present?
|
|
@@ -42,7 +43,10 @@ module CamaleonCms
|
|
|
42
43
|
|
|
43
44
|
def send_password_reset_email(user_to_send)
|
|
44
45
|
user_to_send.send_password_reset
|
|
45
|
-
|
|
46
|
+
# Read the column, not the same-named method has_secure_password generates on Rails 7.1+ (which
|
|
47
|
+
# shadows it with a signed token the controller's column lookup never matches). read_attribute
|
|
48
|
+
# returns the stored column on every supported Rails version (6.1–8.1).
|
|
49
|
+
reset_url = cama_admin_forgot_url({ h: user_to_send.read_attribute(:password_reset_token) })
|
|
46
50
|
extra_data = {
|
|
47
51
|
url: reset_url,
|
|
48
52
|
fullname: user_to_send.fullname,
|
|
@@ -8,8 +8,13 @@ module CamaleonCms
|
|
|
8
8
|
|
|
9
9
|
# add where conditionals to filter private/hidden/expired/drafts/unpublished
|
|
10
10
|
# note: only for post records
|
|
11
|
-
|
|
11
|
+
# eager: collection listings (the_posts/the_contents) preload metas, categories, post_tags and
|
|
12
|
+
# the post-type metas up front to avoid N+1. Single-record lookups (the_post, render_post,
|
|
13
|
+
# the_next_post/the_prev_post) pass eager: false so a one-row find is not charged the listing
|
|
14
|
+
# preloads it never reads.
|
|
15
|
+
def verify_front_visibility(active_record, eager: true)
|
|
12
16
|
active_record = active_record.visible_frontend
|
|
17
|
+
active_record = active_record.with_eager if eager
|
|
13
18
|
r = { active_record: active_record }
|
|
14
19
|
hooks_run('filter_post', r)
|
|
15
20
|
r[:active_record]
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
# Camaleon CMS is a content management system
|
|
2
2
|
# Copyright (C) 2015 by Owen Peredo Diaz
|
|
3
3
|
# Email: owenperedo@gmail.com
|
|
4
|
-
# This program is free software: you can redistribute it and/or modify
|
|
5
|
-
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify it under the terms of the
|
|
5
|
+
# GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License,
|
|
6
|
+
# or (at your option) any later version.
|
|
7
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
|
8
|
+
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
6
9
|
# See the GNU Affero General Public License (GPLv3) for more details.
|
|
7
10
|
module CamaleonCms
|
|
8
11
|
module Frontend
|
|
9
12
|
module ContentSelectHelper
|
|
10
|
-
# select single post of post type
|
|
13
|
+
# select a single post of post type
|
|
11
14
|
# the_post_type('post') do
|
|
12
15
|
# the_post('first-blog-post')
|
|
13
16
|
# end
|
|
14
17
|
def the_post(slug)
|
|
15
|
-
post =
|
|
16
|
-
process_in_block(post)
|
|
17
|
-
yield(post) if block_given?
|
|
18
|
-
end
|
|
18
|
+
post = camaleon_frontend_object.the_post(slug)
|
|
19
|
+
process_in_block(post) { yield(post) if block_given? }
|
|
19
20
|
post
|
|
20
21
|
end
|
|
21
22
|
|
|
@@ -28,7 +29,7 @@ module CamaleonCms
|
|
|
28
29
|
# the_posts(limit: 10)
|
|
29
30
|
# end
|
|
30
31
|
def the_posts(options = {})
|
|
31
|
-
|
|
32
|
+
camaleon_frontend_object.posts.visible_frontend.limit(options[:limit]).decorate
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
# select post type by just pass slug to parameter
|
|
@@ -52,7 +53,7 @@ module CamaleonCms
|
|
|
52
53
|
# the_comments
|
|
53
54
|
# end
|
|
54
55
|
def the_comments(options = {})
|
|
55
|
-
|
|
56
|
+
camaleon_frontend_object.comments.limit(options[:limit]).decorate if camaleon_frontend_object.present?
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
# select title of post
|
|
@@ -60,7 +61,7 @@ module CamaleonCms
|
|
|
60
61
|
# the_title
|
|
61
62
|
# end
|
|
62
63
|
def the_title
|
|
63
|
-
|
|
64
|
+
camaleon_frontend_object&.the_title
|
|
64
65
|
end
|
|
65
66
|
|
|
66
67
|
# select content of post
|
|
@@ -68,7 +69,12 @@ module CamaleonCms
|
|
|
68
69
|
# the_content
|
|
69
70
|
# end
|
|
70
71
|
def the_content
|
|
71
|
-
|
|
72
|
+
# Security (scan-and-reject policy): no render-time transform. Stored content is gated at
|
|
73
|
+
# save (Post#reject_untrusted_dangerous_content), always equals what its author wrote, and
|
|
74
|
+
# renders verbatim — the same contract as the templates' `raw post.the_content`. The
|
|
75
|
+
# sanitize this once applied used the narrow default allowlist, so it also broke trusted
|
|
76
|
+
# authors' tables and embeds on this DSL path only.
|
|
77
|
+
camaleon_frontend_object.the_content.to_s.html_safe if camaleon_frontend_object.present? # rubocop:disable Rails/OutputSafety
|
|
72
78
|
end
|
|
73
79
|
|
|
74
80
|
# select url of post
|
|
@@ -76,7 +82,7 @@ module CamaleonCms
|
|
|
76
82
|
# the_url
|
|
77
83
|
# end
|
|
78
84
|
def the_url
|
|
79
|
-
|
|
85
|
+
camaleon_frontend_object&.the_url
|
|
80
86
|
end
|
|
81
87
|
|
|
82
88
|
# select thumbnail of post
|
|
@@ -84,15 +90,15 @@ module CamaleonCms
|
|
|
84
90
|
# the_thumbnail
|
|
85
91
|
# end
|
|
86
92
|
def the_thumbnail
|
|
87
|
-
|
|
93
|
+
camaleon_frontend_object&.the_thumb_url
|
|
88
94
|
end
|
|
89
95
|
|
|
90
|
-
# select slug of post, post type ...
|
|
96
|
+
# select slug of post, post type ...
|
|
91
97
|
# the_post('blog') do
|
|
92
98
|
# the_slug
|
|
93
99
|
# end
|
|
94
100
|
def the_slug
|
|
95
|
-
|
|
101
|
+
camaleon_frontend_object&.the_slug
|
|
96
102
|
end
|
|
97
103
|
|
|
98
104
|
# select excerpt of post
|
|
@@ -100,7 +106,7 @@ module CamaleonCms
|
|
|
100
106
|
# the_excerpt
|
|
101
107
|
# end
|
|
102
108
|
def the_excerpt(chars = 200)
|
|
103
|
-
|
|
109
|
+
camaleon_frontend_object&.the_excerpt(chars)
|
|
104
110
|
end
|
|
105
111
|
|
|
106
112
|
# select custome field from object
|
|
@@ -108,7 +114,7 @@ module CamaleonCms
|
|
|
108
114
|
# the_field('extra-content')
|
|
109
115
|
# end
|
|
110
116
|
def the_field(slug)
|
|
111
|
-
|
|
117
|
+
camaleon_frontend_object&.the_field(slug)
|
|
112
118
|
end
|
|
113
119
|
|
|
114
120
|
# loop through each post of post type
|
|
@@ -152,10 +158,18 @@ module CamaleonCms
|
|
|
152
158
|
# the_field('extra-content')
|
|
153
159
|
# end
|
|
154
160
|
def process_in_block(object)
|
|
155
|
-
temp_object =
|
|
156
|
-
|
|
161
|
+
temp_object = CurrentRequest.frontend_object
|
|
162
|
+
CurrentRequest.frontend_object = object
|
|
157
163
|
yield
|
|
158
|
-
|
|
164
|
+
ensure
|
|
165
|
+
CurrentRequest.frontend_object = temp_object
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
private
|
|
169
|
+
|
|
170
|
+
def camaleon_frontend_object
|
|
171
|
+
# Fall back to a plugin-set @object ivar; core writes CurrentRequest.frontend_object. Regression M20.
|
|
172
|
+
CurrentRequest.frontend_object || (instance_variable_get(:@object) if instance_variable_defined?(:@object))
|
|
159
173
|
end
|
|
160
174
|
end
|
|
161
175
|
end
|