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
|
@@ -1,10 +1,49 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class Post < CamaleonCms::PostDefault
|
|
3
3
|
include CamaleonCms::CategoriesTagsForPosts
|
|
4
|
+
include CamaleonCms::ContentShortcodeGate
|
|
5
|
+
|
|
6
|
+
# Structural, non-executable markup that long-form post content legitimately uses but the
|
|
7
|
+
# sanitizer default drops. Superset of the default so upstream security additions are inherited.
|
|
8
|
+
SANITIZE_EXTRA_TAGS = %w[table thead tbody tfoot tr td th caption col colgroup
|
|
9
|
+
figure figcaption u s hr].freeze
|
|
10
|
+
# These attributes are allowed knowing what they cost, none of which is script execution:
|
|
11
|
+
# `style` is CSS-scrubbed by the gate's scrubber (expression(), url(javascript:) and friends
|
|
12
|
+
# register as removals) but permits absolutely-positioned overlays; `id` widens DOM clobbering,
|
|
13
|
+
# which the scrubber's own default `name` already opened; and `target` without a forced
|
|
14
|
+
# `rel=noopener` is reverse tabnabbing on browsers predating the implicit default. The trade
|
|
15
|
+
# is deliberate: an untrusted author whose content needs more than this list has the save
|
|
16
|
+
# refused, and the role that wants more grants post_content_unfiltered_html.
|
|
17
|
+
SANITIZE_EXTRA_ATTRIBUTES = %w[id style target rel colspan rowspan].freeze
|
|
18
|
+
CONTENT_ALLOWED_TAGS = (ActionController::Base.helpers.sanitizer_vendor.safe_list_sanitizer
|
|
19
|
+
.allowed_tags.to_a + SANITIZE_EXTRA_TAGS).uniq.freeze
|
|
20
|
+
CONTENT_ALLOWED_ATTRIBUTES = (ActionController::Base.helpers.sanitizer_vendor.safe_list_sanitizer
|
|
21
|
+
.allowed_attributes.to_a + SANITIZE_EXTRA_ATTRIBUTES).uniq.freeze
|
|
22
|
+
|
|
23
|
+
# Opt-out for trusted server-side pipelines (imports, seeds, plugin code) that would otherwise
|
|
24
|
+
# be sanitized by the fail-closed default. Exposed as a reader plus a bang enabler and NO
|
|
25
|
+
# `unfiltered_content=` writer, so `assign_attributes`/mass assignment cannot reach it — only
|
|
26
|
+
# explicit server-side code calling `post.unfiltered_content!` can.
|
|
27
|
+
#
|
|
28
|
+
# It is sticky for the lifetime of the object, not for one save: an instance that has been
|
|
29
|
+
# opted out stays opted out for every subsequent save of that instance. That is what a
|
|
30
|
+
# pipeline staging a record over several steps wants; code that needs the opt-out to apply
|
|
31
|
+
# once should not reuse the instance afterwards.
|
|
32
|
+
attr_reader :unfiltered_content
|
|
33
|
+
|
|
34
|
+
def unfiltered_content!
|
|
35
|
+
@unfiltered_content = true
|
|
36
|
+
self
|
|
37
|
+
end
|
|
4
38
|
|
|
5
39
|
alias_attribute :post_type_id, :taxonomy_id
|
|
6
40
|
default_scope -> { where(post_class: 'Post').order(post_order: :asc, created_at: :desc) }
|
|
7
41
|
|
|
42
|
+
validate :reject_untrusted_dangerous_content, on: %i[create update]
|
|
43
|
+
# Shortcodes in post content are expanded by do_shortcode at render (PostDecorator#the_content),
|
|
44
|
+
# an end-run around post_content_unfiltered_html; gate authorship behind content_shortcodes.
|
|
45
|
+
gate_content_shortcodes :content
|
|
46
|
+
|
|
8
47
|
# DEPRECATED
|
|
9
48
|
has_many :post_relationships, class_name: 'CamaleonCms::PostRelationship', foreign_key: :objectid,
|
|
10
49
|
dependent: :destroy, inverse_of: :post
|
|
@@ -14,16 +53,17 @@ module CamaleonCms
|
|
|
14
53
|
has_many :term_relationships, foreign_key: :objectid, dependent: :destroy, inverse_of: :object
|
|
15
54
|
has_many :categories, class_name: 'CamaleonCms::Category', through: :term_relationships, source: :term_taxonomy
|
|
16
55
|
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
|
|
56
|
+
has_many :comments, class_name: 'CamaleonCms::PostComment', dependent: :destroy
|
|
57
|
+
has_many :drafts, -> { where(status: 'draft_child') }, class_name: 'CamaleonCms::Post', inverse_of: :parent,
|
|
58
|
+
foreign_key: :post_parent, dependent: :destroy
|
|
21
59
|
has_many :children, class_name: 'CamaleonCms::Post', foreign_key: :post_parent, dependent: :destroy,
|
|
22
|
-
primary_key: :id
|
|
60
|
+
primary_key: :id, inverse_of: :parent
|
|
23
61
|
|
|
24
|
-
belongs_to :owner, class_name: CamaManager.get_user_class_name, foreign_key: :user_id,
|
|
25
|
-
|
|
26
|
-
belongs_to :
|
|
62
|
+
belongs_to :owner, class_name: CamaManager.get_user_class_name.to_s, foreign_key: :user_id, optional: true,
|
|
63
|
+
inverse_of: :all_posts
|
|
64
|
+
belongs_to :parent, class_name: 'CamaleonCms::Post', foreign_key: :post_parent, optional: true,
|
|
65
|
+
inverse_of: :children
|
|
66
|
+
belongs_to :post_type, foreign_key: :taxonomy_id, inverse_of: :posts, optional: true
|
|
27
67
|
|
|
28
68
|
scope :visible_frontend, -> { where(status: 'published') }
|
|
29
69
|
scope :public_posts, lambda {
|
|
@@ -38,11 +78,18 @@ module CamaleonCms
|
|
|
38
78
|
scope :drafts, -> { where(status: %w[draft draft_child]) }
|
|
39
79
|
scope :pending, -> { where(status: 'pending') }
|
|
40
80
|
scope :latest, -> { reorder(created_at: :desc) }
|
|
81
|
+
# `preload`, not `includes`: frontend listings run `the_posts` through this scope. If it were
|
|
82
|
+
# `includes`, a caller that later chains a join/`eager_load`/hash-`where` on one of these tables
|
|
83
|
+
# (e.g. PostDecorator#the_related_posts joins :categories) would flip the relation to
|
|
84
|
+
# `eager_loading?` -- one multi-way LEFT JOIN that loads a PARTIAL categories set per post and
|
|
85
|
+
# duplicates rows in `pluck`/`count`. `preload` issues separate queries, never merged into a join.
|
|
86
|
+
scope :with_eager, -> { preload(:metas, :categories, :post_tags, post_type: :metas) }
|
|
41
87
|
|
|
42
88
|
validates_with CamaleonCms::PostUniqValidator
|
|
43
89
|
attr_accessor :show_title_with_parent
|
|
44
90
|
|
|
45
|
-
before_create :fix_post_order, if: ->(p) {
|
|
91
|
+
before_create :fix_post_order, if: ->(p) { p.post_order.blank? || p.post_order == 0 }
|
|
92
|
+
before_save :set_published_at_on_publish
|
|
46
93
|
|
|
47
94
|
# return all parents for current page hierarchy ordered bottom to top
|
|
48
95
|
def parents
|
|
@@ -66,22 +113,22 @@ module CamaleonCms
|
|
|
66
113
|
end
|
|
67
114
|
end
|
|
68
115
|
|
|
69
|
-
#
|
|
116
|
+
# Return the post type of this post (DEPRECATED), though used yet in migration, so let it stay
|
|
70
117
|
def get_post_type_depre
|
|
71
118
|
post_types.reorder(nil).first
|
|
72
119
|
end
|
|
73
120
|
|
|
74
|
-
#
|
|
121
|
+
# Check if this post was published
|
|
75
122
|
def published?
|
|
76
123
|
status == 'published'
|
|
77
124
|
end
|
|
78
125
|
|
|
79
|
-
#
|
|
126
|
+
# Check if this post is in the pending status
|
|
80
127
|
def pending?
|
|
81
128
|
status == 'pending'
|
|
82
129
|
end
|
|
83
130
|
|
|
84
|
-
#
|
|
131
|
+
# Check if this post is in the draft status
|
|
85
132
|
def draft?
|
|
86
133
|
%w[draft draft_child].include?(status)
|
|
87
134
|
end
|
|
@@ -90,12 +137,12 @@ module CamaleonCms
|
|
|
90
137
|
status == 'draft_child'
|
|
91
138
|
end
|
|
92
139
|
|
|
93
|
-
#
|
|
140
|
+
# Check if this post is in the trash status
|
|
94
141
|
def trash?
|
|
95
142
|
status == 'trash'
|
|
96
143
|
end
|
|
97
144
|
|
|
98
|
-
#
|
|
145
|
+
# Check if the current post can manage content
|
|
99
146
|
# return boolean
|
|
100
147
|
def manage_content?(posttype = nil)
|
|
101
148
|
get_option('has_content', (posttype || post_type).get_option('has_content', true))
|
|
@@ -106,34 +153,35 @@ module CamaleonCms
|
|
|
106
153
|
get_option('has_layout', (posttype || post_type).get_option('has_layout', false))
|
|
107
154
|
end
|
|
108
155
|
|
|
109
|
-
#
|
|
156
|
+
# Check if current post can manage template
|
|
110
157
|
# return boolean
|
|
111
158
|
def manage_template?(posttype = nil)
|
|
112
159
|
get_option('has_template', (posttype || post_type).get_option('has_template', true))
|
|
113
160
|
end
|
|
114
161
|
|
|
115
|
-
#
|
|
162
|
+
# Check if the current post can manage summary
|
|
116
163
|
# return boolean
|
|
117
164
|
def manage_summary?(posttype = nil)
|
|
118
165
|
get_option('has_summary', (posttype || post_type).get_option('has_summary', true))
|
|
119
166
|
end
|
|
120
167
|
|
|
121
|
-
#
|
|
168
|
+
# Check if the current post can manage picture
|
|
122
169
|
# return boolean
|
|
123
170
|
def manage_picture?(posttype = nil)
|
|
124
171
|
get_option('has_picture', (posttype || post_type).get_option('has_picture', true))
|
|
125
172
|
end
|
|
126
173
|
|
|
127
|
-
#
|
|
174
|
+
# Check if the current post can manage comments
|
|
128
175
|
# return boolean
|
|
129
176
|
def manage_comments?(posttype = nil)
|
|
130
177
|
get_option('has_comments', (posttype || post_type).get_option('has_comments', false))
|
|
131
178
|
end
|
|
132
179
|
|
|
133
|
-
#
|
|
180
|
+
# Check if the post can be commented
|
|
134
181
|
# sample: @post.can_commented?
|
|
135
|
-
#
|
|
136
|
-
#
|
|
182
|
+
# to enable comments for current post, use this: post.set_meta('has_comments', '1').
|
|
183
|
+
# Note: Parent PostType should be enabled for comments too: post_type.set_option('has_comments', true)
|
|
184
|
+
# @return [TrueClass, FalseClass]
|
|
137
185
|
def can_commented?
|
|
138
186
|
manage_comments? && get_meta('has_comments').to_s == '1'
|
|
139
187
|
end
|
|
@@ -151,10 +199,15 @@ module CamaleonCms
|
|
|
151
199
|
# has_picture, boolean (default true)
|
|
152
200
|
# has_template, boolean (default false)
|
|
153
201
|
# has_comments, boolean (default false)
|
|
154
|
-
#
|
|
155
|
-
#
|
|
202
|
+
#
|
|
203
|
+
# the following is still used if post type was inactivated layout and overwritten by dropdown in post view
|
|
204
|
+
# default_layout: (string) (default layout)
|
|
205
|
+
#
|
|
206
|
+
# the following is still used if post type was inactivated template and overwritten by dropdown in post view
|
|
207
|
+
# default_template: (string) (default template)
|
|
156
208
|
# has_layout: (boolean) (default false)
|
|
157
|
-
# skip_fields: (array) (default empty) array of custom field keys to avoid for this post,
|
|
209
|
+
# skip_fields: (array) (default empty) array of custom field keys to avoid for this post,
|
|
210
|
+
# sample: ["subtitle", "icon"]
|
|
158
211
|
# val: value for the setting
|
|
159
212
|
def set_setting(key, val)
|
|
160
213
|
set_option(key, val)
|
|
@@ -171,7 +224,7 @@ module CamaleonCms
|
|
|
171
224
|
# new_order_position: (Integer) position number
|
|
172
225
|
# return nil
|
|
173
226
|
def set_position(new_order_position)
|
|
174
|
-
update_column(
|
|
227
|
+
update_column(:post_order, new_order_position) # rubocop:disable Rails/SkipsModelValidations
|
|
175
228
|
end
|
|
176
229
|
|
|
177
230
|
# save the summary for current post
|
|
@@ -247,10 +300,67 @@ module CamaleonCms
|
|
|
247
300
|
|
|
248
301
|
private
|
|
249
302
|
|
|
303
|
+
def trusted_for_unfiltered_html?
|
|
304
|
+
user = CurrentRequest.user
|
|
305
|
+
site = CurrentRequest.site
|
|
306
|
+
# Fail closed (sanitize) when either user or site context is missing - e.g. background jobs, rake tasks
|
|
307
|
+
# or the console. Guarding the site too avoids a NoMethodError from Ability#initialize (which dereferences
|
|
308
|
+
# the site for non-admin users) that would otherwise abort the whole save.
|
|
309
|
+
return false if user.blank? || site.blank?
|
|
310
|
+
|
|
311
|
+
CamaleonCms::Ability.new(user, site).can?(:post_content_unfiltered_html, post_type)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Security (scan-and-reject policy): content an untrusted author is not permitted to write is
|
|
315
|
+
# refused with a validation error -- never sanitized or rewritten -- so stored content always
|
|
316
|
+
# equals authored content and the frontend may render it verbatim. The guard on
|
|
317
|
+
# `content_changed?` deliberately leaves pre-gate stored content editable (a title fix on an
|
|
318
|
+
# old post must not brick it); `rake camaleon_cms:security:scan_content` lists such content.
|
|
319
|
+
def reject_untrusted_dangerous_content
|
|
320
|
+
return unless new_record? || content_changed?
|
|
321
|
+
return if content.blank?
|
|
322
|
+
# The explicit pipeline opt-out short-circuits before the Ability lookup below.
|
|
323
|
+
return if unfiltered_content
|
|
324
|
+
# Check trust only once we know there is content to gate, so unchanged/blank updates never pay for
|
|
325
|
+
# building an Ability (a role-meta DB lookup) on every save.
|
|
326
|
+
return if trusted_for_unfiltered_html?
|
|
327
|
+
|
|
328
|
+
# Distinguish an over-size refusal from a disallowed-markup refusal (audit M16): an over-size
|
|
329
|
+
# value can be perfectly clean, so the markup message (scripts/handlers/embeds) would misdescribe
|
|
330
|
+
# it and send the author hunting for markup that is not there.
|
|
331
|
+
if CamaleonCms::UnsafeMarkup.too_large?(content)
|
|
332
|
+
errors.add(:content, cama_content_rejection_message('content_too_large'))
|
|
333
|
+
elsif CamaleonCms::UnsafeMarkup.unsafe_html?(
|
|
334
|
+
content, tags: CONTENT_ALLOWED_TAGS, attributes: CONTENT_ALLOWED_ATTRIBUTES
|
|
335
|
+
)
|
|
336
|
+
errors.add(:content, cama_content_rejection_message('content_rejected'))
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# Only en.yml carries these keys while the process locale follows the current admin/site
|
|
341
|
+
# language — fall back to English rather than emit "translation missing".
|
|
342
|
+
def cama_content_rejection_message(key)
|
|
343
|
+
full_key = "camaleon_cms.admin.post.message.#{key}"
|
|
344
|
+
I18n.t(full_key, default: I18n.t(full_key, locale: :en))
|
|
345
|
+
end
|
|
346
|
+
|
|
250
347
|
# calculate a post order when it is empty
|
|
251
348
|
def fix_post_order
|
|
252
349
|
last_post = post_type.posts.where.not(id: nil).last
|
|
253
|
-
self.post_order = last_post.present? ? last_post.post_order + 1 : 1
|
|
350
|
+
self.post_order = last_post.present? ? (last_post.post_order || 0) + 1 : 1
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Stamp the publish time at the moment a post becomes published without an explicit date --
|
|
354
|
+
# the admin Publish action, or any save that transitions the status to 'published'. Keyed to
|
|
355
|
+
# the status change (or the record being born published), not to every save, so a post created
|
|
356
|
+
# as a draft keeps a nil published_at until it is actually published, a caller-supplied date
|
|
357
|
+
# (e.g. a scheduled post) is preserved, and later edits of an already-published post keep the
|
|
358
|
+
# original date.
|
|
359
|
+
def set_published_at_on_publish
|
|
360
|
+
return unless status == 'published' && published_at.blank?
|
|
361
|
+
return unless new_record? || will_save_change_to_status?
|
|
362
|
+
|
|
363
|
+
self.published_at = Time.current.utc
|
|
254
364
|
end
|
|
255
365
|
end
|
|
256
366
|
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,11 +1,10 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class PostTag < CamaleonCms::TermTaxonomy
|
|
3
|
-
normalize_attrs(:
|
|
4
|
-
|
|
5
|
-
default_scope { where(taxonomy: :post_tag) }
|
|
3
|
+
normalize_attrs(:description)
|
|
6
4
|
|
|
7
5
|
has_many :posts, foreign_key: :objectid, through: :term_relationships, source: :object
|
|
8
|
-
belongs_to :post_type, foreign_key: :parent_id, inverse_of: :post_tags,
|
|
9
|
-
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
|
|
10
9
|
end
|
|
11
10
|
end
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class PostType < CamaleonCms::TermTaxonomy
|
|
3
|
-
normalize_attrs(:
|
|
3
|
+
normalize_attrs(:description)
|
|
4
4
|
|
|
5
5
|
alias_attribute :site_id, :parent_id
|
|
6
|
-
default_scope { where(taxonomy: :post_type) }
|
|
7
6
|
|
|
8
7
|
has_many :categories, foreign_key: :parent_id, dependent: :destroy, inverse_of: :post_type_parent
|
|
9
8
|
has_many :post_tags, foreign_key: :parent_id, dependent: :destroy, inverse_of: :post_type
|
|
@@ -12,12 +11,12 @@ module CamaleonCms
|
|
|
12
11
|
has_many :posts_through_categories, foreign_key: :objectid, through: :term_relationships, source: :object
|
|
13
12
|
has_many :posts_draft, class_name: 'CamaleonCms::Post', foreign_key: :taxonomy_id, dependent: :destroy,
|
|
14
13
|
inverse_of: :post_type
|
|
15
|
-
has_many :field_group_taxonomy,
|
|
16
|
-
|
|
17
|
-
}, 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
|
|
18
16
|
|
|
19
|
-
belongs_to :owner, class_name: CamaManager.get_user_class_name, foreign_key: :user_id,
|
|
20
|
-
|
|
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
|
|
21
20
|
|
|
22
21
|
scope :visible_menu, -> { where(term_group: nil) }
|
|
23
22
|
scope :hidden_menu, -> { where(term_group: -1) }
|
|
@@ -92,7 +91,7 @@ module CamaleonCms
|
|
|
92
91
|
def default_category
|
|
93
92
|
return unless manage_categories?
|
|
94
93
|
|
|
95
|
-
cat = categories.find_by_slug('uncategorized')
|
|
94
|
+
cat = categories.find_by_slug('uncategorized') # rubocop:disable Rails/DynamicFindBy
|
|
96
95
|
unless cat
|
|
97
96
|
cat = categories.create({ name: 'Uncategorized', slug: 'uncategorized', parent_id: id })
|
|
98
97
|
cat.set_option('not_deleted', true)
|
|
@@ -110,10 +109,17 @@ module CamaleonCms
|
|
|
110
109
|
# summary: String resume (optional) => default (empty)
|
|
111
110
|
# post_order: Integer to define the order position in the list (optional)
|
|
112
111
|
# fields: Hash of values for custom fields, sample => fields: {subtitle: 'abc', icon: 'test' } (optional)
|
|
113
|
-
# settings: Hash of post settings, sample => settings:
|
|
114
|
-
# {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' }
|
|
115
114
|
# data_metas: {template: "", layout: ""}
|
|
116
|
-
# 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
|
+
# )
|
|
117
123
|
# More samples here: https://gist.github.com/owen2345/eba9691585ed78ad6f7b52e9591357bf
|
|
118
124
|
# return created post if it was created, else return errors
|
|
119
125
|
def add_post(args)
|
|
@@ -125,7 +131,7 @@ module CamaleonCms
|
|
|
125
131
|
args[:data_tags] = args.delete(:tags)
|
|
126
132
|
_thumb = args.delete(:thumb)
|
|
127
133
|
p = posts.new(args)
|
|
128
|
-
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?
|
|
129
135
|
if p.save!
|
|
130
136
|
_settings.each { |k, v| p.set_setting(k, v) } if _settings.present?
|
|
131
137
|
p.set_position(_order_position) if _order_position.present?
|
|
@@ -139,6 +145,7 @@ module CamaleonCms
|
|
|
139
145
|
end
|
|
140
146
|
|
|
141
147
|
# return all available route formats of this post type for content posts
|
|
148
|
+
# rubocop:disable Layout/LineLength
|
|
142
149
|
def contents_route_formats
|
|
143
150
|
{
|
|
144
151
|
'post_of_post_type' => '<code>/group/:post_type_id-:title/:slug</code><br> (Sample: https://localhost.com/group/17-services/myservice.html)',
|
|
@@ -149,6 +156,7 @@ module CamaleonCms
|
|
|
149
156
|
'hierarchy_post' => '<code>/:parent1_slug/:parent2_slug/.../:slug</code><br> (Sample: https://localhost.com/item-1/item-1-1/item-111.html)'
|
|
150
157
|
}
|
|
151
158
|
end
|
|
159
|
+
# rubocop:enable Layout/LineLength
|
|
152
160
|
|
|
153
161
|
# return the configuration of routes for post contents
|
|
154
162
|
def contents_route_format
|
|
@@ -181,7 +189,7 @@ module CamaleonCms
|
|
|
181
189
|
|
|
182
190
|
# destroy all custom field groups assigned to this post type
|
|
183
191
|
def destroy_field_groups
|
|
184
|
-
if
|
|
192
|
+
if destroyed_by_association.blank? && %w[post page].include?(slug)
|
|
185
193
|
errors.add(:base, 'This post type can not be deleted.')
|
|
186
194
|
return false
|
|
187
195
|
end
|