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
|
@@ -2,7 +2,7 @@ module CamaleonCms
|
|
|
2
2
|
class Media < CamaleonRecord
|
|
3
3
|
self.table_name = "#{PluginRoutes.static_system_info['db_prefix']}media"
|
|
4
4
|
|
|
5
|
-
belongs_to :site,
|
|
5
|
+
belongs_to :site, optional: true
|
|
6
6
|
validates :name, uniqueness: {
|
|
7
7
|
scope: %i[site_id is_folder folder_path is_public],
|
|
8
8
|
message: 'Duplicates not allowed'
|
|
@@ -23,7 +23,7 @@ module CamaleonCms
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
# search file or folder by key
|
|
26
|
-
def self.
|
|
26
|
+
def self.by_key(key)
|
|
27
27
|
key = key.cama_fix_media_key
|
|
28
28
|
if key == '/'
|
|
29
29
|
where(folder_path: File.dirname(key))
|
|
@@ -32,6 +32,12 @@ module CamaleonCms
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
# legacy public name (pre-rename callers); the media table has no `key` column, so
|
|
36
|
+
# without the alias this falls through to Rails' dynamic finder and raises
|
|
37
|
+
class << self
|
|
38
|
+
alias find_by_key by_key
|
|
39
|
+
end
|
|
40
|
+
|
|
35
41
|
# return all items of current folder
|
|
36
42
|
def items
|
|
37
43
|
coll = is_public ? site.public_media : site.private_media
|
|
@@ -3,8 +3,8 @@ module CamaleonCms
|
|
|
3
3
|
self.table_name = "#{PluginRoutes.static_system_info['db_prefix']}metas"
|
|
4
4
|
# attr_accessible :objectid, :key, :value, :object_class
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
belongs_to :owner, polymorphic: true, foreign_key: :objectid, foreign_type: :object_class, optional: true
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
extend CamaleonCms::NormalizeAttrs
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class NavMenu < CamaleonCms::TermTaxonomy
|
|
3
|
-
|
|
3
|
+
normalize_attrs(:description)
|
|
4
|
+
|
|
5
|
+
# deterministic external iteration order (2.9.2 parity); render paths reorder(:term_order)
|
|
6
|
+
default_scope { order(id: :asc) }
|
|
7
|
+
|
|
4
8
|
alias_attribute :site_id, :parent_id
|
|
5
9
|
|
|
6
10
|
has_many :children, class_name: 'CamaleonCms::NavMenuItem', foreign_key: :parent_id, dependent: :destroy,
|
|
7
11
|
inverse_of: :parent
|
|
8
|
-
belongs_to :site, foreign_key: :parent_id, inverse_of: :nav_menus,
|
|
12
|
+
belongs_to :site, foreign_key: :parent_id, inverse_of: :nav_menus, optional: true
|
|
9
13
|
|
|
10
14
|
# add menu item for current menu
|
|
11
15
|
# value: (Hash) is a hash object that contains label, type, link
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class NavMenuItem < CamaleonCms::TermTaxonomy
|
|
3
|
+
# NavMenuItem#name (the menu label) is the one "name" field that is rendered as raw/trusted HTML
|
|
4
|
+
# (see CamaleonCms::Frontend::NavMenuHelper#cama_menu_draw_items), unlike other model names which are
|
|
5
|
+
# ERB-auto-escaped. It therefore keeps save-time sanitization: safe formatting (icons, spans) is preserved
|
|
6
|
+
# while scripts and event handlers are stripped, preventing stored XSS from menu managers.
|
|
7
|
+
normalize_attrs(:name, :description)
|
|
8
|
+
|
|
9
|
+
# deterministic external iteration order (2.9.2 parity); render paths reorder(:term_order)
|
|
10
|
+
default_scope { order(id: :asc) }
|
|
11
|
+
|
|
3
12
|
alias_attribute :site_id, :term_group
|
|
4
13
|
alias_attribute :label, :name
|
|
5
14
|
alias_attribute :url, :description
|
|
6
15
|
alias_attribute :kind, :slug
|
|
7
16
|
alias_attribute :target, :status
|
|
17
|
+
|
|
8
18
|
# attr_accessible :label, :url, :kind
|
|
9
|
-
#
|
|
10
|
-
default_scope { where(taxonomy: :nav_menu_item).order(id: :asc) }
|
|
11
19
|
|
|
12
|
-
belongs_to :parent, class_name: 'CamaleonCms::NavMenu', inverse_of: :children,
|
|
20
|
+
belongs_to :parent, class_name: 'CamaleonCms::NavMenu', inverse_of: :children, optional: true
|
|
13
21
|
belongs_to :parent_item, class_name: 'CamaleonCms::NavMenuItem', foreign_key: :parent_id, inverse_of: :children,
|
|
14
|
-
|
|
22
|
+
optional: true
|
|
15
23
|
has_many :children, class_name: 'CamaleonCms::NavMenuItem', foreign_key: :parent_id, dependent: :destroy,
|
|
16
24
|
inverse_of: :parent_item
|
|
17
25
|
|
|
@@ -54,10 +62,12 @@ module CamaleonCms
|
|
|
54
62
|
private
|
|
55
63
|
|
|
56
64
|
def update_count
|
|
57
|
-
parent&.update_column(
|
|
58
|
-
parent_item&.update_column(
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
parent&.update_column(:count, parent.children.size) # rubocop:disable Rails/SkipsModelValidations
|
|
66
|
+
parent_item&.update_column(:count, parent_item.children.size) # rubocop:disable Rails/SkipsModelValidations
|
|
67
|
+
# update group and position
|
|
68
|
+
update_columns( # rubocop:disable Rails/SkipsModelValidations
|
|
69
|
+
term_group: main_menu.parent_id, term_order: CamaleonCms::NavMenuItem.where(parent_id: parent_id).count
|
|
70
|
+
)
|
|
61
71
|
end
|
|
62
72
|
|
|
63
73
|
# fast access from site to menu items
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class Plugin < CamaleonCms::TermTaxonomy
|
|
3
|
+
normalize_attrs(:description)
|
|
4
|
+
|
|
3
5
|
# attrs:
|
|
4
6
|
# term_group => status active (1, nil)
|
|
5
7
|
# slug => plugin key
|
|
@@ -7,9 +9,8 @@ module CamaleonCms
|
|
|
7
9
|
|
|
8
10
|
attr_accessor :error
|
|
9
11
|
|
|
10
|
-
belongs_to :site, foreign_key: :parent_id,
|
|
12
|
+
belongs_to :site, foreign_key: :parent_id, optional: true, inverse_of: :plugins
|
|
11
13
|
|
|
12
|
-
default_scope { where(taxonomy: :plugin) }
|
|
13
14
|
scope :active, -> { where(term_group: 1) }
|
|
14
15
|
|
|
15
16
|
before_validation :set_default
|
|
@@ -69,7 +70,7 @@ module CamaleonCms
|
|
|
69
70
|
private
|
|
70
71
|
|
|
71
72
|
def set_default
|
|
72
|
-
self.name = slug
|
|
73
|
+
self.name = slug if name.blank?
|
|
73
74
|
end
|
|
74
75
|
|
|
75
76
|
def destroy_custom_fields
|
|
@@ -2,9 +2,44 @@ module CamaleonCms
|
|
|
2
2
|
class Post < CamaleonCms::PostDefault
|
|
3
3
|
include CamaleonCms::CategoriesTagsForPosts
|
|
4
4
|
|
|
5
|
+
# Structural, non-executable markup that long-form post content legitimately uses but the
|
|
6
|
+
# sanitizer default drops. Superset of the default so upstream security additions are inherited.
|
|
7
|
+
SANITIZE_EXTRA_TAGS = %w[table thead tbody tfoot tr td th caption col colgroup
|
|
8
|
+
figure figcaption u s hr].freeze
|
|
9
|
+
# These attributes are allowed knowing what they cost, none of which is script execution:
|
|
10
|
+
# `style` is CSS-scrubbed by the gate's scrubber (expression(), url(javascript:) and friends
|
|
11
|
+
# register as removals) but permits absolutely-positioned overlays; `id` widens DOM clobbering,
|
|
12
|
+
# which the scrubber's own default `name` already opened; and `target` without a forced
|
|
13
|
+
# `rel=noopener` is reverse tabnabbing on browsers predating the implicit default. The trade
|
|
14
|
+
# is deliberate: an untrusted author whose content needs more than this list has the save
|
|
15
|
+
# refused, and the role that wants more grants post_content_unfiltered_html.
|
|
16
|
+
SANITIZE_EXTRA_ATTRIBUTES = %w[id style target rel colspan rowspan].freeze
|
|
17
|
+
CONTENT_ALLOWED_TAGS = (ActionController::Base.helpers.sanitizer_vendor.safe_list_sanitizer
|
|
18
|
+
.allowed_tags.to_a + SANITIZE_EXTRA_TAGS).uniq.freeze
|
|
19
|
+
CONTENT_ALLOWED_ATTRIBUTES = (ActionController::Base.helpers.sanitizer_vendor.safe_list_sanitizer
|
|
20
|
+
.allowed_attributes.to_a + SANITIZE_EXTRA_ATTRIBUTES).uniq.freeze
|
|
21
|
+
|
|
22
|
+
# Opt-out for trusted server-side pipelines (imports, seeds, plugin code) that would otherwise
|
|
23
|
+
# be sanitized by the fail-closed default. Exposed as a reader plus a bang enabler and NO
|
|
24
|
+
# `unfiltered_content=` writer, so `assign_attributes`/mass assignment cannot reach it — only
|
|
25
|
+
# explicit server-side code calling `post.unfiltered_content!` can.
|
|
26
|
+
#
|
|
27
|
+
# It is sticky for the lifetime of the object, not for one save: an instance that has been
|
|
28
|
+
# opted out stays opted out for every subsequent save of that instance. That is what a
|
|
29
|
+
# pipeline staging a record over several steps wants; code that needs the opt-out to apply
|
|
30
|
+
# once should not reuse the instance afterwards.
|
|
31
|
+
attr_reader :unfiltered_content
|
|
32
|
+
|
|
33
|
+
def unfiltered_content!
|
|
34
|
+
@unfiltered_content = true
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
5
38
|
alias_attribute :post_type_id, :taxonomy_id
|
|
6
39
|
default_scope -> { where(post_class: 'Post').order(post_order: :asc, created_at: :desc) }
|
|
7
40
|
|
|
41
|
+
validate :reject_untrusted_dangerous_content, on: %i[create update]
|
|
42
|
+
|
|
8
43
|
# DEPRECATED
|
|
9
44
|
has_many :post_relationships, class_name: 'CamaleonCms::PostRelationship', foreign_key: :objectid,
|
|
10
45
|
dependent: :destroy, inverse_of: :post
|
|
@@ -14,16 +49,17 @@ module CamaleonCms
|
|
|
14
49
|
has_many :term_relationships, foreign_key: :objectid, dependent: :destroy, inverse_of: :object
|
|
15
50
|
has_many :categories, class_name: 'CamaleonCms::Category', through: :term_relationships, source: :term_taxonomy
|
|
16
51
|
has_many :post_tags, class_name: 'CamaleonCms::PostTag', through: :term_relationships, source: :term_taxonomy
|
|
17
|
-
has_many :comments, class_name: 'CamaleonCms::PostComment',
|
|
18
|
-
has_many :drafts,
|
|
19
|
-
|
|
20
|
-
}, class_name: 'CamaleonCms::Post', foreign_key: :post_parent, dependent: :destroy
|
|
52
|
+
has_many :comments, class_name: 'CamaleonCms::PostComment', dependent: :destroy
|
|
53
|
+
has_many :drafts, -> { where(status: 'draft_child') }, class_name: 'CamaleonCms::Post', inverse_of: :parent,
|
|
54
|
+
foreign_key: :post_parent, dependent: :destroy
|
|
21
55
|
has_many :children, class_name: 'CamaleonCms::Post', foreign_key: :post_parent, dependent: :destroy,
|
|
22
|
-
primary_key: :id
|
|
56
|
+
primary_key: :id, inverse_of: :parent
|
|
23
57
|
|
|
24
|
-
belongs_to :owner, class_name: CamaManager.get_user_class_name, foreign_key: :user_id,
|
|
25
|
-
|
|
26
|
-
belongs_to :
|
|
58
|
+
belongs_to :owner, class_name: CamaManager.get_user_class_name.to_s, foreign_key: :user_id, optional: true,
|
|
59
|
+
inverse_of: :all_posts
|
|
60
|
+
belongs_to :parent, class_name: 'CamaleonCms::Post', foreign_key: :post_parent, optional: true,
|
|
61
|
+
inverse_of: :children
|
|
62
|
+
belongs_to :post_type, foreign_key: :taxonomy_id, inverse_of: :posts, optional: true
|
|
27
63
|
|
|
28
64
|
scope :visible_frontend, -> { where(status: 'published') }
|
|
29
65
|
scope :public_posts, lambda {
|
|
@@ -42,7 +78,7 @@ module CamaleonCms
|
|
|
42
78
|
validates_with CamaleonCms::PostUniqValidator
|
|
43
79
|
attr_accessor :show_title_with_parent
|
|
44
80
|
|
|
45
|
-
before_create :fix_post_order, if: ->(p) {
|
|
81
|
+
before_create :fix_post_order, if: ->(p) { p.post_order.blank? || p.post_order == 0 }
|
|
46
82
|
|
|
47
83
|
# return all parents for current page hierarchy ordered bottom to top
|
|
48
84
|
def parents
|
|
@@ -66,22 +102,22 @@ module CamaleonCms
|
|
|
66
102
|
end
|
|
67
103
|
end
|
|
68
104
|
|
|
69
|
-
#
|
|
105
|
+
# Return the post type of this post (DEPRECATED), though used yet in migration, so let it stay
|
|
70
106
|
def get_post_type_depre
|
|
71
107
|
post_types.reorder(nil).first
|
|
72
108
|
end
|
|
73
109
|
|
|
74
|
-
#
|
|
110
|
+
# Check if this post was published
|
|
75
111
|
def published?
|
|
76
112
|
status == 'published'
|
|
77
113
|
end
|
|
78
114
|
|
|
79
|
-
#
|
|
115
|
+
# Check if this post is in the pending status
|
|
80
116
|
def pending?
|
|
81
117
|
status == 'pending'
|
|
82
118
|
end
|
|
83
119
|
|
|
84
|
-
#
|
|
120
|
+
# Check if this post is in the draft status
|
|
85
121
|
def draft?
|
|
86
122
|
%w[draft draft_child].include?(status)
|
|
87
123
|
end
|
|
@@ -90,12 +126,12 @@ module CamaleonCms
|
|
|
90
126
|
status == 'draft_child'
|
|
91
127
|
end
|
|
92
128
|
|
|
93
|
-
#
|
|
129
|
+
# Check if this post is in the trash status
|
|
94
130
|
def trash?
|
|
95
131
|
status == 'trash'
|
|
96
132
|
end
|
|
97
133
|
|
|
98
|
-
#
|
|
134
|
+
# Check if the current post can manage content
|
|
99
135
|
# return boolean
|
|
100
136
|
def manage_content?(posttype = nil)
|
|
101
137
|
get_option('has_content', (posttype || post_type).get_option('has_content', true))
|
|
@@ -106,34 +142,35 @@ module CamaleonCms
|
|
|
106
142
|
get_option('has_layout', (posttype || post_type).get_option('has_layout', false))
|
|
107
143
|
end
|
|
108
144
|
|
|
109
|
-
#
|
|
145
|
+
# Check if current post can manage template
|
|
110
146
|
# return boolean
|
|
111
147
|
def manage_template?(posttype = nil)
|
|
112
148
|
get_option('has_template', (posttype || post_type).get_option('has_template', true))
|
|
113
149
|
end
|
|
114
150
|
|
|
115
|
-
#
|
|
151
|
+
# Check if the current post can manage summary
|
|
116
152
|
# return boolean
|
|
117
153
|
def manage_summary?(posttype = nil)
|
|
118
154
|
get_option('has_summary', (posttype || post_type).get_option('has_summary', true))
|
|
119
155
|
end
|
|
120
156
|
|
|
121
|
-
#
|
|
157
|
+
# Check if the current post can manage picture
|
|
122
158
|
# return boolean
|
|
123
159
|
def manage_picture?(posttype = nil)
|
|
124
160
|
get_option('has_picture', (posttype || post_type).get_option('has_picture', true))
|
|
125
161
|
end
|
|
126
162
|
|
|
127
|
-
#
|
|
163
|
+
# Check if the current post can manage comments
|
|
128
164
|
# return boolean
|
|
129
165
|
def manage_comments?(posttype = nil)
|
|
130
166
|
get_option('has_comments', (posttype || post_type).get_option('has_comments', false))
|
|
131
167
|
end
|
|
132
168
|
|
|
133
|
-
#
|
|
169
|
+
# Check if the post can be commented
|
|
134
170
|
# sample: @post.can_commented?
|
|
135
|
-
#
|
|
136
|
-
#
|
|
171
|
+
# to enable comments for current post, use this: post.set_meta('has_comments', '1').
|
|
172
|
+
# Note: Parent PostType should be enabled for comments too: post_type.set_option('has_comments', true)
|
|
173
|
+
# @return [TrueClass, FalseClass]
|
|
137
174
|
def can_commented?
|
|
138
175
|
manage_comments? && get_meta('has_comments').to_s == '1'
|
|
139
176
|
end
|
|
@@ -151,10 +188,15 @@ module CamaleonCms
|
|
|
151
188
|
# has_picture, boolean (default true)
|
|
152
189
|
# has_template, boolean (default false)
|
|
153
190
|
# has_comments, boolean (default false)
|
|
154
|
-
#
|
|
155
|
-
#
|
|
191
|
+
#
|
|
192
|
+
# the following is still used if post type was inactivated layout and overwritten by dropdown in post view
|
|
193
|
+
# default_layout: (string) (default layout)
|
|
194
|
+
#
|
|
195
|
+
# the following is still used if post type was inactivated template and overwritten by dropdown in post view
|
|
196
|
+
# default_template: (string) (default template)
|
|
156
197
|
# has_layout: (boolean) (default false)
|
|
157
|
-
# skip_fields: (array) (default empty) array of custom field keys to avoid for this post,
|
|
198
|
+
# skip_fields: (array) (default empty) array of custom field keys to avoid for this post,
|
|
199
|
+
# sample: ["subtitle", "icon"]
|
|
158
200
|
# val: value for the setting
|
|
159
201
|
def set_setting(key, val)
|
|
160
202
|
set_option(key, val)
|
|
@@ -171,7 +213,7 @@ module CamaleonCms
|
|
|
171
213
|
# new_order_position: (Integer) position number
|
|
172
214
|
# return nil
|
|
173
215
|
def set_position(new_order_position)
|
|
174
|
-
update_column(
|
|
216
|
+
update_column(:post_order, new_order_position) # rubocop:disable Rails/SkipsModelValidations
|
|
175
217
|
end
|
|
176
218
|
|
|
177
219
|
# save the summary for current post
|
|
@@ -247,10 +289,54 @@ module CamaleonCms
|
|
|
247
289
|
|
|
248
290
|
private
|
|
249
291
|
|
|
292
|
+
def trusted_for_unfiltered_html?
|
|
293
|
+
user = CurrentRequest.user
|
|
294
|
+
site = CurrentRequest.site
|
|
295
|
+
# Fail closed (sanitize) when either user or site context is missing - e.g. background jobs, rake tasks
|
|
296
|
+
# or the console. Guarding the site too avoids a NoMethodError from Ability#initialize (which dereferences
|
|
297
|
+
# the site for non-admin users) that would otherwise abort the whole save.
|
|
298
|
+
return false if user.blank? || site.blank?
|
|
299
|
+
|
|
300
|
+
CamaleonCms::Ability.new(user, site).can?(:post_content_unfiltered_html, post_type)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Security (scan-and-reject policy): content an untrusted author is not permitted to write is
|
|
304
|
+
# refused with a validation error -- never sanitized or rewritten -- so stored content always
|
|
305
|
+
# equals authored content and the frontend may render it verbatim. The guard on
|
|
306
|
+
# `content_changed?` deliberately leaves pre-gate stored content editable (a title fix on an
|
|
307
|
+
# old post must not brick it); `rake camaleon_cms:security:scan_content` lists such content.
|
|
308
|
+
def reject_untrusted_dangerous_content
|
|
309
|
+
return unless new_record? || content_changed?
|
|
310
|
+
return if content.blank?
|
|
311
|
+
# The explicit pipeline opt-out short-circuits before the Ability lookup below.
|
|
312
|
+
return if unfiltered_content
|
|
313
|
+
# Check trust only once we know there is content to gate, so unchanged/blank updates never pay for
|
|
314
|
+
# building an Ability (a role-meta DB lookup) on every save.
|
|
315
|
+
return if trusted_for_unfiltered_html?
|
|
316
|
+
|
|
317
|
+
# Distinguish an over-size refusal from a disallowed-markup refusal (audit M16): an over-size
|
|
318
|
+
# value can be perfectly clean, so the markup message (scripts/handlers/embeds) would misdescribe
|
|
319
|
+
# it and send the author hunting for markup that is not there.
|
|
320
|
+
if CamaleonCms::UnsafeMarkup.too_large?(content)
|
|
321
|
+
errors.add(:content, cama_content_rejection_message('content_too_large'))
|
|
322
|
+
elsif CamaleonCms::UnsafeMarkup.unsafe_html?(
|
|
323
|
+
content, tags: CONTENT_ALLOWED_TAGS, attributes: CONTENT_ALLOWED_ATTRIBUTES
|
|
324
|
+
)
|
|
325
|
+
errors.add(:content, cama_content_rejection_message('content_rejected'))
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Only en.yml carries these keys while the process locale follows the current admin/site
|
|
330
|
+
# language — fall back to English rather than emit "translation missing".
|
|
331
|
+
def cama_content_rejection_message(key)
|
|
332
|
+
full_key = "camaleon_cms.admin.post.message.#{key}"
|
|
333
|
+
I18n.t(full_key, default: I18n.t(full_key, locale: :en))
|
|
334
|
+
end
|
|
335
|
+
|
|
250
336
|
# calculate a post order when it is empty
|
|
251
337
|
def fix_post_order
|
|
252
338
|
last_post = post_type.posts.where.not(id: nil).last
|
|
253
|
-
self.post_order = last_post.present? ? last_post.post_order + 1 : 1
|
|
339
|
+
self.post_order = last_post.present? ? (last_post.post_order || 0) + 1 : 1
|
|
254
340
|
end
|
|
255
341
|
end
|
|
256
342
|
end
|
|
@@ -6,16 +6,20 @@ module CamaleonCms
|
|
|
6
6
|
extend CamaleonCms::NormalizeAttrs
|
|
7
7
|
|
|
8
8
|
self.table_name = "#{PluginRoutes.static_system_info['db_prefix']}comments"
|
|
9
|
-
# attr_accessible :user_id, :post_id, :content, :author, :author_email, :author_url, :author_IP, :approved, :agent,
|
|
9
|
+
# attr_accessible :user_id, :post_id, :content, :author, :author_email, :author_url, :author_IP, :approved, :agent,
|
|
10
|
+
# :agent, :typee, :comment_parent, :is_anonymous
|
|
10
11
|
attr_accessor :is_anonymous
|
|
11
12
|
|
|
12
13
|
# default_scope order('comments.created_at ASC')
|
|
13
14
|
# approved: approved | pending | spam
|
|
14
15
|
|
|
15
|
-
has_many :children, class_name: 'CamaleonCms::PostComment', foreign_key: :comment_parent, dependent: :destroy
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
belongs_to :
|
|
16
|
+
has_many :children, class_name: 'CamaleonCms::PostComment', foreign_key: :comment_parent, dependent: :destroy,
|
|
17
|
+
inverse_of: :parent
|
|
18
|
+
|
|
19
|
+
belongs_to :post, optional: true
|
|
20
|
+
belongs_to :parent, class_name: 'CamaleonCms::PostComment', foreign_key: :comment_parent, optional: true,
|
|
21
|
+
inverse_of: :children
|
|
22
|
+
belongs_to :user, class_name: CamaManager.get_user_class_name.to_s, optional: true
|
|
19
23
|
|
|
20
24
|
default_scope { order("#{CamaleonCms::PostComment.table_name}.created_at DESC") }
|
|
21
25
|
|
|
@@ -26,7 +30,7 @@ module CamaleonCms
|
|
|
26
30
|
normalize_attrs(:content)
|
|
27
31
|
|
|
28
32
|
validates :content, presence: true
|
|
29
|
-
|
|
33
|
+
validates :author, :author_email, presence: { if: proc { |c| c.is_anonymous.present? } }
|
|
30
34
|
after_create :update_counter
|
|
31
35
|
after_destroy :update_counter
|
|
32
36
|
|
|
@@ -11,9 +11,42 @@ module CamaleonCms
|
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
# Point Rails to use the post_class column for Native STI
|
|
15
|
+
self.inheritance_column = :post_class
|
|
16
|
+
|
|
17
|
+
# Controls Writing: Strip 'CamaleonCms::' when saving to DB
|
|
18
|
+
# "CamaleonCms::Widget::Assigned" -> "Widget::Assigned"
|
|
19
|
+
# "CamaleonCms::Post" -> "Post"
|
|
20
|
+
def self.sti_name
|
|
21
|
+
name.sub(/^CamaleonCms::/, '')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Controls Reading: Prepend 'CamaleonCms::' when pulling from DB
|
|
25
|
+
# "Widget::Assigned" -> "CamaleonCms::Widget::Assigned"
|
|
26
|
+
# "Post" -> "CamaleonCms::Post"
|
|
27
|
+
def self.find_sti_class(type_name)
|
|
28
|
+
full_class_name = type_name.start_with?('CamaleonCms::') ? type_name : "CamaleonCms::#{type_name}"
|
|
29
|
+
klass = begin
|
|
30
|
+
full_class_name.constantize
|
|
31
|
+
rescue NameError
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
return klass if klass && klass <= base_class
|
|
35
|
+
|
|
36
|
+
# `super` resolves fully-qualified external classes (plugin STI outside the
|
|
37
|
+
# CamaleonCms namespace) and enforces ancestry; values it cannot place load as
|
|
38
|
+
# the base class, as every row did before native STI.
|
|
39
|
+
begin
|
|
40
|
+
super
|
|
41
|
+
rescue ActiveRecord::SubclassNotFound
|
|
42
|
+
base_class
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
14
46
|
self.table_name = "#{PluginRoutes.static_system_info['db_prefix']}posts"
|
|
15
47
|
|
|
16
|
-
# attr_accessible :user_id, :title, :slug, :content, :content_filtered, :status, :visibility, :visibility_value,
|
|
48
|
+
# attr_accessible :user_id, :title, :slug, :content, :content_filtered, :status, :visibility, :visibility_value,
|
|
49
|
+
# :post_order, :post_type_key, :taxonomy_id, :published_at, :post_parent, :post_order, :is_feature
|
|
17
50
|
attr_accessor :draft_id
|
|
18
51
|
|
|
19
52
|
# attr_accessible :data_options
|
|
@@ -21,10 +54,12 @@ module CamaleonCms
|
|
|
21
54
|
cattr_accessor :current_user
|
|
22
55
|
cattr_accessor :current_site
|
|
23
56
|
|
|
24
|
-
has_many :
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
57
|
+
has_many :children, -> { where(post_class: 'PostDefault') },
|
|
58
|
+
class_name: 'CamaleonCms::PostDefault', foreign_key: :post_parent, dependent: :destroy, inverse_of: :parent
|
|
59
|
+
|
|
60
|
+
belongs_to :parent, class_name: 'CamaleonCms::PostDefault', foreign_key: :post_parent, optional: true,
|
|
61
|
+
inverse_of: :children
|
|
62
|
+
|
|
28
63
|
scope :featured, -> { where(is_feature: true) }
|
|
29
64
|
|
|
30
65
|
validates :title, :slug, presence: true
|
|
@@ -45,11 +80,6 @@ module CamaleonCms
|
|
|
45
80
|
res.take
|
|
46
81
|
end
|
|
47
82
|
|
|
48
|
-
# return the parent of a post (support for sub contents or tree of posts)
|
|
49
|
-
def parent
|
|
50
|
-
CamaleonCms::Post.where(id: post_parent).first
|
|
51
|
-
end
|
|
52
|
-
|
|
53
83
|
# return the author of this Content
|
|
54
84
|
def author
|
|
55
85
|
CamaleonCms::User.find(user_id)
|
|
@@ -62,7 +92,7 @@ module CamaleonCms
|
|
|
62
92
|
CamaleonCms::NavMenuItem.where(url: id, kind: 'post')
|
|
63
93
|
end
|
|
64
94
|
|
|
65
|
-
# Set the meta
|
|
95
|
+
# Set the meta-field values and the post keywords here
|
|
66
96
|
def set_params(meta, custom_fields, options)
|
|
67
97
|
set_metas(meta)
|
|
68
98
|
set_field_values(custom_fields)
|
|
@@ -78,7 +108,7 @@ module CamaleonCms
|
|
|
78
108
|
|
|
79
109
|
# do all before actions to save the content
|
|
80
110
|
def before_saved
|
|
81
|
-
self.title = 'Untitled'
|
|
111
|
+
self.title = 'Untitled' if title.blank?
|
|
82
112
|
self.content_filtered = if content.to_s.include?('<!--:-->')
|
|
83
113
|
content.translations.transform_values do |value|
|
|
84
114
|
value.squish.strip_tags
|
|
@@ -5,11 +5,11 @@ module CamaleonCms
|
|
|
5
5
|
# attr_accessible :objectid, :term_taxonomy_id, :term_order
|
|
6
6
|
default_scope -> { order(term_order: :asc) }
|
|
7
7
|
|
|
8
|
-
belongs_to :post_type, class_name: 'CamaleonCms::PostType', foreign_key: :term_taxonomy_id,
|
|
9
|
-
|
|
8
|
+
belongs_to :post_type, class_name: 'CamaleonCms::PostType', foreign_key: :term_taxonomy_id, inverse_of: false,
|
|
9
|
+
optional: true
|
|
10
10
|
belongs_to :post, lambda {
|
|
11
11
|
order("#{CamaleonCms::Post.table_name}.id DESC")
|
|
12
|
-
}, foreign_key: :objectid, inverse_of: :post_relationships,
|
|
12
|
+
}, foreign_key: :objectid, inverse_of: :post_relationships, optional: true
|
|
13
13
|
|
|
14
14
|
# callbacks
|
|
15
15
|
after_create :update_count
|
|
@@ -18,7 +18,9 @@ module CamaleonCms
|
|
|
18
18
|
private
|
|
19
19
|
|
|
20
20
|
def update_count
|
|
21
|
-
|
|
21
|
+
return if post_type.blank?
|
|
22
|
+
|
|
23
|
+
post_type.update_column(:count, post_type.posts.size) # rubocop:disable Rails/SkipsModelValidations
|
|
22
24
|
end
|
|
23
25
|
end
|
|
24
26
|
end
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class PostTag < CamaleonCms::TermTaxonomy
|
|
3
|
-
|
|
3
|
+
normalize_attrs(:description)
|
|
4
4
|
|
|
5
5
|
has_many :posts, foreign_key: :objectid, through: :term_relationships, source: :object
|
|
6
|
-
belongs_to :post_type, foreign_key: :parent_id, inverse_of: :post_tags,
|
|
7
|
-
belongs_to :owner, class_name: CamaManager.get_user_class_name, foreign_key: :user_id,
|
|
6
|
+
belongs_to :post_type, foreign_key: :parent_id, inverse_of: :post_tags, optional: true
|
|
7
|
+
belongs_to :owner, class_name: CamaManager.get_user_class_name.to_s, foreign_key: :user_id, optional: true,
|
|
8
|
+
inverse_of: false
|
|
8
9
|
end
|
|
9
10
|
end
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class PostType < CamaleonCms::TermTaxonomy
|
|
3
|
+
normalize_attrs(:description)
|
|
4
|
+
|
|
3
5
|
alias_attribute :site_id, :parent_id
|
|
4
|
-
default_scope { where(taxonomy: :post_type) }
|
|
5
6
|
|
|
6
7
|
has_many :categories, foreign_key: :parent_id, dependent: :destroy, inverse_of: :post_type_parent
|
|
7
8
|
has_many :post_tags, foreign_key: :parent_id, dependent: :destroy, inverse_of: :post_type
|
|
@@ -10,12 +11,12 @@ module CamaleonCms
|
|
|
10
11
|
has_many :posts_through_categories, foreign_key: :objectid, through: :term_relationships, source: :object
|
|
11
12
|
has_many :posts_draft, class_name: 'CamaleonCms::Post', foreign_key: :taxonomy_id, dependent: :destroy,
|
|
12
13
|
inverse_of: :post_type
|
|
13
|
-
has_many :field_group_taxonomy,
|
|
14
|
-
|
|
15
|
-
}, class_name: 'CamaleonCms::CustomField', foreign_key: :objectid, dependent: :destroy
|
|
14
|
+
has_many :field_group_taxonomy, -> { where('object_class LIKE ?', 'PostType_%') },
|
|
15
|
+
class_name: 'CamaleonCms::CustomField', foreign_key: :objectid, dependent: :destroy, inverse_of: :owner
|
|
16
16
|
|
|
17
|
-
belongs_to :owner, class_name: CamaManager.get_user_class_name, foreign_key: :user_id,
|
|
18
|
-
|
|
17
|
+
belongs_to :owner, class_name: CamaManager.get_user_class_name.to_s, foreign_key: :user_id, optional: true,
|
|
18
|
+
inverse_of: false
|
|
19
|
+
belongs_to :site, foreign_key: :parent_id, optional: true, inverse_of: :post_types
|
|
19
20
|
|
|
20
21
|
scope :visible_menu, -> { where(term_group: nil) }
|
|
21
22
|
scope :hidden_menu, -> { where(term_group: -1) }
|
|
@@ -90,7 +91,7 @@ module CamaleonCms
|
|
|
90
91
|
def default_category
|
|
91
92
|
return unless manage_categories?
|
|
92
93
|
|
|
93
|
-
cat = categories.find_by_slug('uncategorized')
|
|
94
|
+
cat = categories.find_by_slug('uncategorized') # rubocop:disable Rails/DynamicFindBy
|
|
94
95
|
unless cat
|
|
95
96
|
cat = categories.create({ name: 'Uncategorized', slug: 'uncategorized', parent_id: id })
|
|
96
97
|
cat.set_option('not_deleted', true)
|
|
@@ -108,10 +109,17 @@ module CamaleonCms
|
|
|
108
109
|
# summary: String resume (optional) => default (empty)
|
|
109
110
|
# post_order: Integer to define the order position in the list (optional)
|
|
110
111
|
# fields: Hash of values for custom fields, sample => fields: {subtitle: 'abc', icon: 'test' } (optional)
|
|
111
|
-
# settings: Hash of post settings, sample => settings:
|
|
112
|
-
# {has_content: false, has_summary: true, default_layout: 'my_layout', default_template: 'my_template' }
|
|
112
|
+
# settings: Hash of post settings, sample => settings: (optional, see more in post.set_setting(...))
|
|
113
|
+
# {has_content: false, has_summary: true, default_layout: 'my_layout', default_template: 'my_template' }
|
|
113
114
|
# data_metas: {template: "", layout: ""}
|
|
114
|
-
# sample:
|
|
115
|
+
# sample:
|
|
116
|
+
# my_posttype.add_post(
|
|
117
|
+
# title: "My Title", post_order: 5, content: 'lorem_ipsum',
|
|
118
|
+
# settings: {
|
|
119
|
+
# default_template: "home/counters", has_content: false, has_seo: false, skip_fields: ["sub_tite", 'banner']
|
|
120
|
+
# },
|
|
121
|
+
# fields: { pattern: true, bg: 'https://www.reallusion.com/de/images/3dx5/whatsnew/3dx5_features_banner_bg_02.jpg' }
|
|
122
|
+
# )
|
|
115
123
|
# More samples here: https://gist.github.com/owen2345/eba9691585ed78ad6f7b52e9591357bf
|
|
116
124
|
# return created post if it was created, else return errors
|
|
117
125
|
def add_post(args)
|
|
@@ -123,7 +131,7 @@ module CamaleonCms
|
|
|
123
131
|
args[:data_tags] = args.delete(:tags)
|
|
124
132
|
_thumb = args.delete(:thumb)
|
|
125
133
|
p = posts.new(args)
|
|
126
|
-
p.slug = site.get_valid_post_slug(p.title.parameterize)
|
|
134
|
+
p.slug = site.get_valid_post_slug(p.title.parameterize) if p.slug.blank?
|
|
127
135
|
if p.save!
|
|
128
136
|
_settings.each { |k, v| p.set_setting(k, v) } if _settings.present?
|
|
129
137
|
p.set_position(_order_position) if _order_position.present?
|
|
@@ -137,6 +145,7 @@ module CamaleonCms
|
|
|
137
145
|
end
|
|
138
146
|
|
|
139
147
|
# return all available route formats of this post type for content posts
|
|
148
|
+
# rubocop:disable Layout/LineLength
|
|
140
149
|
def contents_route_formats
|
|
141
150
|
{
|
|
142
151
|
'post_of_post_type' => '<code>/group/:post_type_id-:title/:slug</code><br> (Sample: https://localhost.com/group/17-services/myservice.html)',
|
|
@@ -147,6 +156,7 @@ module CamaleonCms
|
|
|
147
156
|
'hierarchy_post' => '<code>/:parent1_slug/:parent2_slug/.../:slug</code><br> (Sample: https://localhost.com/item-1/item-1-1/item-111.html)'
|
|
148
157
|
}
|
|
149
158
|
end
|
|
159
|
+
# rubocop:enable Layout/LineLength
|
|
150
160
|
|
|
151
161
|
# return the configuration of routes for post contents
|
|
152
162
|
def contents_route_format
|
|
@@ -179,7 +189,7 @@ module CamaleonCms
|
|
|
179
189
|
|
|
180
190
|
# destroy all custom field groups assigned to this post type
|
|
181
191
|
def destroy_field_groups
|
|
182
|
-
if
|
|
192
|
+
if destroyed_by_association.blank? && %w[post page].include?(slug)
|
|
183
193
|
errors.add(:base, 'This post type can not be deleted.')
|
|
184
194
|
return false
|
|
185
195
|
end
|