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
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class TermRelationship < CamaleonRecord
|
|
3
3
|
self.table_name = "#{PluginRoutes.static_system_info['db_prefix']}term_relationships"
|
|
4
|
+
|
|
4
5
|
default_scope -> { order(term_order: :asc) }
|
|
5
6
|
|
|
6
|
-
belongs_to :term_taxonomy, inverse_of: :term_relationships,
|
|
7
|
-
belongs_to :object,
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
belongs_to :term_taxonomy, inverse_of: :term_relationships, optional: true
|
|
8
|
+
belongs_to :object, -> { order("#{CamaleonCms::Post.table_name}.id DESC") },
|
|
9
|
+
class_name: 'CamaleonCms::Post', foreign_key: :objectid, inverse_of: :term_relationships,
|
|
10
|
+
optional: true
|
|
10
11
|
|
|
11
|
-
# callbacks
|
|
12
12
|
after_create :update_count
|
|
13
13
|
before_destroy :update_count
|
|
14
14
|
|
|
15
15
|
private
|
|
16
16
|
|
|
17
|
-
#
|
|
17
|
+
# Update the counter of post-published items
|
|
18
18
|
# TODO verify counter
|
|
19
19
|
def update_count
|
|
20
|
-
|
|
20
|
+
return unless term_taxonomy&.try(:posts)
|
|
21
|
+
|
|
22
|
+
term_taxonomy
|
|
23
|
+
.update_column(:count, term_taxonomy.posts.published.size) # rubocop:disable Rails/SkipsModelValidations
|
|
21
24
|
end
|
|
22
25
|
end
|
|
23
26
|
end
|
|
@@ -14,10 +14,57 @@ module CamaleonCms
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
self.table_name = "#{PluginRoutes.static_system_info['db_prefix']}term_taxonomy"
|
|
17
|
-
# attr_accessible :taxonomy, :description, :parent_id, :count, :name, :slug, :term_group, :status, :term_order,
|
|
17
|
+
# attr_accessible :taxonomy, :description, :parent_id, :count, :name, :slug, :term_group, :status, :term_order,
|
|
18
|
+
# :user_id
|
|
18
19
|
# attr_accessible :data_options
|
|
19
20
|
# attr_accessible :data_metas
|
|
20
21
|
|
|
22
|
+
self.inheritance_column = :taxonomy
|
|
23
|
+
|
|
24
|
+
def self.sti_name
|
|
25
|
+
name.demodulize.underscore
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.polymorphic_name
|
|
29
|
+
name.demodulize
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.find_sti_class(type_name)
|
|
33
|
+
# Handle explicit exceptions where database string doesn't match namespacing layout
|
|
34
|
+
case type_name.to_s
|
|
35
|
+
when 'widget'
|
|
36
|
+
return CamaleonCms::Widget::Main
|
|
37
|
+
when 'sidebar'
|
|
38
|
+
return CamaleonCms::Widget::Sidebar
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Standard conversion for "site" -> "CamaleonCms::Site"
|
|
42
|
+
# or "nav_menu_item" -> "CamaleonCms::NavMenuItem"
|
|
43
|
+
klass = begin
|
|
44
|
+
"CamaleonCms::#{type_name.camelize}".constantize
|
|
45
|
+
rescue NameError
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
# The ancestry guard keeps a value like "meta" from instantiating an unrelated
|
|
49
|
+
# CamaleonCms class against the term_taxonomy table.
|
|
50
|
+
return klass if klass && klass <= base_class
|
|
51
|
+
|
|
52
|
+
# Runtime scan across loaded taxonomy models.
|
|
53
|
+
found = CamaleonCms::TermTaxonomy.descendants.find { |k| k.sti_name == type_name.to_s }
|
|
54
|
+
return found if found
|
|
55
|
+
|
|
56
|
+
# `super` resolves fully-qualified external classes (plugin STI outside the CamaleonCms
|
|
57
|
+
# namespace) and enforces ancestry — the scan above only sees classes already loaded, so
|
|
58
|
+
# without this an unloaded plugin taxonomy silently degraded to the base class. Same
|
|
59
|
+
# fallback PostDefault.find_sti_class keeps. Values it cannot place load as the base class,
|
|
60
|
+
# as every row did before native STI.
|
|
61
|
+
begin
|
|
62
|
+
super
|
|
63
|
+
rescue ActiveRecord::SubclassNotFound
|
|
64
|
+
base_class
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
21
68
|
# callbacks
|
|
22
69
|
before_validation :before_validating
|
|
23
70
|
before_destroy :destroy_dependencies
|
|
@@ -27,11 +74,12 @@ module CamaleonCms
|
|
|
27
74
|
validates_with CamaleonCms::UniqValidator
|
|
28
75
|
|
|
29
76
|
# relations
|
|
30
|
-
has_many :term_relationships, class_name: 'CamaleonCms::TermRelationship',
|
|
77
|
+
has_many :term_relationships, class_name: 'CamaleonCms::TermRelationship',
|
|
31
78
|
dependent: :destroy
|
|
32
79
|
# has_many :posts, foreign_key: :objectid, through: :term_relationships, :source => :objects
|
|
33
|
-
belongs_to :parent, class_name: 'CamaleonCms::TermTaxonomy',
|
|
34
|
-
belongs_to :owner, class_name: CamaManager.get_user_class_name, foreign_key: :user_id,
|
|
80
|
+
belongs_to :parent, class_name: 'CamaleonCms::TermTaxonomy', optional: true
|
|
81
|
+
belongs_to :owner, class_name: CamaManager.get_user_class_name.to_s, foreign_key: :user_id, optional: true,
|
|
82
|
+
inverse_of: false
|
|
35
83
|
|
|
36
84
|
# return all children taxonomy
|
|
37
85
|
# sample: sub categories of a category
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class Theme < CamaleonCms::TermTaxonomy
|
|
3
|
-
normalize_attrs(:
|
|
3
|
+
normalize_attrs(:description)
|
|
4
4
|
|
|
5
5
|
# attrs:
|
|
6
6
|
# slug => plugin key
|
|
7
|
-
belongs_to :site, class_name: 'CamaleonCms::Site', foreign_key: :parent_id,
|
|
8
|
-
|
|
9
|
-
default_scope { where(taxonomy: :theme) }
|
|
7
|
+
belongs_to :site, class_name: 'CamaleonCms::Site', foreign_key: :parent_id, optional: true, inverse_of: :themes
|
|
10
8
|
|
|
11
9
|
before_validation :fix_name
|
|
12
10
|
before_destroy :destroy_custom_fields
|
|
@@ -1,22 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
if PluginRoutes.static_system_info['user_model'].blank?
|
|
2
2
|
module CamaleonCms
|
|
3
3
|
class User < CamaleonRecord
|
|
4
4
|
include CamaleonCms::UserMethods
|
|
5
5
|
|
|
6
|
-
self.table_name = PluginRoutes.static_system_info['cama_users_db_table'] ||
|
|
6
|
+
self.table_name = PluginRoutes.static_system_info['cama_users_db_table'] ||
|
|
7
|
+
"#{PluginRoutes.static_system_info['db_prefix']}users"
|
|
7
8
|
|
|
8
9
|
default_scope { order(role: :asc) }
|
|
9
10
|
|
|
11
|
+
# No `dependent:` — a deleted user's widgets keep rendering; only the owner is gone.
|
|
12
|
+
has_many :widgets, class_name: 'CamaleonCms::Widget::Main', # rubocop:disable Rails/HasManyOrHasOneDependent
|
|
13
|
+
inverse_of: :owner
|
|
14
|
+
|
|
10
15
|
validates :username, presence: true
|
|
11
|
-
|
|
16
|
+
# The following might be continued wit: , :unless => Proc.new { |a| a.auth_social.present? }
|
|
17
|
+
validates :email, presence: true, format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i }
|
|
12
18
|
has_secure_password
|
|
19
|
+
# Security (audit 2026-08-11 M15): has_secure_password only validates presence (on create) and the
|
|
20
|
+
# 72-byte bcrypt maximum, so a trivially short password was accepted. Enforce a minimum length
|
|
21
|
+
# whenever a password is actually set (create, reset, change). allow_blank leaves an update that
|
|
22
|
+
# submits no password untouched (a blank field is not a change), and lets has_secure_password's own
|
|
23
|
+
# presence check own the empty-on-create case.
|
|
24
|
+
validates :password, length: { minimum: 8 }, allow_blank: true
|
|
13
25
|
|
|
14
26
|
def self.find_by_email(email)
|
|
15
|
-
|
|
27
|
+
find_by(['lower(email) = ?', email.to_s.downcase])
|
|
16
28
|
end
|
|
17
29
|
|
|
18
30
|
def self.find_by_username(username)
|
|
19
|
-
|
|
31
|
+
find_by(['lower(username) = ?', username.to_s.downcase])
|
|
20
32
|
end
|
|
21
33
|
end
|
|
22
34
|
end
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class UserRole < CamaleonCms::TermTaxonomy
|
|
3
|
-
normalize_attrs(:
|
|
3
|
+
normalize_attrs(:description)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
def self.sti_name
|
|
6
|
+
'user_roles'
|
|
7
|
+
end
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
after_destroy :set_users_as_cilent
|
|
8
10
|
|
|
9
|
-
belongs_to :site, class_name: 'CamaleonCms::Site', foreign_key: :parent_id,
|
|
11
|
+
belongs_to :site, class_name: 'CamaleonCms::Site', foreign_key: :parent_id, optional: true,
|
|
12
|
+
inverse_of: :user_roles_rel
|
|
10
13
|
|
|
11
14
|
def roles_post_type
|
|
12
15
|
get_meta('_post_type')
|
|
@@ -20,6 +23,23 @@ module CamaleonCms
|
|
|
20
23
|
term_group.nil?
|
|
21
24
|
end
|
|
22
25
|
|
|
26
|
+
# Users in a role slugged `admin` are administrators: `User#admin?` tests exactly this, and
|
|
27
|
+
# `Ability#initialize` answers `can :manage, :all` for them and returns before this role's
|
|
28
|
+
# `_post_type_`/`_manager_` metas are read. Those metas therefore describe nothing — every
|
|
29
|
+
# permission is held whatever they say.
|
|
30
|
+
def grants_all_permissions?
|
|
31
|
+
slug == 'admin'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Whether the permission checkboxes mean anything. False for the default admin role, which is not
|
|
35
|
+
# editable at all, and false for any other role slugged `admin`, whose permissions cannot be
|
|
36
|
+
# withheld from its users. The editor renders them locked and the controller declines to write
|
|
37
|
+
# them, so the two cannot disagree — and a locked checkbox, which the browser does not submit,
|
|
38
|
+
# can never silently clear what is stored.
|
|
39
|
+
def permissions_editable?
|
|
40
|
+
editable? && !grants_all_permissions?
|
|
41
|
+
end
|
|
42
|
+
|
|
23
43
|
ROLES = {
|
|
24
44
|
post_type: [
|
|
25
45
|
{
|
|
@@ -81,6 +101,14 @@ module CamaleonCms
|
|
|
81
101
|
label: I18n.t('camaleon_cms.admin.users.roles_values.html_manage_tags').to_s,
|
|
82
102
|
color: 'warning',
|
|
83
103
|
description: I18n.t('camaleon_cms.admin.users.tool_tip.manage_tags').to_s
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
key: 'post_content_unfiltered_html',
|
|
107
|
+
label: I18n.t('camaleon_cms.admin.users.roles_values.post_content_unfiltered_html',
|
|
108
|
+
default: 'Allow unfiltered HTML in post content').to_s,
|
|
109
|
+
color: 'danger',
|
|
110
|
+
description: I18n.t('camaleon_cms.admin.users.tool_tip.post_content_unfiltered_html',
|
|
111
|
+
default: 'Permit users with this role to save raw/unfiltered HTML in post content').to_s
|
|
84
112
|
}
|
|
85
113
|
],
|
|
86
114
|
manager: [
|
|
@@ -133,12 +161,44 @@ module CamaleonCms
|
|
|
133
161
|
{
|
|
134
162
|
key: 'select_eval',
|
|
135
163
|
label: I18n.t('camaleon_cms.admin.custom_field.select_eval', default: 'Select Eval').to_s,
|
|
136
|
-
description: I18n.t(
|
|
164
|
+
description: I18n.t(
|
|
165
|
+
'camaleon_cms.admin.users.tool_tip.select_eval',
|
|
166
|
+
default: 'Allow toggling and using select_eval custom fields in the admin UI.'
|
|
167
|
+
).to_s
|
|
137
168
|
},
|
|
138
169
|
{
|
|
139
170
|
key: 'theme_settings',
|
|
140
171
|
label: I18n.t('camaleon_cms.admin.settings.theme_setting', default: 'Theme Settings').to_s,
|
|
141
172
|
description: I18n.t('camaleon_cms.admin.users.tool_tip.themes').to_s
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
key: 'contact_form_unfiltered_html',
|
|
176
|
+
label: I18n.t('camaleon_cms.admin.users.roles_values.contact_form_unfiltered_html',
|
|
177
|
+
default: 'Allow unfiltered HTML in contact forms').to_s,
|
|
178
|
+
color: 'danger',
|
|
179
|
+
description: I18n.t(
|
|
180
|
+
'camaleon_cms.admin.users.tool_tip.contact_form_unfiltered_html',
|
|
181
|
+
default: 'Permit users with this role to save raw/unfiltered HTML anywhere in a contact form: the ' \
|
|
182
|
+
'markup wrapping a form, field labels, descriptions, templates, CSS classes, default ' \
|
|
183
|
+
'values, custom attributes, option labels and response messages. Without it, a save ' \
|
|
184
|
+
'carrying such content is refused rather than cleaned up. Distinct from the per-post-type ' \
|
|
185
|
+
'"Allow unfiltered HTML in post content" permission.'
|
|
186
|
+
).to_s
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
key: 'media_unfiltered_upload',
|
|
190
|
+
label: I18n.t('camaleon_cms.admin.users.roles_values.media_unfiltered_upload',
|
|
191
|
+
default: 'Allow unscanned media uploads').to_s,
|
|
192
|
+
color: 'danger',
|
|
193
|
+
description: I18n.t(
|
|
194
|
+
'camaleon_cms.admin.users.tool_tip.media_unfiltered_upload',
|
|
195
|
+
default: 'Permit users with this role to upload files without the malicious-content scan. Without ' \
|
|
196
|
+
'it, every upload is scanned whatever its source, and a file carrying scripts, event ' \
|
|
197
|
+
'handlers, blocked elements or blocked URI schemes is refused rather than cleaned up. ' \
|
|
198
|
+
'Uploaded files are served from the site origin, so this grants the ability to publish ' \
|
|
199
|
+
'active content there. Site-wide, unlike the per-post-type "Allow unfiltered HTML in post ' \
|
|
200
|
+
'content" permission.'
|
|
201
|
+
).to_s
|
|
142
202
|
}
|
|
143
203
|
]
|
|
144
204
|
}.freeze
|
|
@@ -146,7 +206,7 @@ module CamaleonCms
|
|
|
146
206
|
private
|
|
147
207
|
|
|
148
208
|
def set_users_as_cilent
|
|
149
|
-
site.users.where(role: slug).update_all(role: 'client')
|
|
209
|
+
site.users.where(role: slug).update_all(role: 'client') # rubocop:disable Rails/SkipsModelValidations
|
|
150
210
|
end
|
|
151
211
|
end
|
|
152
212
|
end
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
module Widget
|
|
3
3
|
class Assigned < CamaleonCms::PostDefault
|
|
4
|
-
default_scope -> {
|
|
4
|
+
default_scope -> { order(:taxonomy_id) }
|
|
5
|
+
|
|
6
|
+
def self.type_condition(table = arel_table)
|
|
7
|
+
super.or(table[inheritance_column].eq(name))
|
|
8
|
+
end
|
|
9
|
+
|
|
5
10
|
# post_parent: sidebar_id
|
|
6
11
|
# visibility: widget_id
|
|
7
12
|
# comment_count: item_order
|
|
@@ -12,11 +17,11 @@ module CamaleonCms
|
|
|
12
17
|
|
|
13
18
|
# attr_accessible :widget_id, :sidebar_id, :item_order
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
belongs_to :sidebar, class_name: 'CamaleonCms::Widget::Sidebar', foreign_key: :post_parent, inverse_of: :assigned,
|
|
21
|
+
optional: true
|
|
22
|
+
belongs_to :widget, class_name: 'CamaleonCms::Widget::Main', foreign_key: :visibility, inverse_of: :assigned,
|
|
23
|
+
optional: true
|
|
24
|
+
|
|
20
25
|
after_initialize :fix_slug2
|
|
21
26
|
before_create :set_order
|
|
22
27
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
module Widget
|
|
3
3
|
class Main < CamaleonCms::TermTaxonomy
|
|
4
|
-
|
|
4
|
+
def self.sti_name
|
|
5
|
+
'widget'
|
|
6
|
+
end
|
|
5
7
|
|
|
6
|
-
default_scope { where(taxonomy: :widget) }
|
|
7
8
|
# attr_accessible :excerpt, :renderer
|
|
8
9
|
# name: "title"
|
|
9
10
|
# description: "content for this"
|
|
@@ -12,14 +13,17 @@ module CamaleonCms
|
|
|
12
13
|
# excerpt: string for message
|
|
13
14
|
# renderer: string (path to the template for render this widget)
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
belongs_to :site, class_name: 'CamaleonCms::Site', foreign_key: :parent_id
|
|
16
|
+
# inverse_of: false — custom `user_model` classes get only UserMethods, which
|
|
17
|
+
# defines no `widgets` collection the inverse could point at.
|
|
18
|
+
belongs_to :owner, class_name: CamaManager.get_user_class_name.to_s, foreign_key: :user_id, inverse_of: false,
|
|
19
|
+
optional: true
|
|
20
|
+
belongs_to :site, class_name: 'CamaleonCms::Site', foreign_key: :parent_id, inverse_of: :widgets, optional: true
|
|
21
|
+
|
|
22
|
+
has_many :assigned, class_name: 'CamaleonCms::Widget::Assigned', foreign_key: :visibility, dependent: :destroy,
|
|
23
|
+
inverse_of: :widget
|
|
20
24
|
|
|
21
|
-
has_many :assigned, class_name: 'CamaleonCms::Widget::Assigned', foreign_key: :visibility, dependent: :destroy
|
|
22
25
|
before_save :check_excerpt
|
|
26
|
+
|
|
23
27
|
def is_simple?
|
|
24
28
|
status == 'simple'
|
|
25
29
|
end
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
module Widget
|
|
3
3
|
class Sidebar < CamaleonCms::TermTaxonomy
|
|
4
|
-
normalize_attrs(:
|
|
4
|
+
normalize_attrs(:description)
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
has_many :metas, lambda {
|
|
9
|
-
where(object_class: 'Widget::Sidebar')
|
|
10
|
-
}, class_name: 'CamaleonCms::Meta', foreign_key: :objectid, dependent: :destroy
|
|
11
|
-
has_many :assigned, foreign_key: :post_parent, dependent: :destroy
|
|
12
|
-
belongs_to :site, class_name: 'CamaleonCms::Site', foreign_key: :parent_id
|
|
6
|
+
has_many :assigned, foreign_key: :post_parent, dependent: :destroy, inverse_of: :sidebar
|
|
7
|
+
belongs_to :site, class_name: 'CamaleonCms::Site', foreign_key: :parent_id, inverse_of: :sidebars, optional: true
|
|
13
8
|
|
|
14
9
|
# scopes
|
|
15
10
|
scope :default_sidebar, -> { where(slug: 'default-sidebar') }
|
|
@@ -1,16 +1,60 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
class CamaleonRecord < ActiveRecord::Base
|
|
4
|
-
|
|
3
|
+
class CamaleonRecord < ActiveRecord::Base # rubocop:disable Rails/ApplicationRecord
|
|
4
|
+
# Sentinels used to shield HTML comment delimiters (the translation locale markers such as `<!--:en-->`)
|
|
5
|
+
# from ActionController's sanitize(), which strips HTML comments. Private-Use-Area code points are used so
|
|
6
|
+
# that plain text typed by a user can never be rewritten into a comment delimiter after sanitization: the
|
|
7
|
+
# previous `!--`/`--!` tokens collided with ordinary text (e.g. "Read more !--"), so the restore pass would
|
|
8
|
+
# inject stray `<!--`/`-->` into the output. The hide map also strips any raw sentinel characters supplied
|
|
9
|
+
# in the input, so they cannot be smuggled through to become delimiters on restore.
|
|
10
|
+
TRANSLATION_TAG_HIDE_SENTINELS = { open: "\u{E000}", close: "\u{E001}" }.freeze
|
|
11
|
+
TRANSLATION_TAG_HIDE_MAP = {
|
|
12
|
+
'<!--' => TRANSLATION_TAG_HIDE_SENTINELS[:open],
|
|
13
|
+
'-->' => TRANSLATION_TAG_HIDE_SENTINELS[:close],
|
|
14
|
+
TRANSLATION_TAG_HIDE_SENTINELS[:open] => '',
|
|
15
|
+
TRANSLATION_TAG_HIDE_SENTINELS[:close] => ''
|
|
16
|
+
}.freeze
|
|
5
17
|
TRANSLATION_TAG_HIDE_REGEX = Regexp.new(TRANSLATION_TAG_HIDE_MAP.keys.map { |x| Regexp.escape(x) }.join('|')).freeze
|
|
6
|
-
TRANSLATION_TAG_RESTORE_MAP = {
|
|
18
|
+
TRANSLATION_TAG_RESTORE_MAP = {
|
|
19
|
+
TRANSLATION_TAG_HIDE_SENTINELS[:open] => '<!--',
|
|
20
|
+
TRANSLATION_TAG_HIDE_SENTINELS[:close] => '-->'
|
|
21
|
+
}.freeze
|
|
7
22
|
TRANSLATION_TAG_RESTORE_REGEX =
|
|
8
23
|
Regexp.new(TRANSLATION_TAG_RESTORE_MAP.keys.map { |x| Regexp.escape(x) }.join('|')).freeze
|
|
9
24
|
|
|
10
|
-
include ActiveRecordExtras::Relation
|
|
11
|
-
|
|
12
25
|
self.abstract_class = true
|
|
13
26
|
|
|
27
|
+
# Sanitize a value with ActionController's sanitize() while preserving translation locale markers
|
|
28
|
+
# (<!--:xx-->). Shared by Post#sanitize_content and the NormalizeAttrs concern so the transform lives in
|
|
29
|
+
# one place. Returns nil unchanged. `tags:`/`attributes:` default to nil, i.e. the sanitizer's own
|
|
30
|
+
# allowlist, so NormalizeAttrs and other callers are unaffected; Post#content passes a widened list.
|
|
31
|
+
def self.cama_sanitize_translatable(value, tags: nil, attributes: nil)
|
|
32
|
+
return value if value.nil?
|
|
33
|
+
|
|
34
|
+
options = {}
|
|
35
|
+
options[:tags] = tags if tags
|
|
36
|
+
options[:attributes] = attributes if attributes
|
|
37
|
+
ActionController::Base.helpers.sanitize(
|
|
38
|
+
value.to_s.gsub(TRANSLATION_TAG_HIDE_REGEX, TRANSLATION_TAG_HIDE_MAP), **options
|
|
39
|
+
).gsub(TRANSLATION_TAG_RESTORE_REGEX, TRANSLATION_TAG_RESTORE_MAP)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.polymorphic_name
|
|
43
|
+
return super unless name.to_s.start_with?('CamaleonCms::')
|
|
44
|
+
|
|
45
|
+
name.demodulize
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.polymorphic_class_for(name)
|
|
49
|
+
super
|
|
50
|
+
rescue NameError
|
|
51
|
+
legacy_class = legacy_camaleon_polymorphic_class(name)
|
|
52
|
+
return legacy_class if legacy_class
|
|
53
|
+
return nil if legacy_polymorphic_marker?(name)
|
|
54
|
+
|
|
55
|
+
raise
|
|
56
|
+
end
|
|
57
|
+
|
|
14
58
|
# save cache value for this key
|
|
15
59
|
def cama_set_cache(key, val)
|
|
16
60
|
@cama_cache_vars ||= {}
|
|
@@ -20,6 +64,8 @@ class CamaleonRecord < ActiveRecord::Base
|
|
|
20
64
|
|
|
21
65
|
# remove cache value for this key
|
|
22
66
|
def cama_remove_cache(key)
|
|
67
|
+
return unless @cama_cache_vars
|
|
68
|
+
|
|
23
69
|
@cama_cache_vars.delete(cama_build_cache_key(key))
|
|
24
70
|
end
|
|
25
71
|
|
|
@@ -83,4 +129,42 @@ class CamaleonRecord < ActiveRecord::Base
|
|
|
83
129
|
|
|
84
130
|
@current_site = CurrentRequest.site
|
|
85
131
|
end
|
|
132
|
+
|
|
133
|
+
def self.legacy_camaleon_polymorphic_class(name)
|
|
134
|
+
class_name = name.to_s
|
|
135
|
+
return if class_name.blank?
|
|
136
|
+
|
|
137
|
+
candidates = []
|
|
138
|
+
|
|
139
|
+
unless class_name.include?('::')
|
|
140
|
+
candidates << "CamaleonCms::#{class_name}"
|
|
141
|
+
|
|
142
|
+
if class_name.include?('_')
|
|
143
|
+
base_name = class_name.split('_').first
|
|
144
|
+
candidates << "CamaleonCms::#{base_name}" if base_name.present?
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
candidates << CamaManager.get_user_class_name.to_s if class_name == 'User'
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
candidates << "CamaleonCms::#{class_name}" if class_name.include?('::') && !class_name.start_with?('CamaleonCms::')
|
|
151
|
+
|
|
152
|
+
candidates.uniq.each do |candidate|
|
|
153
|
+
next unless valid_constant_name?(candidate)
|
|
154
|
+
|
|
155
|
+
klass = candidate.safe_constantize
|
|
156
|
+
return klass if klass
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
nil
|
|
160
|
+
end
|
|
161
|
+
private_class_method def self.legacy_polymorphic_marker?(name)
|
|
162
|
+
name.to_s.start_with?('_')
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private_class_method def self.valid_constant_name?(name)
|
|
166
|
+
name.to_s.match?(/\A[A-Z]\w*(::[A-Z]\w*)*\z/)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
private_class_method :legacy_camaleon_polymorphic_class
|
|
86
170
|
end
|
|
@@ -24,7 +24,7 @@ module CamaleonCms
|
|
|
24
24
|
# update category assignations for this post
|
|
25
25
|
# remove assignations that no longer exist
|
|
26
26
|
# add new assignations
|
|
27
|
-
# cats: (Array) array of category ids assigned for this post, sample: [1,2,3]
|
|
27
|
+
# cats: (Array) array of category ids assigned for this post, sample: [1, 2, 3]
|
|
28
28
|
def update_categories(cats = [])
|
|
29
29
|
rescue_extra_data
|
|
30
30
|
cats = cats.to_i
|
|
@@ -35,7 +35,7 @@ module CamaleonCms
|
|
|
35
35
|
news_categories.each do |key|
|
|
36
36
|
term_relationships.create(term_taxonomy_id: key)
|
|
37
37
|
end
|
|
38
|
-
|
|
38
|
+
update_counts('categories')
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
# update tags assignations for this post
|
|
@@ -53,7 +53,7 @@ module CamaleonCms
|
|
|
53
53
|
post_tag = post_type.post_tags.where({ name: f }).first_or_create(slug: f.parameterize)
|
|
54
54
|
term_relationships.where({ term_taxonomy_id: post_tag.id }).first_or_create
|
|
55
55
|
end
|
|
56
|
-
|
|
56
|
+
update_counts('tags')
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
# Assign this post for category with id: category_id
|
|
@@ -64,43 +64,25 @@ module CamaleonCms
|
|
|
64
64
|
categories_id.each do |key|
|
|
65
65
|
term_relationships.where(term_taxonomy_id: key).first_or_create!
|
|
66
66
|
end
|
|
67
|
-
|
|
67
|
+
update_counts('categories')
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
#
|
|
71
|
-
# categories_id: (Array) array of category ids
|
|
70
|
+
# Unassign this post from categories with ids: categories_id
|
|
71
|
+
# categories_id: (Array) array of category ids to unassign from this post, sample: [1,2,3]
|
|
72
72
|
def unassign_category(categories_id)
|
|
73
73
|
categories_id = [categories_id] if categories_id.is_a?(Integer)
|
|
74
|
+
# a loaded (stale) categories association would hide the removed ids from the
|
|
75
|
+
# counter refresh below — check_default_category loads it on every save
|
|
76
|
+
categories.reset
|
|
74
77
|
rescue_extra_data
|
|
75
78
|
term_relationships.where(term_taxonomy_id: categories_id).destroy_all
|
|
76
|
-
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Assign new tags to this post
|
|
80
|
-
# tags_title: (String) tags name separated by commas, sample: "Tag1,Tag two,tag new"
|
|
81
|
-
def assign_tags(tag_titles)
|
|
82
|
-
update_counters_before
|
|
83
|
-
tags = tag_titles.split(',').strip
|
|
84
|
-
tags.each do |t|
|
|
85
|
-
post_tag = post_type.post_tags.where(name: t).first_or_create!
|
|
86
|
-
term_relationships.where({ term_taxonomy_id: post_tag.id }).first_or_create!
|
|
87
|
-
end
|
|
88
|
-
update_counters('tags')
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Unassign tags from this post
|
|
92
|
-
# tags_title: (String) tags name separated by commas, sample: "Tag1,Tag two,tag new"
|
|
93
|
-
def unassign_tags(tag_titles)
|
|
94
|
-
update_counters_before
|
|
95
|
-
tags = tag_titles.split(',').strip
|
|
96
|
-
term_relationships.where({ term_taxonomy_id: post_type.post_tags.where(name: tags).pluck(:id) }).destroy_all
|
|
97
|
-
update_counters('tags')
|
|
79
|
+
update_counts('categories')
|
|
98
80
|
end
|
|
99
81
|
|
|
100
|
-
#
|
|
82
|
+
# Update the quantity of posts assigned for tags and categories assigned to this post
|
|
101
83
|
def update_extra_data
|
|
102
84
|
rescue_extra_data
|
|
103
|
-
|
|
85
|
+
update_counts
|
|
104
86
|
end
|
|
105
87
|
|
|
106
88
|
private
|
|
@@ -113,23 +95,23 @@ module CamaleonCms
|
|
|
113
95
|
@tags_before = post_tags.pluck(:id) if manage_tags?
|
|
114
96
|
end
|
|
115
97
|
|
|
116
|
-
#
|
|
98
|
+
# Update the quantity of posts assigned for tags and categories assigned to this post
|
|
117
99
|
# if kind is empty, then this will update both: cats and tags
|
|
118
100
|
# kind: (string) tags | categories
|
|
119
|
-
def
|
|
101
|
+
def update_counts(kind = '')
|
|
120
102
|
if ['', 'categories'].include?(kind) && manage_categories?
|
|
121
|
-
post_type.full_categories.where(id: (@cats_before + categories.pluck(:id)).uniq).
|
|
122
|
-
c.update_column(
|
|
103
|
+
post_type.full_categories.where(id: (@cats_before + categories.pluck(:id)).uniq).find_each do |c|
|
|
104
|
+
c.update_column(:count, c.posts.published.size) # rubocop:disable Rails/SkipsModelValidations
|
|
123
105
|
end
|
|
124
106
|
end
|
|
125
107
|
return unless ['', 'tags'].include?(kind) && manage_tags?
|
|
126
108
|
|
|
127
|
-
post_type.post_tags.where(id: (@tags_before + post_tags.pluck(:id)).uniq).
|
|
128
|
-
tag.update_column(
|
|
109
|
+
post_type.post_tags.where(id: (@tags_before + post_tags.pluck(:id)).uniq).find_each do |tag|
|
|
110
|
+
tag.update_column(:count, tag.posts.published.size) # rubocop:disable Rails/SkipsModelValidations
|
|
129
111
|
end
|
|
130
112
|
end
|
|
131
113
|
|
|
132
|
-
#
|
|
114
|
+
# Save extra data such as categories and tags assigned to this post
|
|
133
115
|
def save_extra_data
|
|
134
116
|
update_categories(data_categories) if manage_categories? && !data_categories.nil?
|
|
135
117
|
update_tags(data_tags) if manage_tags? && !data_tags.nil?
|
|
@@ -139,7 +121,7 @@ module CamaleonCms
|
|
|
139
121
|
def check_default_category
|
|
140
122
|
return unless manage_categories?
|
|
141
123
|
|
|
142
|
-
assign_category([post_type.default_category.id])
|
|
124
|
+
assign_category([post_type.default_category.id]) if categories.blank?
|
|
143
125
|
end
|
|
144
126
|
end
|
|
145
127
|
end
|
|
@@ -3,22 +3,35 @@
|
|
|
3
3
|
module CamaleonCms
|
|
4
4
|
module CommonRelationships
|
|
5
5
|
extend ActiveSupport::Concern
|
|
6
|
+
# rubocop:disable Rails/InverseOf
|
|
6
7
|
|
|
7
8
|
included do
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
# Scopes use the demodulized class name (the 2.9.2 contract every install's rows were
|
|
10
|
+
# written under): 'Post', 'Main' for Widget::Main, and 'User' for a host Admin::User.
|
|
11
|
+
# to_s tolerates anonymous subclasses (STI specs build them), whose name is nil.
|
|
12
|
+
object_class_name = name.to_s.demodulize
|
|
13
|
+
has_many :metas, -> { where(object_class: object_class_name) },
|
|
10
14
|
class_name: 'CamaleonCms::Meta', foreign_key: :objectid, dependent: :destroy
|
|
11
15
|
|
|
12
|
-
has_many :custom_field_values, -> { where(object_class:
|
|
16
|
+
has_many :custom_field_values, -> { where(object_class: object_class_name) },
|
|
13
17
|
class_name: 'CamaleonCms::CustomFieldsRelationship', foreign_key: :objectid, dependent: :delete_all
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
# No dependent: on the definition associations (2.9.2 behavior). Teardown is owned by
|
|
20
|
+
# CustomFieldsRead#_destroy_custom_field_groups, which runs first and destroys groups WITH
|
|
21
|
+
# callbacks; a dependent here is inert at best, and on hook-less includers (PostComment) a
|
|
22
|
+
# delete_all removed group rows while orphaning their fields and option metas. A :destroy
|
|
23
|
+
# cascade is no alternative: custom_fields matches group rows too (same table, same scope)
|
|
24
|
+
# but its CustomField typing would skip group teardown.
|
|
25
|
+
# rubocop:disable Rails/HasManyOrHasOneDependent
|
|
26
|
+
has_many :custom_fields, -> { where(object_class: object_class_name) },
|
|
16
27
|
class_name: 'CamaleonCms::CustomField', foreign_key: :objectid
|
|
17
28
|
|
|
18
29
|
# valid only for simple groups and not for complex like: posts, post, ... where the group is for individual or
|
|
19
30
|
# children groups
|
|
20
|
-
has_many :custom_field_groups, -> { where(object_class:
|
|
31
|
+
has_many :custom_field_groups, -> { where(object_class: object_class_name) },
|
|
21
32
|
class_name: 'CamaleonCms::CustomFieldGroup', foreign_key: :objectid
|
|
33
|
+
# rubocop:enable Rails/HasManyOrHasOneDependent
|
|
22
34
|
end
|
|
35
|
+
# rubocop:enable Rails/InverseOf
|
|
23
36
|
end
|
|
24
37
|
end
|