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.
Files changed (241) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -9
  3. data/Rakefile +3 -16
  4. data/app/apps/plugins/attack/admin_controller.rb +9 -6
  5. data/app/apps/plugins/attack/attack_helper.rb +35 -25
  6. data/app/apps/plugins/authoring_post/authoring_post_helper.rb +23 -16
  7. data/app/apps/plugins/front_cache/admin_controller.rb +16 -10
  8. data/app/apps/plugins/front_cache/config/initializer.rb +1 -1
  9. data/app/apps/plugins/front_cache/front_cache_helper.rb +57 -24
  10. data/app/apps/plugins/visibility_post/config/config.json +1 -0
  11. data/app/apps/plugins/visibility_post/config/routes_front.txt +1 -0
  12. data/app/apps/plugins/visibility_post/front_controller.rb +23 -0
  13. data/app/apps/plugins/visibility_post/visibility_post_helper.rb +141 -70
  14. data/app/apps/themes/camaleon_first/main_helper.rb +18 -12
  15. data/app/apps/themes/default/assets/css/style.css.scss +24 -0
  16. data/app/apps/themes/default/assets/genericons/genericons.css +3 -8
  17. data/app/apps/themes/default/default_helper.rb +2 -1
  18. data/app/apps/themes/default/views/category.html.erb +1 -1
  19. data/app/apps/themes/default/views/layouts/index.html.erb +3 -1
  20. data/app/apps/themes/default/views/post_tag.html.erb +1 -1
  21. data/app/apps/themes/default/views/post_type.html.erb +1 -1
  22. data/app/apps/themes/default/views/search.html.erb +1 -1
  23. data/app/apps/themes/new/assets/css/styles.css.scss +24 -0
  24. data/app/apps/themes/new/custom_helper.rb +5 -9
  25. data/app/apps/themes/new/views/category.html.erb +1 -1
  26. data/app/apps/themes/new/views/layouts/_header.html.erb +3 -1
  27. data/app/apps/themes/new/views/post_tag.html.erb +1 -1
  28. data/app/apps/themes/new/views/post_type.html.erb +1 -1
  29. data/app/apps/themes/new/views/search.html.erb +1 -1
  30. data/app/assets/javascripts/camaleon_cms/admin/_data.js +16 -0
  31. data/app/assets/javascripts/camaleon_cms/admin/_post.js +5 -2
  32. data/app/assets/javascripts/camaleon_cms/admin/nav_menu.js +14 -3
  33. data/app/assets/javascripts/camaleon_cms/admin/uploader/_media_manager.js +5 -0
  34. data/app/controllers/camaleon_cms/admin/appearances/nav_menus_controller.rb +29 -10
  35. data/app/controllers/camaleon_cms/admin/appearances/themes_controller.rb +8 -5
  36. data/app/controllers/camaleon_cms/admin/appearances/widgets/assign_controller.rb +9 -2
  37. data/app/controllers/camaleon_cms/admin/appearances/widgets/main_controller.rb +4 -3
  38. data/app/controllers/camaleon_cms/admin/appearances/widgets/sidebar_controller.rb +2 -2
  39. data/app/controllers/camaleon_cms/admin/categories_controller.rb +27 -8
  40. data/app/controllers/camaleon_cms/admin/comments_controller.rb +0 -1
  41. data/app/controllers/camaleon_cms/admin/installers_controller.rb +32 -1
  42. data/app/controllers/camaleon_cms/admin/media_controller.rb +84 -19
  43. data/app/controllers/camaleon_cms/admin/plugins_controller.rb +1 -3
  44. data/app/controllers/camaleon_cms/admin/post_tags_controller.rb +15 -6
  45. data/app/controllers/camaleon_cms/admin/posts/drafts_controller.rb +40 -11
  46. data/app/controllers/camaleon_cms/admin/posts_controller.rb +58 -22
  47. data/app/controllers/camaleon_cms/admin/sessions_controller.rb +238 -45
  48. data/app/controllers/camaleon_cms/admin/settings/custom_fields_controller.rb +103 -18
  49. data/app/controllers/camaleon_cms/admin/settings/post_types_controller.rb +17 -5
  50. data/app/controllers/camaleon_cms/admin/settings/sites_controller.rb +33 -7
  51. data/app/controllers/camaleon_cms/admin/settings_controller.rb +11 -6
  52. data/app/controllers/camaleon_cms/admin/user_roles_controller.rb +9 -7
  53. data/app/controllers/camaleon_cms/admin/users_controller.rb +89 -18
  54. data/app/controllers/camaleon_cms/admin_controller.rb +144 -26
  55. data/app/controllers/camaleon_cms/apps/plugins_admin_controller.rb +5 -0
  56. data/app/controllers/camaleon_cms/camaleon_controller.rb +37 -92
  57. data/app/controllers/camaleon_cms/frontend_controller.rb +140 -70
  58. data/app/controllers/concerns/camaleon_cms/admin/custom_fields_concern.rb +44 -0
  59. data/app/controllers/concerns/camaleon_cms/frontend_concern.rb +46 -17
  60. data/app/controllers/concerns/camaleon_cms/frontend_visited_state_concern.rb +72 -0
  61. data/app/controllers/concerns/camaleon_cms/hook_lifecycle_concern.rb +104 -0
  62. data/app/controllers/concerns/camaleon_cms/request_context_concern.rb +113 -0
  63. data/app/controllers/concerns/camaleon_cms/runtime_admin_menu_concern.rb +237 -0
  64. data/app/controllers/concerns/camaleon_cms/runtime_captcha_image_concern.rb +13 -0
  65. data/app/controllers/concerns/camaleon_cms/runtime_html_content_concern.rb +143 -0
  66. data/app/controllers/concerns/camaleon_cms/runtime_shortcode_theme_concern.rb +246 -0
  67. data/app/controllers/concerns/camaleon_cms/runtime_state_concern.rb +13 -0
  68. data/app/controllers/concerns/camaleon_cms/runtime_uploader_concern.rb +54 -0
  69. data/app/controllers/concerns/camaleon_cms/session_captcha_runtime_concern.rb +19 -0
  70. data/app/controllers/concerns/camaleon_cms/session_runtime_concern.rb +40 -0
  71. data/app/decorators/camaleon_cms/application_decorator.rb +11 -8
  72. data/app/decorators/camaleon_cms/category_decorator.rb +3 -2
  73. data/app/decorators/camaleon_cms/post_comment_decorator.rb +13 -5
  74. data/app/decorators/camaleon_cms/post_decorator.rb +67 -51
  75. data/app/decorators/camaleon_cms/post_type_decorator.rb +1 -1
  76. data/app/decorators/camaleon_cms/site_decorator.rb +29 -23
  77. data/app/decorators/camaleon_cms/term_taxonomy_decorator.rb +21 -10
  78. data/app/decorators/camaleon_cms/theme_decorator.rb +3 -3
  79. data/app/decorators/camaleon_cms/user_decorator.rb +26 -5
  80. data/app/helpers/camaleon_cms/admin/application_helper.rb +21 -24
  81. data/app/helpers/camaleon_cms/admin/breadcrumb_helper.rb +1 -1
  82. data/app/helpers/camaleon_cms/admin/custom_fields_helper.rb +24 -23
  83. data/app/helpers/camaleon_cms/admin/menus_helper.rb +249 -139
  84. data/app/helpers/camaleon_cms/admin/post_type_helper.rb +51 -31
  85. data/app/helpers/camaleon_cms/camaleon_helper.rb +31 -39
  86. data/app/helpers/camaleon_cms/captcha_helper.rb +73 -53
  87. data/app/helpers/camaleon_cms/comment_helper.rb +87 -41
  88. data/app/helpers/camaleon_cms/content_helper.rb +13 -6
  89. data/app/helpers/camaleon_cms/email_helper.rb +7 -3
  90. data/app/helpers/camaleon_cms/frontend/content_select_helper.rb +34 -20
  91. data/app/helpers/camaleon_cms/frontend/nav_menu_helper.rb +144 -67
  92. data/app/helpers/camaleon_cms/frontend/seo_helper.rb +39 -28
  93. data/app/helpers/camaleon_cms/frontend/site_helper.rb +32 -12
  94. data/app/helpers/camaleon_cms/hooks_helper.rb +14 -4
  95. data/app/helpers/camaleon_cms/html_helper.rb +73 -38
  96. data/app/helpers/camaleon_cms/plugins_helper.rb +23 -24
  97. data/app/helpers/camaleon_cms/session_helper.rb +195 -39
  98. data/app/helpers/camaleon_cms/short_code_helper.rb +181 -85
  99. data/app/helpers/camaleon_cms/site_helper.rb +43 -9
  100. data/app/helpers/camaleon_cms/theme_helper.rb +35 -19
  101. data/app/helpers/camaleon_cms/uploader_helper.rb +19 -422
  102. data/app/helpers/camaleon_cms/user_roles_helper.rb +5 -4
  103. data/app/mailers/camaleon_cms/html_mailer.rb +4 -4
  104. data/app/models/camaleon_cms/ability.rb +65 -105
  105. data/app/models/camaleon_cms/category.rb +7 -5
  106. data/app/models/camaleon_cms/custom_field.rb +29 -6
  107. data/app/models/camaleon_cms/custom_field_group.rb +75 -21
  108. data/app/models/camaleon_cms/custom_fields_relationship.rb +133 -2
  109. data/app/models/camaleon_cms/media.rb +8 -2
  110. data/app/models/camaleon_cms/meta.rb +2 -2
  111. data/app/models/camaleon_cms/nav_menu.rb +6 -2
  112. data/app/models/camaleon_cms/nav_menu_item.rb +18 -8
  113. data/app/models/camaleon_cms/plugin.rb +4 -3
  114. data/app/models/camaleon_cms/post.rb +113 -27
  115. data/app/models/camaleon_cms/post_comment.rb +10 -6
  116. data/app/models/camaleon_cms/post_default.rb +42 -12
  117. data/app/models/camaleon_cms/post_relationship.rb +6 -4
  118. data/app/models/camaleon_cms/post_tag.rb +4 -3
  119. data/app/models/camaleon_cms/post_type.rb +22 -12
  120. data/app/models/camaleon_cms/site.rb +40 -28
  121. data/app/models/camaleon_cms/term_relationship.rb +10 -7
  122. data/app/models/camaleon_cms/term_taxonomy.rb +51 -5
  123. data/app/models/camaleon_cms/theme.rb +3 -3
  124. data/app/models/camaleon_cms/user.rb +17 -5
  125. data/app/models/camaleon_cms/user_role.rb +77 -4
  126. data/app/models/camaleon_cms/widget/assigned.rb +11 -6
  127. data/app/models/camaleon_cms/widget/main.rb +13 -7
  128. data/app/models/camaleon_cms/widget/sidebar.rb +3 -6
  129. data/app/models/camaleon_record.rb +123 -5
  130. data/app/models/concerns/camaleon_cms/categories_tags_for_posts.rb +20 -38
  131. data/app/models/concerns/camaleon_cms/common_relationships.rb +18 -5
  132. data/app/models/concerns/camaleon_cms/custom_fields_read.rb +115 -58
  133. data/app/models/concerns/camaleon_cms/metas.rb +32 -7
  134. data/app/models/concerns/camaleon_cms/normalize_attrs.rb +2 -8
  135. data/app/models/concerns/camaleon_cms/site_default_settings.rb +120 -16
  136. data/app/models/concerns/camaleon_cms/user_methods.rb +44 -19
  137. data/app/models/current_request.rb +23 -0
  138. data/app/uploaders/camaleon_cms_aws_uploader.rb +39 -11
  139. data/app/uploaders/camaleon_cms_local_uploader.rb +76 -19
  140. data/app/uploaders/camaleon_cms_uploader.rb +23 -10
  141. data/app/validators/camaleon_cms/post_uniq_validator.rb +21 -8
  142. data/app/validators/camaleon_cms/uniq_validator.rb +1 -1
  143. data/app/validators/camaleon_cms/user_url_validator.rb +215 -36
  144. data/app/views/camaleon_cms/admin/appearances/nav_menus/_left_menu_items.html.erb +2 -2
  145. data/app/views/camaleon_cms/admin/appearances/widgets/main/form.html.erb +1 -1
  146. data/app/views/camaleon_cms/admin/categories/index.html.erb +3 -3
  147. data/app/views/camaleon_cms/admin/comments/index.html.erb +2 -2
  148. data/app/views/camaleon_cms/admin/comments/list.html.erb +1 -1
  149. data/app/views/camaleon_cms/admin/installers/form.html.erb +5 -0
  150. data/app/views/camaleon_cms/admin/installers/welcome.html.erb +30 -6
  151. data/app/views/camaleon_cms/admin/plugins/_plugins_list.html.erb +2 -2
  152. data/app/views/camaleon_cms/admin/post_tags/index.html.erb +3 -3
  153. data/app/views/camaleon_cms/admin/posts/_sidebar.html.erb +2 -2
  154. data/app/views/camaleon_cms/admin/posts/form.html.erb +3 -2
  155. data/app/views/camaleon_cms/admin/posts/index.html.erb +6 -6
  156. data/app/views/camaleon_cms/admin/search.html.erb +1 -1
  157. data/app/views/camaleon_cms/admin/sessions/back_to_parent.html.erb +39 -0
  158. data/app/views/camaleon_cms/admin/sessions/logout_confirm.html.erb +27 -0
  159. data/app/views/camaleon_cms/admin/settings/_email_settings.html.erb +4 -1
  160. data/app/views/camaleon_cms/admin/settings/custom_fields/_get_items.html.erb +1 -1
  161. data/app/views/camaleon_cms/admin/settings/custom_fields/_render.html.erb +42 -6
  162. data/app/views/camaleon_cms/admin/settings/custom_fields/_render_category_simple.html.erb +1 -1
  163. data/app/views/camaleon_cms/admin/settings/custom_fields/fields/_select_eval.html.erb +1 -1
  164. data/app/views/camaleon_cms/admin/settings/custom_fields/form.html.erb +5 -4
  165. data/app/views/camaleon_cms/admin/settings/custom_fields/index.html.erb +1 -1
  166. data/app/views/camaleon_cms/admin/settings/post_types/index.html.erb +1 -1
  167. data/app/views/camaleon_cms/admin/settings/shortcodes.html.erb +4 -4
  168. data/app/views/camaleon_cms/admin/settings/site.html.erb +1 -1
  169. data/app/views/camaleon_cms/admin/settings/sites/form.html.erb +1 -1
  170. data/app/views/camaleon_cms/admin/settings/sites/index.html.erb +1 -1
  171. data/app/views/camaleon_cms/admin/user_roles/form.html.erb +102 -7
  172. data/app/views/camaleon_cms/admin/user_roles/index.html.erb +1 -1
  173. data/app/views/camaleon_cms/admin/users/form.html.erb +8 -4
  174. data/app/views/camaleon_cms/admin/users/index.html.erb +2 -2
  175. data/app/views/camaleon_cms/default_theme/partials/_render_custom_field.html.erb +8 -2
  176. data/app/views/camaleon_cms/default_theme/sitemap.html.erb +1 -1
  177. data/app/views/camaleon_cms/default_theme/sitemap.xml.builder +1 -1
  178. data/app/views/layouts/camaleon_cms/admin/_flash_messages.html.erb +2 -2
  179. data/app/views/layouts/camaleon_cms/admin/_header.html.erb +1 -1
  180. data/config/initializers/action_view.rb +42 -5
  181. data/config/initializers/active_record_extension.rb +13 -27
  182. data/config/initializers/assets.rb +52 -23
  183. data/config/initializers/custom_initializers.rb +3 -3
  184. data/config/locales/camaleon_cms/admin/ar.yml +7 -2
  185. data/config/locales/camaleon_cms/admin/de.yml +7 -2
  186. data/config/locales/camaleon_cms/admin/en.yml +39 -2
  187. data/config/locales/camaleon_cms/admin/es.yml +7 -2
  188. data/config/locales/camaleon_cms/admin/fr.yml +7 -2
  189. data/config/locales/camaleon_cms/admin/it.yml +7 -2
  190. data/config/locales/camaleon_cms/admin/nl.yml +8 -2
  191. data/config/locales/camaleon_cms/admin/pt-BR.yml +7 -2
  192. data/config/locales/camaleon_cms/admin/pt.yml +7 -2
  193. data/config/locales/camaleon_cms/admin/ru.yml +7 -2
  194. data/config/locales/camaleon_cms/admin/uk.yml +7 -2
  195. data/config/locales/camaleon_cms/admin/zh-CH.yml +7 -2
  196. data/config/routes/admin.rb +33 -15
  197. data/config/routes/frontend.rb +21 -15
  198. data/db/migrate/20150611161134_post_table_into_utf8.rb +14 -14
  199. data/db/migrate/20150926095310_rename_column_posts.rb +3 -3
  200. data/db/migrate/20151212095328_add_confirm_token_to_users.rb +3 -3
  201. data/db/migrate/20160504155652_add_feature_to_posts.rb +1 -1
  202. data/db/migrate/20160504155653_move_first_name_of_users.rb +2 -2
  203. data/db/migrate/20160609121449_add_group_to_custom_field_values.rb +1 -1
  204. data/db/migrate/20161215202255_drop_user_relationship_table.rb +1 -1
  205. data/db/migrate/20180124132318_create_media.rb +1 -1
  206. data/db/migrate/20180704211100_adjust_field_length.rb +1 -1
  207. data/lib/camaleon_cms/assets_precompile.rb +86 -0
  208. data/lib/camaleon_cms/captcha_image_generation.rb +65 -0
  209. data/lib/camaleon_cms/content_security.rb +158 -0
  210. data/lib/camaleon_cms/engine.rb +47 -9
  211. data/lib/camaleon_cms/media_security_headers.rb +27 -0
  212. data/lib/camaleon_cms/setup_token.rb +69 -0
  213. data/lib/camaleon_cms/svg_content_checker.rb +123 -0
  214. data/lib/camaleon_cms/task_reporter.rb +14 -0
  215. data/lib/camaleon_cms/unsafe_markup.rb +207 -0
  216. data/lib/camaleon_cms/uploader_content_security.rb +248 -0
  217. data/lib/camaleon_cms/uploader_image_processing.rb +166 -0
  218. data/lib/camaleon_cms/uploader_path_security.rb +146 -0
  219. data/lib/camaleon_cms/uploader_pipeline.rb +350 -0
  220. data/lib/camaleon_cms/uploader_support.rb +86 -0
  221. data/lib/camaleon_cms/version.rb +1 -1
  222. data/lib/camaleon_cms.rb +11 -0
  223. data/lib/ext/hash.rb +28 -2
  224. data/lib/ext/string.rb +13 -8
  225. data/lib/ext/translator.rb +10 -2
  226. data/lib/generators/camaleon_cms/gem_plugin_template/app/controllers/plugins/my_plugin/admin_controller.rb +5 -1
  227. data/lib/generators/camaleon_cms/install_generator.rb +7 -4
  228. data/lib/generators/camaleon_cms/install_template/plugin_routes.rb +5 -12
  229. data/lib/generators/camaleon_cms/theme_generator.rb +5 -10
  230. data/lib/generators/camaleon_cms/theme_template/views/layouts/index.html.erb +3 -1
  231. data/lib/plugin_routes.rb +412 -368
  232. data/lib/tasks/camaleon_cms/camaleon_tasks.rake +2 -1
  233. data/lib/tasks/cross_site_field_groups.rake +74 -0
  234. data/lib/tasks/custom_fields_roles.rake +58 -0
  235. data/lib/tasks/orphaned_comments.rake +42 -0
  236. data/lib/tasks/private_upload_acls.rake +31 -0
  237. data/lib/tasks/site_custom_field_groups.rake +36 -0
  238. data/lib/tasks/unsafe_stored_content.rake +42 -0
  239. data/lib/tasks/unsafe_stored_uploads.rake +70 -0
  240. data/lib/tasks/user_field_groups.rake +27 -0
  241. metadata +185 -7
@@ -126,6 +126,7 @@ pt-BR:
126
126
  message:
127
127
  success: 'Bem-vindo!!!'
128
128
  fail: 'Usuário ou senha incorreta'
129
+ too_many_attempts: 'Muitas tentativas malsucedidas. Tente novamente mais tarde.'
129
130
  forgot_url_incorrect: 'URL incorreta'
130
131
  forgot_expired: 'Expirou o prazo para redefinir a senha.'
131
132
  hello: 'Olá'
@@ -555,7 +556,10 @@ pt-BR:
555
556
  requires_different_username: 'Requer usuário diferente'
556
557
  requires_different_email: 'Requer email diferente'
557
558
  new_photo: 'Nova foto'
558
- others_permissions: 'Outras permissões'
559
+ other_permissions: 'Outras permissões'
560
+ select_eval_modal:
561
+ title: 'Permissao perigosa'
562
+ description: 'A permissao select_eval permite executar codigo dinamico a partir das opcoes de campos personalizados. Ao habilitar, pode causar execucao arbitraria de codigo, exposicao de dados e comprometimento total do site caso conteudo nao confiavel seja avaliado.'
559
563
  profile: 'Perfil'
560
564
  slogan: 'Slogan'
561
565
  type_contents: 'TIpo de conteúdos'
@@ -581,7 +585,7 @@ pt-BR:
581
585
  media: 'Permissão para o meio'
582
586
  publish: 'Você precisa permitir Criar ou Editar, Editar Outro, Editar, Publicar , para habilitar esta permissão'
583
587
  themes: 'Permissão para a seção Temas'
584
- widgets: 'Permissão para a seção Widgets'
588
+ widgets: 'Permitir a edição de widgets, incluindo salvar código não higienizado.'
585
589
  menus: 'Permissão para a seção Menu'
586
590
  plugins: 'Permissão para a seção Plugins'
587
591
  users: 'Permissão to criar, editar, excluir Usuários'
@@ -623,6 +627,7 @@ pt-BR:
623
627
  error_created: 'Nenhum Widget criado.'
624
628
  error_updated: 'Nenhum Widget atualizado.'
625
629
  not_registered_widgets: 'Não há widgets registrados'
630
+ warning: "Aviso: Os widgets podem armazenar HTML e Javascript não higienizados. Insira apenas o código que você entende e confia."
626
631
  intro:
627
632
  profile: "Neste lugar você pode ver suas opções de perfis."
628
633
  content: "Este bloco contém todo o grupo de conteúdo de seu site. Cada conteúdo pode ter uma estrutura diferente configurada em Configurações => Grupos de conteúdo"
@@ -124,6 +124,7 @@ pt:
124
124
  message:
125
125
  success: 'Benvindo!!!'
126
126
  fail: 'Utilizador ou password incorreta'
127
+ too_many_attempts: 'Demasiadas tentativas falhadas. Tente novamente mais tarde.'
127
128
  forgot_url_incorrect: 'Endereço incorreto'
128
129
  forgot_expired: 'Expirou o prazo para redefinir a password.'
129
130
  hello: 'Olá'
@@ -553,7 +554,10 @@ pt:
553
554
  requires_different_username: 'Requer utilizador diferente'
554
555
  requires_different_email: 'Requer email diferente'
555
556
  new_photo: 'Nova foto'
556
- others_permissions: 'Outras permissões'
557
+ other_permissions: 'Outras permissões'
558
+ select_eval_modal:
559
+ title: 'Permissao perigosa'
560
+ description: 'A permissao select_eval permite executar codigo dinamico a partir das opcoes dos campos personalizados. Ao ativar, pode causar execucao arbitraria de codigo, exposicao de dados e comprometimento total do site se conteudo nao confiavel for avaliado.'
557
561
  profile: 'Perfil'
558
562
  slogan: 'Slogan'
559
563
  type_contents: 'TIpo de conteúdos'
@@ -579,7 +583,7 @@ pt:
579
583
  media: 'Permissão para o meio'
580
584
  publish: 'Permissão para Criar ou Editar, Editar Outro, Editar, Publicar, para habilitar esta permissão'
581
585
  themes: 'Permissão para a secção Temas'
582
- widgets: 'Permissão para a secção Widgets'
586
+ widgets: 'Permite a edição de widgets, incluindo salvar código não higienizado.'
583
587
  menus: 'Permissão para a secção Menu'
584
588
  plugins: 'Permissão para a secção Plugins'
585
589
  users: 'Permissão para criar, editar, apagar utilizadores'
@@ -621,6 +625,7 @@ pt:
621
625
  error_created: 'Nenhum Widget criado.'
622
626
  error_updated: 'Nenhum Widget atualizado.'
623
627
  not_registered_widgets: 'Não há widgets registados'
628
+ warning: "Aviso: Os widgets podem armazenar HTML e Javascript não higienizados. Introduza apenas o código que compreende e em que confia."
624
629
  intro:
625
630
  profile: "Neste lugar pode ver suas opções de perfis."
626
631
  content: "Este bloco contém todo o grupo de conteúdo de seu site. Cada conteúdo pode ter uma estrutura diferente configurada em Configurações => Grupos de conteúdo"
@@ -137,6 +137,7 @@ ru:
137
137
  message:
138
138
  success: 'Добро пожаловать!!!'
139
139
  fail: 'Неверное имя пользователя или пароль'
140
+ too_many_attempts: 'Слишком много неудачных попыток. Повторите попытку позже.'
140
141
  forgot_url_incorrect: 'Неверный URL'
141
142
  forgot_expired: 'Сброс истек.'
142
143
  hello: 'Привет'
@@ -574,7 +575,10 @@ ru:
574
575
  requires_different_email: 'Требуется другая эл. почта'
575
576
  created_pending_validate_email: "Пользователь был создан, пожалуйста, подтвердите вашу электронную почту"
576
577
  new_photo: 'Новая фотография'
577
- others_permissions: 'Другие разрешения'
578
+ other_permissions: 'Другие разрешения'
579
+ select_eval_modal:
580
+ title: 'Опасное право доступа'
581
+ description: 'Поле типа select_eval позволяет выполнять динамический код из параметров, введённых пользователем. Это может привести к произвольному выполнению кода, утечке данных (в том числе паролей и ключей авторизации) и полной компрометации сайта. Включайте это правл доступа только для доверенных пользователей!'
578
582
  profile: 'Профиль'
579
583
  slogan: 'Слоган'
580
584
  type_contents: 'Тип контента'
@@ -600,7 +604,7 @@ ru:
600
604
  media: 'Permission to the midsection'
601
605
  publish: 'Вам требуется разрешение на Создание, Редактирование, Редактирование других или Опубликовать чтобы включить это разрешение'
602
606
  themes: 'Разрешение тем на раздел'
603
- widgets: 'Разрешение виджетов на раздел'
607
+ widgets: 'Разрешите редактирование виджетов, в том числе сохранение неочищенного кода.'
604
608
  menus: 'Разрешение на раздел Меню'
605
609
  plugins: 'Разрешение на раздел Плагинов'
606
610
  users: 'Разрешение на создание, редактирование и удаление Пользователей'
@@ -651,6 +655,7 @@ ru:
651
655
  error_created: "Боковая панель не создана."
652
656
  error_updated: "Боковая панель не обновлена."
653
657
  error_deleted: "Боковая панель удалена."
658
+ warning: "Предупреждение: Виджеты могут хранить неочищенный HTML и Javascript. Вводите только тот код, который вы понимаете и которому доверяете."
654
659
  intro:
655
660
  profile: "Здесь вы можете увидеть настройки вашего профиля."
656
661
  content: "Этот блок содержит все группы контента вашего сайта. Каждый контент может иметь различную структуру, их можно настроить в Настройки => Группа контентов"
@@ -134,6 +134,7 @@ uk:
134
134
  message:
135
135
  success: 'Ласкаво просимо !!!'
136
136
  fail: "Неправильне ім'я користувача або пароль"
137
+ too_many_attempts: 'Забагато невдалих спроб. Спробуйте пізніше.'
137
138
  forgot_url_incorrect: 'Неправильний URL'
138
139
  forgot_expired: 'Скидання закінчилось.'
139
140
  hello: 'Привіт'
@@ -574,7 +575,10 @@ uk:
574
575
  requires_different_email: 'Потрібні інші ел. пошта '
575
576
  created_pending_validate_email: "Користувач був створений, будь ласка, підтвердіть вашу електронну пошту"
576
577
  new_photo: 'Нова фотографія'
577
- others_permissions: 'Інші роздільності'
578
+ other_permissions: 'Інші роздільності'
579
+ select_eval_modal:
580
+ title: 'Небезпечний дозвіл'
581
+ description: 'Дозвіл select_eval дозволяє виконувати динамічний код з параметрів користувацьких полів. Увімкнення може призвести до довільного виконання коду, витоку даних і повної компрометації сайту, якщо обробляється недовірений вміст.'
578
582
  profile: 'Профіль'
579
583
  slogan: 'Слоган'
580
584
  type_contents: 'Тип Контенту'
@@ -600,7 +604,7 @@ uk:
600
604
  media: 'Permission to the midsection'
601
605
  publish: 'Вам потрібен дозвіл на Створення, редагування, Редагування інших або Опублікувати щоб включити цей дозвіл'
602
606
  themes: 'Дозвіл тим на розділ'
603
- widgets: 'Дозвіл віджетів на розділ'
607
+ widgets: 'Уможливлено редагування віджетів, зокрема збереження непродезінфікованого коду.'
604
608
  menus: 'Дозвіл на розділ Меню'
605
609
  plugins: 'Дозвіл на розділ Плагінів'
606
610
  users: 'Дозвіл на створення, редагування і видалення користувачів'
@@ -651,6 +655,7 @@ uk:
651
655
  error_created: "Бічна панель не створена."
652
656
  error_updated: "Бічна панель не оновлена."
653
657
  error_deleted: "Бічна панель видалена."
658
+ warning: "Попередження: Віджети можуть зберігати непродезінфіковані HTML та Javascript. Вводьте лише той код, який ви розумієте та якому довіряєте."
654
659
  intro:
655
660
  profile: "Тут ви можете побачити налаштування вашого профілю."
656
661
  content: "Цей блок містить всі групи контенту вашого сайту. Кожен контент може мати різну структуру, яких можна налаштувати в Налаштування => Група контентів"
@@ -143,6 +143,7 @@ zh-CN:
143
143
  message:
144
144
  success: '欢迎登录!!!'
145
145
  fail: '用户名或密码不正确'
146
+ too_many_attempts: '尝试次数过多,请稍后再试。'
146
147
  forgot_url_incorrect: '链接错误'
147
148
  forgot_expired: '重置链接已过期。'
148
149
  hello: '您好'
@@ -607,7 +608,10 @@ zh-CN:
607
608
  role_can_not_be_deleted: '无法删除此角色'
608
609
  user_can_not_delete_own_account: '用户不能删除自己的帐户'
609
610
  new_photo: '新建照片'
610
- others_permissions: '其他权限'
611
+ other_permissions: '其他权限'
612
+ select_eval_modal:
613
+ title: '危险权限'
614
+ description: 'select_eval 权限允许从自定义字段选项中执行动态代码。启用后,如果评估不受信任的内容,可能导致任意代码执行、数据泄露以及站点被完全攻陷。'
611
615
  profile: '资料'
612
616
  slogan: '标语'
613
617
  type_contents: '内容的类型'
@@ -633,7 +637,7 @@ zh-CN:
633
637
  media: '媒体资源部分'
634
638
  publish: '开启该权限,允许创建,编辑, 或公开'
635
639
  themes: '主题部分'
636
- widgets: '插件部分'
640
+ widgets: '允许编辑小部件,包括保存未经清理的代码。'
637
641
  menus: '菜单部分'
638
642
  plugins: '插件部分'
639
643
  users: '创建,编辑,删除用户'
@@ -684,6 +688,7 @@ zh-CN:
684
688
  error_created: '侧边栏创建失败'
685
689
  error_updated: '侧边栏更新失败'
686
690
  error_deleted: "侧边栏已删除."
691
+ warning: "警告:小组件可以存储未经清理的 HTML 和 Javascript。仅输入您理解和信任的代码。"
687
692
  intro:
688
693
  profile: "在这个地方,您可以看到您的个人资料选项."
689
694
  content: "此块包含您网站的所有内容组。 每个内容可以具有在 设置 => 内容组"
@@ -8,17 +8,22 @@ Rails.application.routes.draw do
8
8
  get 'search'
9
9
  get 'login' => 'sessions#login'
10
10
  post 'login' => 'sessions#login_post'
11
- get 'logout' => 'sessions#logout'
11
+ # Security (audit M6): logout changes state, so it acts only over POST. The GET renders a
12
+ # confirmation page instead of 404ing -- frontend themes across the ecosystem link this
13
+ # path -- and the impersonation flow keeps its redirect (see SessionsController#logout).
14
+ match 'logout' => 'sessions#logout', via: %i[get post]
15
+ match 'back_to_parent' => 'sessions#back_to_parent', via: %i[get post]
12
16
  match 'forgot' => 'sessions#forgot', via: %i[get post patch]
13
- match 'confirm_email' => 'sessions#confirm_email', via: %i[get post path]
17
+ match 'confirm_email' => 'sessions#confirm_email', via: %i[get post patch]
14
18
  match 'register' => 'sessions#register', via: %i[get post patch]
15
19
  match 'api/:method', action: :api, via: %i[get post], as: :api
16
20
 
17
21
  resources :post_type, as: :post_type do
18
22
  resources :posts, controller: 'posts' do
19
23
  # resources :comments
20
- get :trash
21
- get :restore
24
+ # Security (audit M6): state-changing member actions must not ride GET links.
25
+ patch :trash
26
+ patch :restore
22
27
  collection do
23
28
  match 'ajax', via: %i[get post patch]
24
29
  end
@@ -41,7 +46,8 @@ Rails.application.routes.draw do
41
46
  match 'profile/edit' => 'users#profile_edit', via: %i[get post patch]
42
47
  resources :users, controller: 'users' do
43
48
  patch 'updated_ajax'
44
- get :impersonate, on: :member
49
+ # Security (audit M6): forces a session switch -- the highest-value CSRF target here.
50
+ post :impersonate, on: :member
45
51
  end
46
52
 
47
53
  resources :user_roles, controller: 'user_roles' do
@@ -53,11 +59,11 @@ Rails.application.routes.draw do
53
59
  collection do
54
60
  post 'get_items/:key', action: :get_items, as: :get_items
55
61
  post 'reorder'
56
- get 'list'
62
+ match 'list', via: %i[get post]
57
63
  end
58
64
  end
59
65
  get 'site'
60
- get 'test_email'
66
+ post 'test_email' # Security (audit M6): sends mail; must not be a CSRF-able GET
61
67
  get 'theme'
62
68
  post 'save_theme'
63
69
  get 'languages'
@@ -73,25 +79,33 @@ Rails.application.routes.draw do
73
79
  resources :comments, controller: 'comments' do
74
80
  get 'answer'
75
81
  post 'save_answer'
76
- get 'toggle_status'
82
+ patch 'toggle_status' # Security (audit M6): flips moderation state
77
83
  end
78
84
  end
79
85
 
80
86
  namespace :appearances do
81
- match 'widgets', via: %i[get delete]
87
+ # Security (audit M6): this legacy widgets surface predates the widgets/{main,sidebar,
88
+ # assign} controllers (its target controller is long gone) but still declared delete
89
+ # endpoints reachable over CSRF-exempt GET. The non-GET verbs keep the paths and helpers
90
+ # routable for any external binding.
91
+ delete 'widgets'
82
92
  match 'widgets_save', via: %i[post patch]
83
- match 'widget_delete', via: %i[get patch]
93
+ patch 'widget_delete'
84
94
  get 'render_form'
85
95
 
86
96
  resources :themes, only: [:index] do
87
97
  collection do
88
98
  get 'preview'
89
99
  # match "settings", via: [:get, :post, :patch]
90
- match 'load_data', via: %i[get post patch]
100
+ # Security (audit M6): load_data clears and re-imports post types, nav menus and sliders
101
+ # -- a state change that must not ride a CSRF-exempt GET link.
102
+ match 'load_data', via: %i[post patch]
91
103
  end
92
104
  end
93
105
  resources :nav_menus, except: :show do
94
- get 'item_delete/:id' => :delete_menu_item, as: :delete_menu_item
106
+ # Security (audit M6): destroys a menu item -- must not ride a CSRF-exempt GET. The
107
+ # admin JS sends a token-bearing DELETE (nav_menu.js).
108
+ delete 'item_delete/:id' => :delete_menu_item, as: :delete_menu_item
95
109
  get 'custom_settings/:id' => :custom_settings, as: :custom_settings
96
110
  post 'save_custom_settings/:id' => :save_custom_settings, as: :save_custom_settings
97
111
  get 'edit_menu_item/:id' => :edit_menu_item, as: :edit_menu_item
@@ -114,8 +128,10 @@ Rails.application.routes.draw do
114
128
  end
115
129
 
116
130
  resources :plugins, only: %i[index destroy] do
117
- get 'toggle', on: :collection
118
- get 'upgrade'
131
+ # Security (audit M6): activating/deactivating and upgrading a plugin run install and
132
+ # upgrade hooks -- state changes, not reads.
133
+ patch 'toggle', on: :collection
134
+ post 'upgrade'
119
135
  end
120
136
 
121
137
  # installer
@@ -125,7 +141,9 @@ Rails.application.routes.draw do
125
141
  end
126
142
 
127
143
  resources :media, only: [:index] do
128
- match 'crop', via: :all, on: :collection
144
+ # Security (audit M6): crop writes a cropped upload and can rewrite a user avatar
145
+ # (saved_avatar); via: :all admitted every verb, the CSRF-exempt GET/HEAD included.
146
+ post 'crop', on: :collection
129
147
  get 'ajax', on: :collection
130
148
  get 'download_private_file', on: :collection
131
149
  post 'upload', on: :collection
@@ -29,11 +29,12 @@ Rails.application.routes.draw do
29
29
  multilingual_segment = PluginRoutes.all_translations('routes.category', default: 'category')
30
30
  request.params[:category_id].match(/[0-9]+/) && request.params[:label_cat].in?(multilingual_segment)
31
31
  }
32
- get ':post_type_title/:label_cat/:category_id-:title/:slug' => :post, as: 'post_of_category_post_type', constraints: lambda { |request|
33
- multilingual_segment = PluginRoutes.all_translations('routes.category', default: 'category')
34
- request.params[:post_type_title].match(/^(?!(#{PluginRoutes.all_locales})$)[\w.-]+$/) &&
35
- request.params[:category_id].match(/[0-9]+/) && request.params[:label_cat].in?(multilingual_segment)
36
- }
32
+ get ':post_type_title/:label_cat/:category_id-:title/:slug' => :post, as: 'post_of_category_post_type',
33
+ constraints: lambda { |request|
34
+ multilingual_segment = PluginRoutes.all_translations('routes.category', default: 'category')
35
+ request.params[:post_type_title].match(/^(?!(#{PluginRoutes.all_locales})$)[\w.-]+$/) &&
36
+ request.params[:category_id].match(/[0-9]+/) && request.params[:label_cat].in?(multilingual_segment)
37
+ }
37
38
  get ':label/:post_tag_id-:title' => :post_tag, as: 'post_tag', constraints: lambda { |request|
38
39
  multilingual_segment = PluginRoutes.all_translations('routes.tag', default: 'tag')
39
40
  request.params[:post_tag_id].match(/[0-9]+/) && request.params[:label].in?(multilingual_segment)
@@ -44,12 +45,14 @@ Rails.application.routes.draw do
44
45
  }
45
46
  get ':label/:post_tag_slug' => :post_tag, as: 'post_tag_simple', constraints: lambda { |request|
46
47
  multilingual_segment = PluginRoutes.all_translations('routes.tag', default: 'tag')
47
- request.params[:post_tag_slug].match(%r{[a-zA-Z0-9_=\s\-/]+}) && request.params[:label].in?(multilingual_segment)
48
- }
49
- get ':label/:user_id-:user_name' => :profile, as: :profile, defaults: { label: 'profile' }, constraints: lambda { |request|
50
- multilingual_segment = PluginRoutes.all_translations('routes.profile', default: 'profile') | %w[profile]
51
- request.params[:user_id].match(/[0-9]+/) && request.params[:label].in?(multilingual_segment)
48
+ request.params[:post_tag_slug].match(%r{[a-zA-Z0-9_=\s\-/]+}) &&
49
+ request.params[:label].in?(multilingual_segment)
52
50
  }
51
+ get ':label/:user_id-:user_name' => :profile, as: :profile, defaults: { label: 'profile' },
52
+ constraints: lambda { |request|
53
+ multilingual_segment = PluginRoutes.all_translations('routes.profile', default: 'profile') | %w[profile]
54
+ request.params[:user_id].match(/[0-9]+/) && request.params[:label].in?(multilingual_segment)
55
+ }
53
56
  get ':label' => :search, as: :search, defaults: { label: 'search' }, constraints: lambda { |request|
54
57
  multilingual_segment = PluginRoutes.all_translations('routes.search', default: 'search') | %w[search]
55
58
  request.params[:label].in?(multilingual_segment)
@@ -71,11 +74,14 @@ Rails.application.routes.draw do
71
74
  # post types
72
75
  controller 'camaleon_cms/frontend' do
73
76
  PluginRoutes.get_sites.each do |s|
74
- s.post_types.pluck(:slug, :id).each do |pt_slug, pt_id|
75
- get ':post_type_slug' => :post_type, as: "post_type_#{pt_id}", post_type_id: pt_id, constraints: lambda { |request|
76
- multilingual_segment = PluginRoutes.all_translations("routes.post_types.#{pt_slug}", default: pt_slug)
77
- request.params[:post_type_slug].in?(multilingual_segment)
78
- }
77
+ # get_sites eager-loads :post_types, so map in memory instead of a per-site pluck query.
78
+ s.post_types.map { |pt| [pt.slug, pt.id] }.each do |pt_slug, pt_id|
79
+ get ':post_type_slug' => :post_type, as: "post_type_#{pt_id}", post_type_id: pt_id,
80
+ constraints: lambda { |request|
81
+ multilingual_segment =
82
+ PluginRoutes.all_translations("routes.post_types.#{pt_slug}", default: pt_slug)
83
+ request.params[:post_type_slug].in?(multilingual_segment)
84
+ }
79
85
  end
80
86
  end
81
87
  end
@@ -1,12 +1,12 @@
1
1
  class PostTableIntoUtf8 < CamaManager.migration_class
2
2
  def change
3
3
  if table_exists? CamaleonCms::User.table_name
4
- add_column(CamaleonCms::User.table_name, :email, :string) unless column_exists?(CamaleonCms::User.table_name, :email)
5
- add_column(CamaleonCms::User.table_name, :username, :string) unless column_exists?(CamaleonCms::User.table_name, :username)
6
- add_column(CamaleonCms::User.table_name, :role, :string, default: 'client', index: true) unless column_exists?(CamaleonCms::User.table_name, :role)
7
- add_column(CamaleonCms::User.table_name, :parent_id, :integer) unless column_exists?(CamaleonCms::User.table_name, :parent_id)
8
- add_column(CamaleonCms::User.table_name, :site_id, :integer, index: true, default: -1) unless column_exists?(CamaleonCms::User.table_name, :site_id)
9
- add_column(CamaleonCms::User.table_name, :auth_token, :string) unless column_exists?(CamaleonCms::User.table_name, :auth_token)
4
+ add_column(CamaleonCms::User.table_name, :email, :string, if_not_exists: true)
5
+ add_column(CamaleonCms::User.table_name, :username, :string, if_not_exists: true)
6
+ add_column(CamaleonCms::User.table_name, :role, :string, default: 'client', index: true, if_not_exists: true)
7
+ add_column(CamaleonCms::User.table_name, :parent_id, :integer, if_not_exists: true)
8
+ add_column(CamaleonCms::User.table_name, :site_id, :integer, index: true, default: -1, if_not_exists: true)
9
+ add_column(CamaleonCms::User.table_name, :auth_token, :string, if_not_exists: true)
10
10
  else
11
11
  create_table CamaleonCms::User.table_name do |t|
12
12
  t.string "username", index: true
@@ -26,7 +26,7 @@ class PostTableIntoUtf8 < CamaManager.migration_class
26
26
  end
27
27
  end
28
28
 
29
- create_table "#{PluginRoutes.static_system_info["db_prefix"]}term_taxonomy" do |t|
29
+ create_table "#{PluginRoutes.static_system_info["db_prefix"]}term_taxonomy", if_not_exists: true do |t|
30
30
  t.string "taxonomy", index: true
31
31
  t.text "description", limit: 1073741823
32
32
  t.integer "parent_id", index: true
@@ -41,7 +41,7 @@ class PostTableIntoUtf8 < CamaManager.migration_class
41
41
  t.belongs_to :user, index: true#, foreign_key: true
42
42
  end
43
43
 
44
- create_table "#{PluginRoutes.static_system_info["db_prefix"]}posts" do |t|
44
+ create_table "#{PluginRoutes.static_system_info["db_prefix"]}posts", if_not_exists: true do |t|
45
45
  t.string "title"
46
46
  t.string "slug", index: true
47
47
  t.text "content", limit: 1073741823
@@ -58,13 +58,13 @@ class PostTableIntoUtf8 < CamaManager.migration_class
58
58
  t.belongs_to :user, index: true#, foreign_key: true
59
59
  end
60
60
 
61
- create_table "#{PluginRoutes.static_system_info["db_prefix"]}term_relationships" do |t|
61
+ create_table "#{PluginRoutes.static_system_info["db_prefix"]}term_relationships", if_not_exists: true do |t|
62
62
  t.integer "objectid", index: true
63
63
  t.integer "term_order", index: true
64
64
  t.belongs_to :term_taxonomy, index: true
65
65
  end
66
66
 
67
- create_table "#{PluginRoutes.static_system_info["db_prefix"]}user_relationships" do |t|
67
+ create_table "#{PluginRoutes.static_system_info["db_prefix"]}user_relationships", if_not_exists: true do |t|
68
68
  t.integer "term_order"
69
69
  t.integer "active", default: 1
70
70
 
@@ -72,7 +72,7 @@ class PostTableIntoUtf8 < CamaManager.migration_class
72
72
  t.belongs_to :user, index: true
73
73
  end
74
74
 
75
- create_table "#{PluginRoutes.static_system_info["db_prefix"]}comments" do |t|
75
+ create_table "#{PluginRoutes.static_system_info["db_prefix"]}comments", if_not_exists: true do |t|
76
76
  t.string "author"
77
77
  t.string "author_email"
78
78
  t.string "author_url"
@@ -87,7 +87,7 @@ class PostTableIntoUtf8 < CamaManager.migration_class
87
87
  t.timestamps null: false
88
88
  end
89
89
 
90
- create_table "#{PluginRoutes.static_system_info["db_prefix"]}custom_fields" do |t|
90
+ create_table "#{PluginRoutes.static_system_info["db_prefix"]}custom_fields", if_not_exists: true do |t|
91
91
  t.string "object_class", index: true
92
92
  t.string "name"
93
93
  t.string "slug", index: true
@@ -100,7 +100,7 @@ class PostTableIntoUtf8 < CamaManager.migration_class
100
100
  t.string "status"
101
101
  end
102
102
 
103
- create_table "#{PluginRoutes.static_system_info["db_prefix"]}custom_fields_relationships" do |t|
103
+ create_table "#{PluginRoutes.static_system_info["db_prefix"]}custom_fields_relationships", if_not_exists: true do |t|
104
104
  t.integer "objectid", index: true
105
105
  t.integer "custom_field_id", index: true
106
106
  t.integer "term_order"
@@ -109,7 +109,7 @@ class PostTableIntoUtf8 < CamaManager.migration_class
109
109
  t.string "custom_field_slug", index: true
110
110
  end
111
111
 
112
- create_table "#{PluginRoutes.static_system_info["db_prefix"]}metas" do |t|
112
+ create_table "#{PluginRoutes.static_system_info["db_prefix"]}metas", if_not_exists: true do |t|
113
113
  t.string "key", index: true
114
114
  t.text "value", limit: 1073741823
115
115
  t.integer "objectid", index: true
@@ -1,9 +1,9 @@
1
1
  # change post structure to optimize query speed
2
2
  class RenameColumnPosts < CamaManager.migration_class
3
3
  def change
4
- remove_column "#{PluginRoutes.static_system_info["db_prefix"]}posts", :comment_count
5
- add_column "#{PluginRoutes.static_system_info["db_prefix"]}posts", :post_order, :integer, default: 0
6
- add_column "#{PluginRoutes.static_system_info["db_prefix"]}posts", :taxonomy_id, :integer, default: nil, index: true
4
+ remove_column "#{PluginRoutes.static_system_info["db_prefix"]}posts", :comment_count, if_exists: true
5
+ add_column "#{PluginRoutes.static_system_info["db_prefix"]}posts", :post_order, :integer, default: 0, if_not_exists: true
6
+ add_column "#{PluginRoutes.static_system_info["db_prefix"]}posts", :taxonomy_id, :integer, default: nil, index: true, if_not_exists: true
7
7
  CamaleonCms::Post.all.each do |post|
8
8
  begin
9
9
  post_id = post.get_post_type_depre.id
@@ -1,7 +1,7 @@
1
1
  class AddConfirmTokenToUsers < CamaManager.migration_class
2
2
  def change
3
- add_column CamaleonCms::User.table_name, :confirm_email_token, :string, default: nil
4
- add_column CamaleonCms::User.table_name, :confirm_email_sent_at, :datetime, default: nil
5
- add_column CamaleonCms::User.table_name, :is_valid_email, :boolean, default: true
3
+ add_column CamaleonCms::User.table_name, :confirm_email_token, :string, default: nil, if_not_exists: true
4
+ add_column CamaleonCms::User.table_name, :confirm_email_sent_at, :datetime, default: nil, if_not_exists: true
5
+ add_column CamaleonCms::User.table_name, :is_valid_email, :boolean, default: true, if_not_exists: true
6
6
  end
7
7
  end
@@ -1,5 +1,5 @@
1
1
  class AddFeatureToPosts < CamaManager.migration_class
2
2
  def change
3
- add_column "#{PluginRoutes.static_system_info["db_prefix"]}posts", :is_feature, :boolean, default: false
3
+ add_column "#{PluginRoutes.static_system_info["db_prefix"]}posts", :is_feature, :boolean, default: false, if_not_exists: true
4
4
  end
5
5
  end
@@ -1,7 +1,7 @@
1
1
  class MoveFirstNameOfUsers < CamaManager.migration_class
2
2
  def change
3
- add_column CamaleonCms::User.table_name, :first_name, :string unless column_exists?(CamaleonCms::User.table_name, :first_name)
4
- add_column CamaleonCms::User.table_name, :last_name, :string unless column_exists?(CamaleonCms::User.table_name, :last_name)
3
+ add_column CamaleonCms::User.table_name, :first_name, :string, if_not_exists: true
4
+ add_column CamaleonCms::User.table_name, :last_name, :string, if_not_exists: true
5
5
  CamaleonCms::User.all.each do |u|
6
6
  u.update_columns(first_name: u.get_meta('first_name'), last_name: u.get_meta('last_name')) if u.get_meta('first_name').present?
7
7
  end
@@ -1,5 +1,5 @@
1
1
  class AddGroupToCustomFieldValues < CamaManager.migration_class
2
2
  def change
3
- add_column "#{PluginRoutes.static_system_info["db_prefix"]}custom_fields_relationships", :group_number, :integer, default: 0
3
+ add_column "#{PluginRoutes.static_system_info["db_prefix"]}custom_fields_relationships", :group_number, :integer, default: 0, if_not_exists: true
4
4
  end
5
5
  end
@@ -1,5 +1,5 @@
1
1
  class DropUserRelationshipTable < CamaManager.migration_class
2
2
  def change
3
- drop_table "#{PluginRoutes.static_system_info["db_prefix"]}user_relationships"
3
+ drop_table "#{PluginRoutes.static_system_info["db_prefix"]}user_relationships", if_exists: true
4
4
  end
5
5
  end
@@ -1,6 +1,6 @@
1
1
  class CreateMedia < CamaManager.migration_class
2
2
  def change
3
- create_table "#{PluginRoutes.static_system_info["db_prefix"]}media" do |t|
3
+ create_table "#{PluginRoutes.static_system_info["db_prefix"]}media", if_not_exists: true do |t|
4
4
  t.references :site, index: true
5
5
  t.string :name, index: true
6
6
  t.boolean :is_folder, index: true, default: false
@@ -2,7 +2,7 @@ class AdjustFieldLength < CamaManager.migration_class
2
2
  def change
3
3
  post_table = CamaleonCms::Post.table_name
4
4
  change_column post_table, :title, :text
5
- remove_index(post_table, :slug) if index_exists?(post_table, :slug)
5
+ remove_index(post_table, :slug, if_exists: true)
6
6
  change_column post_table, :slug, :text
7
7
  add_index post_table, :slug, length: 255
8
8
  change_column "#{PluginRoutes.static_system_info["db_prefix"]}term_taxonomy", :name, :text
@@ -0,0 +1,86 @@
1
+ module CamaleonCms
2
+ # Helper used by config/initializers/assets.rb to declare plugin/theme assets as
3
+ # precompiled.
4
+ #
5
+ # Plugin and theme assets can live in three different layouts:
6
+ # * host app: <app>/app/apps/{plugins,themes}/<key>/assets/...
7
+ # * the camaleon_cms gem: <gem>/app/apps/{plugins,themes}/<key>/assets/...
8
+ # * separate "gem_mode" gems: <gem>/app/assets/<type>/{plugins,themes}/<key>/...
9
+ # (e.g. cama_contact_form ships app/assets/javascripts/plugins/cama_contact_form/...)
10
+ #
11
+ # Rails' default Sprockets precompile rules only cover the app/assets top level, so none
12
+ # of these are declared as precompiled. On modern Sprockets (>= 4) with
13
+ # `config.assets.unknown_asset_fallback = false`, requesting such an asset (e.g. via
14
+ # `javascript_include_tag "plugins/visibility_post/assets/js/form.js"`) raises
15
+ # `Sprockets::Rails::Helper::AssetNotPrecompiledError` unless it is declared.
16
+ #
17
+ # We enumerate the concrete asset files from the *real* Sprockets load paths and declare
18
+ # their exact logical paths as strings. Strings are the canonical, Sprockets-version
19
+ # agnostic form (work on both 3.x and 4.x). Scanning `config.assets.paths` (instead of a
20
+ # couple of hardcoded directories) ensures host, gem-bundled and separately gem-packaged
21
+ # (gem_mode) plugins/themes are all covered, matching the reach of the old precompile
22
+ # `proc` this replaced. Because the declaration is computed from the on-disk files, every
23
+ # installed plugin/theme is declared regardless of whether it is currently active, so
24
+ # switching themes / enabling-disabling plugins (DB-only operations) never requires a
25
+ # recompile.
26
+ module AssetsPrecompile
27
+ PRECOMPILE_EXTENSIONS = /\.(css|js|svg|ttf|woff|woff2|eot|otf|swf|pdf|png|jpe?g|gif|ico|mp3|mp4|webm|ogg|webp)\z/i
28
+ PRECOMPILE_CONTENT_TYPES = %r{(javascript|image/|audio|video|font)}
29
+
30
+ module_function
31
+
32
+ # path: logical asset path relative to an asset load path, e.g.
33
+ # "plugins/visibility_post/assets/js/form.js", "themes/my_theme/assets/css/app.css"
34
+ # or "plugins/cama_contact_form/admin_editor.js" (gem_mode layout).
35
+ # Returns true when the asset must be precompiled.
36
+ def match?(path)
37
+ dirname = File.dirname(path)
38
+ return false unless dirname.start_with?('plugins/', 'themes/')
39
+
40
+ name = File.basename(path)
41
+ return false if name.start_with?('_') || path.include?('/views/')
42
+
43
+ PRECOMPILE_EXTENSIONS.match?(path) || content_type_for(name).scan(PRECOMPILE_CONTENT_TYPES).any?
44
+ end
45
+
46
+ def content_type_for(name)
47
+ MIME::Types.type_for(name).first.content_type
48
+ rescue StandardError
49
+ ''
50
+ end
51
+
52
+ # Enumerate the logical paths of every plugin/theme asset found under the given asset
53
+ # roots. For each root, only the `plugins/` and `themes/` subtrees are scanned, and each
54
+ # file is converted to its logical path (relative to the root) and filtered by `match?`.
55
+ def logical_paths(asset_roots = default_asset_roots)
56
+ asset_roots.flat_map do |root|
57
+ root = root.to_s
58
+ next [] if root.empty? || !Dir.exist?(root)
59
+
60
+ prefix = "#{root.chomp('/')}/"
61
+ Dir.glob(File.join(root, '{plugins,themes}', '**', '*')).filter_map do |file|
62
+ next unless File.file?(file)
63
+
64
+ logical = file.delete_prefix(prefix)
65
+ logical if match?(logical)
66
+ end
67
+ end.uniq
68
+ end
69
+
70
+ # Asset roots to scan: the configured Sprockets load paths (which cover host,
71
+ # gem-bundled and gem_mode plugin/theme assets), plus the host and camaleon_cms gem
72
+ # `app/apps` folders as a fallback in case the asset paths are not yet populated when
73
+ # this is called during initialization.
74
+ def default_asset_roots
75
+ roots = []
76
+ if defined?(Rails) && Rails.respond_to?(:application) && Rails.application
77
+ roots.concat(Array(Rails.application.config.assets.paths).map(&:to_s))
78
+ end
79
+ roots << Rails.root.join('app/apps').to_s if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
80
+ # rubocop:disable Style/GlobalVars
81
+ roots << File.join($camaleon_engine_dir, 'app', 'apps') if defined?($camaleon_engine_dir) && $camaleon_engine_dir
82
+ # rubocop:enable Style/GlobalVars
83
+ roots.uniq
84
+ end
85
+ end
86
+ end