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
|
@@ -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,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
|
|
@@ -1,32 +1,45 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
class Site < CamaleonCms::TermTaxonomy
|
|
3
|
-
normalize_attrs(:
|
|
3
|
+
normalize_attrs(:description)
|
|
4
4
|
|
|
5
5
|
include CamaleonCms::SiteDefaultSettings
|
|
6
6
|
|
|
7
7
|
# attrs: [name, description, slug]
|
|
8
8
|
attr_accessor :site_domain
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
# Transient (never persisted): when default_settings auto-provisions the
|
|
11
|
+
# administrator, it stashes the randomly generated plaintext password here so
|
|
12
|
+
# the provisioning controller can surface it once. nil when no admin was minted
|
|
13
|
+
# (e.g. a shared admin already existed). See harden-installer-default-admin.
|
|
14
|
+
attr_accessor :generated_admin_password
|
|
11
15
|
|
|
12
|
-
has_many :post_types, class_name: 'CamaleonCms::PostType', foreign_key: :parent_id, dependent: :destroy
|
|
16
|
+
has_many :post_types, class_name: 'CamaleonCms::PostType', foreign_key: :parent_id, dependent: :destroy,
|
|
17
|
+
inverse_of: :site
|
|
13
18
|
has_many :nav_menus, class_name: 'CamaleonCms::NavMenu', foreign_key: :parent_id, dependent: :destroy,
|
|
14
19
|
inverse_of: :site
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
has_many :
|
|
18
|
-
|
|
20
|
+
# inverse_of: false — this joins by term_group (the site id), while NavMenuItem#parent
|
|
21
|
+
# reads parent_id (the menu id); an inverse here overwrites items' real menu parent.
|
|
22
|
+
has_many :nav_menu_items, class_name: 'CamaleonCms::NavMenuItem', foreign_key: :term_group, inverse_of: false,
|
|
23
|
+
dependent: :destroy
|
|
24
|
+
has_many :widgets, class_name: 'CamaleonCms::Widget::Main', foreign_key: :parent_id, dependent: :destroy,
|
|
25
|
+
inverse_of: :site
|
|
26
|
+
has_many :sidebars, class_name: 'CamaleonCms::Widget::Sidebar', foreign_key: :parent_id, dependent: :destroy,
|
|
27
|
+
inverse_of: :site
|
|
28
|
+
has_many :user_roles_rel, class_name: 'CamaleonCms::UserRole', foreign_key: :parent_id, dependent: :destroy,
|
|
29
|
+
inverse_of: :site
|
|
19
30
|
has_many :custom_field_groups, class_name: 'CamaleonCms::CustomFieldGroup', foreign_key: :parent_id,
|
|
20
|
-
dependent: :destroy
|
|
21
|
-
has_many :term_taxonomies, class_name: 'CamaleonCms::TermTaxonomy', foreign_key: :parent_id
|
|
31
|
+
dependent: :destroy, inverse_of: :parent
|
|
32
|
+
has_many :term_taxonomies, class_name: 'CamaleonCms::TermTaxonomy', foreign_key: :parent_id, inverse_of: :parent,
|
|
33
|
+
dependent: :destroy
|
|
22
34
|
|
|
23
35
|
has_many :posts, through: :post_types, source: :posts
|
|
24
|
-
has_many :plugins, class_name: 'CamaleonCms::Plugin', foreign_key: :parent_id, dependent: :destroy
|
|
25
|
-
|
|
26
|
-
has_many :
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
has_many :plugins, class_name: 'CamaleonCms::Plugin', foreign_key: :parent_id, dependent: :destroy,
|
|
37
|
+
inverse_of: :site
|
|
38
|
+
has_many :themes, class_name: 'CamaleonCms::Theme', foreign_key: :parent_id, dependent: :destroy, inverse_of: :site
|
|
39
|
+
has_many :public_media, -> { where(is_public: true) }, class_name: 'CamaleonCms::Media', foreign_key: :site_id,
|
|
40
|
+
dependent: :destroy, inverse_of: :site
|
|
41
|
+
has_many :private_media, -> { where(is_public: false) }, class_name: 'CamaleonCms::Media', foreign_key: :site_id,
|
|
42
|
+
dependent: :destroy, inverse_of: :site
|
|
30
43
|
|
|
31
44
|
after_create :default_settings
|
|
32
45
|
after_create :set_default_user_roles
|
|
@@ -35,7 +48,7 @@ module CamaleonCms
|
|
|
35
48
|
before_destroy :destroy_site
|
|
36
49
|
after_destroy :refresh_routes
|
|
37
50
|
|
|
38
|
-
|
|
51
|
+
validates :slug, uniqueness: { scope: :taxonomy }
|
|
39
52
|
|
|
40
53
|
# all user roles for this site
|
|
41
54
|
def user_roles
|
|
@@ -79,7 +92,7 @@ module CamaleonCms
|
|
|
79
92
|
options[:_admin_theme] || 'en'
|
|
80
93
|
end
|
|
81
94
|
|
|
82
|
-
#
|
|
95
|
+
# Set the current admin language for this site
|
|
83
96
|
def set_admin_language(language)
|
|
84
97
|
set_option('_admin_theme', language)
|
|
85
98
|
end
|
|
@@ -149,11 +162,9 @@ module CamaleonCms
|
|
|
149
162
|
|
|
150
163
|
# list all users of current site
|
|
151
164
|
def users
|
|
152
|
-
if PluginRoutes.system_info['users_share_sites']
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
CamaleonCms::User.where(site_id: id)
|
|
156
|
-
end
|
|
165
|
+
return CamaleonCms::User.all if PluginRoutes.system_info['users_share_sites']
|
|
166
|
+
|
|
167
|
+
CamaleonCms::User.where(site_id: id)
|
|
157
168
|
end
|
|
158
169
|
alias users_include_admins users
|
|
159
170
|
|
|
@@ -176,15 +187,13 @@ module CamaleonCms
|
|
|
176
187
|
def get_valid_post_slug(slug, post_id = nil)
|
|
177
188
|
slugs = slug.translations
|
|
178
189
|
if slugs.present?
|
|
179
|
-
slugs.each
|
|
180
|
-
slugs[k] = get_valid_post_slug(v)
|
|
181
|
-
end
|
|
190
|
+
slugs.each { |k, v| slugs[k] = get_valid_post_slug(v) }
|
|
182
191
|
slugs.to_translate
|
|
183
192
|
else
|
|
184
193
|
res = slug
|
|
185
194
|
(1..9999).each do |i|
|
|
186
|
-
p = posts.find_by_slug(res)
|
|
187
|
-
break if
|
|
195
|
+
p = posts.find_by_slug(res) # rubocop:disable Rails/DynamicFindBy
|
|
196
|
+
break if p.blank? || (p.present? && p.id == post_id)
|
|
188
197
|
|
|
189
198
|
res = "#{slug}-#{i}"
|
|
190
199
|
end
|
|
@@ -211,7 +220,7 @@ module CamaleonCms
|
|
|
211
220
|
# if the anonymous user not exist, will create one
|
|
212
221
|
def get_anonymous_user
|
|
213
222
|
user = users.where(username: 'anonymous').first
|
|
214
|
-
|
|
223
|
+
if user.blank?
|
|
215
224
|
pass = "anonymous#{rand(9999)}"
|
|
216
225
|
user = users.create({ email: 'anonymous_user@local.com', username: 'anonymous', password: pass,
|
|
217
226
|
password_confirmation: pass, first_name: 'Anonymous' })
|
|
@@ -221,7 +230,8 @@ module CamaleonCms
|
|
|
221
230
|
|
|
222
231
|
# return the domain for current site
|
|
223
232
|
# sample: mysite.com | sample.mysite.com
|
|
224
|
-
# also, you can define custom domain for this site by: my_site.site_domain = 'my_site.com'
|
|
233
|
+
# also, you can define custom domain for this site by: my_site.site_domain = 'my_site.com'
|
|
234
|
+
# used for sites with different domains to call from console or task
|
|
225
235
|
def get_domain
|
|
226
236
|
@site_domain || (if main_site?
|
|
227
237
|
slug
|