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
@@ -60,10 +60,10 @@
60
60
  <td><%= link_to raw(f.the_status), {action: :index, s: f.status}, class: "cama_ajax_request" %> </td>
61
61
  <td><%= f.author.fullname %></td>
62
62
  <% if @post_type.manage_categories? %>
63
- <td><%= raw post_type_list_taxonomy(f.categories, "success") %></td>
63
+ <td><%= post_type_list_taxonomy(f.categories, "success", @post_type) %></td>
64
64
  <% end %>
65
65
  <% if @post_type.manage_tags? %>
66
- <td><%= raw post_type_list_taxonomy(f.post_tags, "default") %></td>
66
+ <td><%= post_type_list_taxonomy(f.post_tags, "default", @post_type) %></td>
67
67
  <% end %>
68
68
  <% extra_column = {post: f, post_type: @post_type, content: "", from_body: true}; hooks_run("list_post_extra_columns", extra_column) %>
69
69
  <%= raw extra_column[:content] %>
@@ -88,18 +88,18 @@
88
88
  <% end %>
89
89
 
90
90
  <% if f.trash? %>
91
- <%= link_to raw('<i class="fa fa-check"></i>'), {action: :restore, post_id: f.id, s: params[:s]}, class: "btn btn-success btn-xs", title: "#{t('camaleon_cms.admin.button.restore')}" if can? :update, f %>
91
+ <%= link_to raw('<i class="fa fa-check"></i>'), {action: :restore, post_id: f.id, s: params[:s]}, method: :patch, class: "btn btn-success btn-xs", title: "#{t('camaleon_cms.admin.button.restore')}" if can? :update, f %>
92
92
  <%= link_to raw('<i class="fa fa-times"></i>'), {action: :destroy, id: f.id, s: params[:s]},
93
93
  method: :delete, data: {confirm: t('camaleon_cms.admin.message.delete_item')}, class: "btn btn-danger btn-xs cama_ajax_request", title: "#{t('camaleon_cms.admin.button.delete_complete', default: 'Delete completely')}" if can? :destroy, f %>
94
94
  <% elsif f.draft? %>
95
95
  <%#= link_to raw('<i class="fa fa-check"></i>'), {action: :edit, id: f.id, recover_draft: 'true' }, class: "btn btn-success btn-xs", title: "#{t('camaleon_cms.admin.button.recover')}" if can? :update, f %>
96
96
  <%= link_to raw('<i class="fa fa-pencil"></i>'), {action: :edit, id: f.id}, class: "btn btn-default btn-xs cama_ajax_request", title: "#{t('camaleon_cms.admin.button.edit')}" if can? :update, f %>
97
97
  <%= link_to raw('<i class="fa fa-trash"></i>'), {action: :trash, post_id: f.id, s: params[:s]},
98
- data: {confirm: t('camaleon_cms.admin.message.delete')}, class: "btn btn-danger btn-xs cama_ajax_request", title: "#{t('camaleon_cms.admin.button.move_trash')}" if can? :destroy, f %>
98
+ method: :patch, data: {confirm: t('camaleon_cms.admin.message.delete')}, class: "btn btn-danger btn-xs cama_ajax_request", title: "#{t('camaleon_cms.admin.button.move_trash')}" if can? :destroy, f %>
99
99
  <% else %>
100
100
  <%= link_to raw('<i class="fa fa-pencil"></i>'), {action: :edit, id: f.id}, class: "btn btn-default btn-xs cama_ajax_request", title: "#{t('camaleon_cms.admin.button.edit')}" if can? :update, f %>
101
101
  <%= link_to raw('<i class="fa fa-trash"></i>'), {action: :trash, post_id: f.id, s: params[:s]},
102
- data: {confirm: t('camaleon_cms.admin.message.delete')}, class: "btn btn-danger btn-xs cama_ajax_request", title: "#{t('camaleon_cms.admin.button.move_trash')}" if can? :destroy, f %>
102
+ method: :patch, data: {confirm: t('camaleon_cms.admin.message.delete')}, class: "btn btn-danger btn-xs cama_ajax_request", title: "#{t('camaleon_cms.admin.button.move_trash')}" if can? :destroy, f %>
103
103
  <% end %>
104
104
  </td>
105
105
  </tr>
@@ -107,6 +107,6 @@
107
107
  </tbody>
108
108
  </table>
109
109
  <%= content_tag("div", raw(t('camaleon_cms.admin.message.data_found_list')), class: "alert alert-warning") if @posts.empty? %>
110
- <%= raw cama_do_pagination(@posts) %>
110
+ <%= cama_do_pagination(@posts) %>
111
111
  </div>
112
112
  </div>
@@ -62,7 +62,7 @@
62
62
  </tbody>
63
63
  </table>
64
64
  <%= content_tag("div", raw(t('camaleon_cms.admin.message.data_found_list')), class: "alert alert-warning") if @items.empty? %>
65
- <%= raw cama_do_pagination(@items, panel_class: "cama_ajax_request") %>
65
+ <%= cama_do_pagination(@items, panel_class: "cama_ajax_request") %>
66
66
  </div>
67
67
  </div>
68
68
  </div>
@@ -0,0 +1,39 @@
1
+ <div class="login-body">
2
+ <h3 class="login-box-msg"><%= t('camaleon_cms.admin.login.back_to_parent.title', default: 'Return to your account') %></h3>
3
+ <%= render "camaleon_cms/flash_messages" %>
4
+ <p>
5
+ <%# The parent admin's username is deliberately not shown: whoever holds an abandoned
6
+ impersonation must not learn which admin account to attack (or phish). %>
7
+ <%= t('camaleon_cms.admin.login.back_to_parent.instructions',
8
+ default: 'You are currently working as %{impersonated}. Enter the password for your own account to return to it, or log out completely.',
9
+ impersonated: @impersonated_user.username) %>
10
+ </p>
11
+ <%= form_tag cama_admin_back_to_parent_path, method: "post", role: "form", id: "back_to_parent", class: "form-horizontal cama_form_validate" do %>
12
+ <div class="form-group">
13
+ <div class="col-md-12">
14
+ <%= password_field_tag :password, nil, class: "form-control required", autofocus: true,
15
+ placeholder: t('camaleon_cms.admin.login.password', default: 'Password') %>
16
+ </div>
17
+ </div>
18
+ <div class="form-group">
19
+ <div class="col-md-12">
20
+ <%= raw cama_captcha_tags_if_under_attack("login") %>
21
+ </div>
22
+ </div>
23
+ <% end %>
24
+ <%# Security (audit M6): the full-logout button is its OWN button_to form, a sibling of the
25
+ re-auth form above -- never nested inside it. A nested <form> is dropped by the HTML parser
26
+ (the button would submit the outer form), and with per-form CSRF tokens the two tokens would
27
+ collide, breaking both submits. The "Return to my account" button stays a submit of the
28
+ re-auth form via the HTML5 `form` attribute. %>
29
+ <div class="row">
30
+ <div class="col-xs-6">
31
+ <%= button_to t('camaleon_cms.admin.login.back_to_parent.logout', default: 'Log out completely'),
32
+ cama_admin_logout_path(full: 1), method: :post,
33
+ class: 'btn btn-default btn-block btn-flat' %>
34
+ </div>
35
+ <div class="col-xs-6">
36
+ <button type="submit" form="back_to_parent" class="btn btn-primary btn-block btn-flat"><%= t('camaleon_cms.admin.login.back_to_parent.submit', default: 'Return to my account') %></button>
37
+ </div>
38
+ </div>
39
+ </div>
@@ -0,0 +1,27 @@
1
+ <div class="login-body">
2
+ <h3 class="login-box-msg"><%= t('camaleon_cms.admin.logout.confirm_title', default: 'Log out?') %></h3>
3
+ <%= render "camaleon_cms/flash_messages" %>
4
+ <p>
5
+ <%# Security (audit M6): reached over GET (an old bookmark, or a theme still linking the
6
+ path) — the session ends only through the POST below, so a forged GET cannot end it. %>
7
+ <%= t('camaleon_cms.admin.logout.confirm_instructions',
8
+ default: 'You are about to end your session. Continue?') %>
9
+ </p>
10
+ <div class="row">
11
+ <div class="col-xs-6">
12
+ <%# Cancel returns to the vetted return_to when a theme supplied one (see
13
+ #prepare_logout_confirmation), else the dashboard -- never blindly into the admin. %>
14
+ <a href="<%= @logout_cancel_url %>" class="btn btn-default btn-block btn-flat"><%= t('camaleon_cms.admin.button.cancel', default: 'Cancel') %></a>
15
+ </div>
16
+ <div class="col-xs-6">
17
+ <%# `full` (impersonation), `return_to` (ecosystem themes link logout with a return-to-site
18
+ destination) and `locale` are carried through to the POST -- precomputed as scalars in
19
+ the controller so a hash-shaped param cannot crash url generation; return_to is then
20
+ vetted by cama_logout_user's safe-redirect check like on any logout. %>
21
+ <%= button_to t('camaleon_cms.admin.logout.logout', default: 'Logout'),
22
+ cama_admin_logout_path(full: @logout_full, return_to: @logout_return_to,
23
+ locale: @logout_locale),
24
+ method: :post, class: 'btn btn-danger btn-block btn-flat' %>
25
+ </div>
26
+ </div>
27
+ </div>
@@ -49,7 +49,10 @@
49
49
  var form = modal.find('form');
50
50
  form.validate({submitHandler: function(){
51
51
  showLoading();
52
- $.get(link.attr('href'), form.serialize(), function(){ hideLoading(); }).complete(function(){ modal.modal('hide'); }).error(function(r){ $.fn.alert({title: r.responseText, type: 'error'}) });
52
+ // Security (audit M6): test_email sends mail, so the route is POST-only;
53
+ // jquery_ujs' prefilter attaches the CSRF token to non-GET ajax.
54
+ // .always/.fail (not the .complete/.error jqXHR aliases removed in jQuery 3).
55
+ $.post(link.attr('href'), form.serialize(), function(){ hideLoading(); }).always(function(){ modal.modal('hide'); }).fail(function(r){ $.fn.alert({title: r.responseText, type: 'error'}) });
53
56
  }});
54
57
  }});
55
58
  return false;
@@ -3,7 +3,7 @@
3
3
  @item_options_value = @item_options_value || {}
4
4
  @item_id = @item_value[:id] || ''
5
5
  @index = "#{@item_id}_#{Time.now.to_i}_#{rand(999...99999)}"
6
- @field_config = cama_custom_field_elements[@key.to_sym]
6
+ @field_config = @key.present? ? cama_custom_field_elements[@key.to_sym] : nil
7
7
  @panel_id = "panel-field#{@index}"
8
8
  colors = ['primary','success','warning','colorful','info','danger']
9
9
  unless @field_config.nil?
@@ -15,15 +15,26 @@
15
15
  </div>
16
16
  <% end %>
17
17
  <div class="fields_container">
18
- <% fields.decorate.each do |field| is_disabled = field.options[:disabled].present? || field.options[:readonly].present?; group_field_data[field.slug] = {default_value: field.options[:default_value], is_disabled: is_disabled, id: field.id, multiple: field.options[:multiple].to_s.to_bool && !is_disabled, kind: field.get_option('field_key')} %>
19
- <div class="item-custom-field c-field-<%= field.options[:field_key] %>" data-field-key="<%= field.slug %>">
18
+ <% fields.decorate.each do |field|
19
+ field_key = field.get_option('field_key').presence || field.options[:field_key].presence || field.options['field_key'].presence
20
+ normalized_field_key = field_key.presence || 'text_box'
21
+ is_disabled = field.get_option('disabled').present? || field.get_option('readonly').present?
22
+ required_field = field.get_option('required').to_s.to_bool
23
+ group_field_data[field.slug] = {
24
+ default_value: field.get_option('default_value'),
25
+ is_disabled: is_disabled,
26
+ id: field.id,
27
+ multiple: field.get_option('multiple').to_s.to_bool && !is_disabled,
28
+ kind: normalized_field_key
29
+ } %>
30
+ <div class="item-custom-field c-field-<%= normalized_field_key %>" data-field-key="<%= field.slug %>">
20
31
  <input name="<%= field_name %>[<%= field.slug %>][id]" type="hidden" value="<%= field.id %>"/>
21
32
  <input name="<%= field_name %>[<%= field.slug %>][group_number]" class="cama_custom_group_number" type="hidden" value="0"/>
22
33
  <label>
23
34
  <%= field.name %>
24
- <%= raw "<em class='text-danger'>*</em>" if field.options[:required].to_s.to_bool %>
35
+ <%= raw "<em class='text-danger'>*</em>" if required_field %>
25
36
  <% if current_site.get_option('custom_fields_show_shortcodes') && ["post", "posttype", "category", "postTag", "site", "user", "navmenu", "theme"].include?(obj_class) %>
26
- <small class="shortcode_field"><br><%= raw cama_shortcode_print("[data field='#{field.slug}' #{"object='#{obj_class}' #{"id='#{record.id}'" if obj_class != "Theme" }" unless record.new_record?}]") %></small>
37
+ <small class="shortcode_field"><br><%= cama_shortcode_print("[data field='#{field.slug}' #{"object='#{obj_class}' #{"id='#{record.id}'" if obj_class != "Theme" }" unless record.new_record?}]") %></small>
27
38
  <% end %>
28
39
  </label>
29
40
  <% if field.description.present? %>
@@ -31,7 +42,11 @@
31
42
  <% end %>
32
43
  <div class="editor-custom-fields content-field-<%= field.id %>" data-add_field_title="<%= t('camaleon_cms.admin.button.add_new_field', default: 'Add new field')%>">
33
44
  <%= begin
34
- render(field.get_option('render') || (cama_custom_field_elements[field.get_option('field_key').to_sym][:render] rescue nil) || "camaleon_cms/admin/settings/custom_fields/fields/#{field.options[:field_key]}", field: field, values: nil, is_disabled: is_disabled, default_use: default_use, field_name: field_name)
45
+ custom_field_render = (cama_custom_field_elements[normalized_field_key.to_sym][:render] rescue nil)
46
+ render(
47
+ field.get_option('render') || custom_field_render || "camaleon_cms/admin/settings/custom_fields/fields/#{normalized_field_key}",
48
+ field: field, values: nil, is_disabled: is_disabled, default_use: default_use, field_name: field_name
49
+ )
35
50
  rescue => e
36
51
  Rails.logger.error "Camaleon CMS - Render Custom Fields Error: Custom field template was not found => #{e.message}: #{$!.backtrace}".cama_log_style(:red)
37
52
  e.message
@@ -40,7 +55,28 @@
40
55
  </div>
41
56
  <% end %>
42
57
  </div>
43
- <script> jQuery(function(){ build_custom_field_group(<%= raw(params[:field_options].present? ? (r = []; params[:field_options].each{|k, v| val = {}; v.each{|kk, vv| val["#{kk}"] = (vv['values'].values rescue vv['values']); }; r << val }; r.to_json) : record.get_fields_grouped(fields.pluck(:slug).uniq).to_json) %>, '<%= group.id %>', <%= raw group_field_data.to_json %>, <%= group.is_repeat %>, '<%= field_name %>'); }); </script>
58
+ <%
59
+ field_options_json = if params[:field_options].present?
60
+ params[:field_options].to_unsafe_h.each_with_object([]) do |(_k, v), r|
61
+ r << v.each_with_object({}) do |(kk, vv), val|
62
+ val[kk.to_s] = (vv['values'].values rescue vv['values'])
63
+ end
64
+ end.to_json
65
+ else
66
+ record.get_fields_grouped(fields.pluck(:slug).uniq).to_json
67
+ end
68
+ %>
69
+ <script>
70
+ jQuery(function () {
71
+ build_custom_field_group(
72
+ <%= json_escape(field_options_json).html_safe %>,
73
+ '<%= group.id %>',
74
+ <%= json_escape(group_field_data.to_json).html_safe %>,
75
+ <%= group.is_repeat %>,
76
+ '<%= field_name %>'
77
+ );
78
+ });
79
+ </script>
44
80
  </div>
45
81
  </div>
46
82
  <% if group.is_repeat %>
@@ -2,7 +2,7 @@
2
2
  def _render_category_option_html(item, pos = 0)
3
3
  category = item.decorate
4
4
  value = "Category_Post,#{category.id}" %>
5
- <option value="<%= value %>" data-help="<%= "#{t('camaleon_cms.admin.settings.tooltip.add_custom_field_posts')}"+ category.the_title %>"><%= "#{('—' * pos)} #{category.the_title}" %></option>
5
+ <option value="<%= value %>" data-help="<%= safe_join([t('camaleon_cms.admin.settings.tooltip.add_custom_field_posts'), category.the_title]) %>"><%= safe_join([('—' * pos), ' ', category.the_title]) %></option>
6
6
  <% end
7
7
 
8
8
  def _render_categories(categories, _level = 0)
@@ -1,3 +1,3 @@
1
1
  <div class="group-input-fields-content">
2
- <%= select_tag "#{field_name}[#{field.slug}][values][]", instance_eval(field.options[:command].to_s.strip), class: "form-control input-value #{'required' if field.options[:required].to_s.to_bool}" %>
2
+ <%= select_tag "#{field_name}[#{field.slug}][values][]", instance_eval(field.options[:command].to_s.strip), class: "form-control input-value #{'required' if field.options[:required].to_s.to_bool}" %>
3
3
  </div>
@@ -64,13 +64,14 @@
64
64
  </optgroup>
65
65
 
66
66
  <optgroup label="<%= t('camaleon_cms.admin.settings.others') %>">
67
- <% value = "#{PluginRoutes.static_system_info['user_model'].presence || 'User'},#{current_site.id}" %>
67
+ <%# Demodulized: user groups are stored and read under the association's scope name %>
68
+ <% value = "#{PluginRoutes.get_user_class_name.demodulize},#{current_site.id}" %>
68
69
  <option value="<%= value %>" data-help="<%= t('camaleon_cms.admin.settings.tooltip.add_custom_field_users') %>"><%= t('camaleon_cms.admin.sidebar.users') %></option>
69
70
  <% value = "Site,#{current_site.id}" %>
70
71
  <option value="<%= value %>" data-help="<%= t('camaleon_cms.admin.settings.tooltip.add_custom_field_sites') %>"><%= t('camaleon_cms.admin.sidebar.site_settings') %></option>
71
- <% f_args={models: @_extra_models_for_fields || []}; hooks_run('custom_field_custom_models', f_args); f_args[:models].each do |model| %>
72
+ <% cf_extra_models_for_fields.each do |model| %>
72
73
  <% value = "#{model.name},#{current_site.id}" %>
73
- <option value="<%= value %>" data-help=""><%= model.name %></option><%= model.name %>
74
+ <option value="<%= value %>" data-help=""><%= model.name %></option>
74
75
  <% end %>
75
76
  </optgroup>
76
77
 
@@ -152,7 +153,7 @@
152
153
  <div class="panel-body">
153
154
  <div id="content-items-default" class="form-group input-group-sm">
154
155
  <% cama_custom_field_elements.each do |i,item| %>
155
- <% next if item[:key] == "select_eval" %>
156
+ <% next if item[:key] == 'select_eval' %>
156
157
  <a id="item-<%= item[:key] %>" class="btn btn-default" href="<%= get_items_cama_admin_settings_custom_fields_path(item[:key]) %>" style="margin-bottom: 4px"><%= item[:label] %></a>
157
158
  <% end %>
158
159
  </div>
@@ -44,7 +44,7 @@
44
44
  </tbody>
45
45
  </table>
46
46
  <%= content_tag("div", raw(t('camaleon_cms.admin.message.data_found_list')), class: "alert alert-warning") if @field_groups.empty? %>
47
- <%= raw cama_do_pagination(@field_groups) %>
47
+ <%= cama_do_pagination(@field_groups) %>
48
48
  </div>
49
49
  </div>
50
50
  <!-- END BASIC TABLE SAMPLE -->
@@ -42,7 +42,7 @@
42
42
  </tbody>
43
43
  </table>
44
44
  <%= content_tag("div", raw(t('camaleon_cms.admin.message.data_found_list')), class: "alert alert-warning") if @post_types.empty? %>
45
- <%= raw cama_do_pagination(@post_types) %>
45
+ <%= cama_do_pagination(@post_types) %>
46
46
  </div>
47
47
  </div>
48
48
  <!-- END BASIC TABLE SAMPLE -->
@@ -13,17 +13,17 @@
13
13
  </tr>
14
14
  </thead>
15
15
  <tbody>
16
- <% @_shortcodes.each do |code| %>
16
+ <% shortcodes_list.each do |code| %>
17
17
  <tr>
18
18
  <td>
19
19
  <code>[<%= code %>]</code>
20
20
  </td>
21
- <td class="col_descr"><%= @_shortcodes_descr[code] %></td>
21
+ <td class="col_descr"><%= shortcodes_descriptions[code] %></td>
22
22
  </tr>
23
23
  <% end %>
24
24
  </tbody>
25
25
  </table>
26
- <%= content_tag("div", t('camaleon_cms.admin.message.data_found_list'), class: "alert alert-warning") if @_shortcodes.empty? %>
26
+ <%= content_tag("div", t('camaleon_cms.admin.message.data_found_list'), class: "alert alert-warning") if shortcodes_list.empty? %>
27
27
  </div>
28
28
  </div>
29
29
  </div>
@@ -34,4 +34,4 @@
34
34
  $(this).html($(this).html().replace(/\n/g, "<br>"));
35
35
  });
36
36
  })
37
- </script>
37
+ </script>
@@ -3,7 +3,7 @@
3
3
  <div class="page-content-wrap">
4
4
  <div class="panel panel-default">
5
5
  <div class="panel-heading">
6
- <h4><span class="fa fa-cog"></span> <%= "#{t('camaleon_cms.admin.settings.configuration_site')}: #{@site.the_title} " %> </h4>
6
+ <h4><span class="fa fa-cog"></span> <%= safe_join([t('camaleon_cms.admin.settings.configuration_site'), ': ', @site.the_title, ' ']) %> </h4>
7
7
  </div>
8
8
  <%= form_for @site, as: "site", url: {action: :site_saved}, html: {id: "site_settings_form", class: "panel-body cama_ajax_request validate"} do |f| %>
9
9
  <div class="tabs">
@@ -5,7 +5,7 @@
5
5
  <div class="col-md-8 col-md-offset-2">
6
6
  <div class="panel panel-default">
7
7
  <div class="panel-heading">
8
- <h4><span class="fa fa-cog"></span> <%= @site.new_record? ? t('camaleon_cms.admin.page_title.new_site') : "#{t('camaleon_cms.admin.page_title.edit_site')}: #{@site.the_title} "%> </h4>
8
+ <h4><span class="fa fa-cog"></span> <%= @site.new_record? ? t('camaleon_cms.admin.page_title.new_site') : safe_join([t('camaleon_cms.admin.page_title.edit_site'), ': ', @site.the_title, ' ']) %> </h4>
9
9
  </div>
10
10
  <div class="panel-body">
11
11
  <%= render partial: 'layouts/camaleon_cms/admin/form_error', locals: {data: @site} %>
@@ -50,7 +50,7 @@
50
50
  </tbody>
51
51
  </table>
52
52
  <%= content_tag("div", raw(t('camaleon_cms.admin.message.data_found_list')), class: "alert alert-warning") if @sites.empty? %>
53
- <%= raw cama_do_pagination(@sites) %>
53
+ <%= cama_do_pagination(@sites) %>
54
54
  </div>
55
55
  </div>
56
56
  <!-- END BASIC TABLE SAMPLE -->
@@ -17,6 +17,26 @@
17
17
  <%= render partial: 'layouts/camaleon_cms/admin/form_error', locals: {data: @user_role} %>
18
18
  <% rol_values_post_type = @user_role.new_record? ? {} : @user_role.get_meta("_post_type_#{current_site.id.to_s}") %>
19
19
  <% rol_values_manager = @user_role.new_record? ? {} : @user_role.get_meta("_manager_#{current_site.id.to_s}") %>
20
+ <%# A role whose users are administrators holds every permission through `Ability`'s %>
21
+ <%# `can :manage, :all`, which returns before either meta above is read. Their stored %>
22
+ <%# metas are only what `set_default_user_roles` seeded when the site was created, so %>
23
+ <%# every key added to ROLES afterwards rendered unchecked on existing sites and read %>
24
+ <%# as "denied to administrators", which it never was. Deriving the state from the %>
25
+ <%# role keeps it true for keys added later, with no backfill task to run and none to %>
26
+ <%# write again next time. %>
27
+ <%# %>
28
+ <%# Such a role's boxes are also locked, and `UserRolesController` declines to write %>
29
+ <%# its metas — the two conditions are the same predicate, so a locked box, which the %>
30
+ <%# browser never submits, cannot silently clear what is stored. %>
31
+ <% grants_all = @user_role.grants_all_permissions? %>
32
+ <% permissions_editable = @user_role.permissions_editable? %>
33
+ <%# Without this the locked, all-checked boxes look like a bug rather than a statement %>
34
+ <%# about what an administrator is. %>
35
+ <% if grants_all %>
36
+ <div class="alert alert-info">
37
+ <span class="fa fa-info-circle"></span>&nbsp;<%= t('camaleon_cms.admin.users.admin_holds_all_permissions', default: 'Users with this role are administrators, so they hold every permission below regardless of what is selected. These settings cannot be changed.') %>
38
+ </div>
39
+ <% end %>
20
40
  <div id="contents-checkbox" class="form-horizontal">
21
41
  <div class="">
22
42
  <table class="table table-responsive table-hover table-striped">
@@ -49,7 +69,7 @@
49
69
  values = [] if values == 0
50
70
  %>
51
71
  <% if visible %>
52
- <input type="checkbox" name="rol_values[post_type][<%= value[:key] %>][]" value="<%= pt.id %>" <%= "checked" if values.include?(pt.id) %> <%= "disabled" unless @user_role.editable? %> onclick="<%= "cama_user_roles_do_all_publish(this, '#{value[:color]}', '#{value[:key]}')" %>" >
72
+ <input type="checkbox" name="rol_values[post_type][<%= value[:key] %>][]" value="<%= pt.id %>" <%= "checked" if grants_all || values.include?(pt.id) %> <%= "disabled" unless permissions_editable %> onclick="<%= "cama_user_roles_do_all_publish(this, '#{value[:color]}', '#{value[:key]}')" %>" >
53
73
  <% end %>
54
74
  </td>
55
75
  <% end %>
@@ -61,12 +81,13 @@
61
81
  </div>
62
82
 
63
83
  <div class="">
64
- <h5><%= t('camaleon_cms.admin.users.others_permissions')%></h5>
84
+ <h5><%= t('camaleon_cms.admin.users.other_permissions')%></h5>
65
85
  <div class="row">
66
86
  <% values = rol_values_manager || {} %>
87
+ <%# Same rule as the grid above: see `grants_all` where it is assigned. %>
67
88
  <% cama_get_roles_values[:manager].each do |value| %>
68
89
  <div class="col-md-4">
69
- <label><input type="checkbox" name="rol_values[manager][<%= value[:key] %>]" value="1" <%= "checked" if values[value[:key].to_sym].present? %> <%= "disabled" unless @user_role.editable? %> >&nbsp; <%= raw value[:label] %></label>
90
+ <label><input type="checkbox" name="rol_values[manager][<%= value[:key] %>]" value="1" <%= "checked" if grants_all || values[value[:key].to_sym].present? %> <%= "disabled" unless permissions_editable %> <%= 'data-danger-select-eval=1' if value[:key].to_s == 'select_eval' %> >&nbsp; <%= raw value[:label] %></label>
70
91
  &nbsp;<%= raw cama_html_tooltip(value[:description], 'right') if value[:description].present? %>
71
92
  </div>
72
93
  <% end %>
@@ -77,7 +98,7 @@
77
98
 
78
99
 
79
100
  </div>
80
- <% if @user_role.editable? %>
101
+ <% if permissions_editable %>
81
102
  <div id="checked-actions" class="panel-footer text-right">
82
103
  <a class="btn btn-default btn-sm" data-type="all" href="#"><%= t('camaleon_cms.admin.button.select_all') %></a>
83
104
  <a class="btn btn-default btn-sm" data-type="none" href="#"><%= t('camaleon_cms.admin.button.select_none') %></a>
@@ -119,20 +140,94 @@
119
140
  <!-- END PAGE CONTENT WRAPPER -->
120
141
  </div>
121
142
 
143
+ <div class="modal fade" id="select-eval-danger-modal" tabindex="-1" role="dialog" aria-labelledby="select-eval-danger-modal-title" aria-hidden="true">
144
+ <div class="modal-dialog" role="document">
145
+ <div class="modal-content">
146
+ <div class="modal-header">
147
+ <button type="button" class="close" data-dismiss="modal" aria-label="<%= t('camaleon_cms.admin.button.cancel') %>">
148
+ <span aria-hidden="true">&times;</span>
149
+ </button>
150
+ <h4 class="modal-title" id="select-eval-danger-modal-title"><%= t('camaleon_cms.admin.users.select_eval_modal.title') %></h4>
151
+ </div>
152
+ <div class="modal-body">
153
+ <p><%= t('camaleon_cms.admin.users.select_eval_modal.description') %></p>
154
+ </div>
155
+ <div class="modal-footer">
156
+ <button type="button" class="btn btn-default" data-role="select-eval-cancel" data-dismiss="modal"><%= t('camaleon_cms.admin.button.cancel') %></button>
157
+ <button type="button" class="btn btn-danger" data-role="select-eval-confirm"><%= t('camaleon_cms.admin.button.accept') %></button>
158
+ </div>
159
+ </div>
160
+ </div>
161
+ </div>
162
+
122
163
  <script>
123
164
  jQuery(function($){
165
+ var pendingDangerSelectEval = null
166
+ var pendingDangerSelectEvalConfirmed = false
167
+ var $allCheckboxes = $("#contents-checkbox input[type='checkbox']")
168
+ var $selectEvalCheckbox = $("#contents-checkbox input[data-danger-select-eval='1']")
169
+ var $selectEvalModal = $("#select-eval-danger-modal")
170
+
171
+ function openSelectEvalDangerModal(checkbox){
172
+ pendingDangerSelectEval = checkbox
173
+ pendingDangerSelectEvalConfirmed = false
174
+ checkbox.prop("checked", false)
175
+ $selectEvalModal.modal("show")
176
+ }
177
+
178
+ function shouldWarnForSelectEvalEnable(wasChecked){
179
+ return $selectEvalCheckbox.length && !$selectEvalCheckbox.prop("disabled") && !wasChecked
180
+ }
181
+
124
182
  $("#contents-checkbox input:checked").each(function(){$(this).attr("data-checked", 1)})
183
+
184
+ $("#contents-checkbox").on("change", "input[data-danger-select-eval='1']", function(){
185
+ if($(this).prop("checked")){
186
+ openSelectEvalDangerModal($(this))
187
+ }
188
+ })
189
+
190
+ $selectEvalModal.find("[data-role='select-eval-confirm']").on("click", function(){
191
+ if(pendingDangerSelectEval){
192
+ pendingDangerSelectEval.prop("checked", true)
193
+ pendingDangerSelectEvalConfirmed = true
194
+ }
195
+ $selectEvalModal.modal("hide")
196
+ })
197
+
198
+ $(document).on("keydown", function(event){
199
+ if(event.which === 27 && $selectEvalModal.hasClass("in")){
200
+ $selectEvalModal.modal("hide")
201
+ }
202
+ })
203
+
204
+ $selectEvalModal.on("hidden.bs.modal", function(){
205
+ if(pendingDangerSelectEval && !pendingDangerSelectEvalConfirmed){
206
+ pendingDangerSelectEval.prop("checked", false)
207
+ }
208
+ pendingDangerSelectEval = null
209
+ pendingDangerSelectEvalConfirmed = false
210
+ })
211
+
125
212
  $("#checked-actions a[data-type]").click(function(){
126
213
  switch ($(this).attr("data-type")){
127
214
  case 'all':
128
- $("#contents-checkbox input[type='checkbox']").prop('checked', true);
215
+ var wasSelectEvalChecked = $selectEvalCheckbox.prop('checked');
216
+ $allCheckboxes.prop('checked', true);
217
+ if(shouldWarnForSelectEvalEnable(wasSelectEvalChecked)){
218
+ openSelectEvalDangerModal($selectEvalCheckbox)
219
+ }
129
220
  break;
130
221
  case 'none':
131
- $("#contents-checkbox input[type='checkbox']").prop('checked', false);
222
+ $allCheckboxes.prop('checked', false);
132
223
  break;
133
224
  case 'restore':
134
- $("#contents-checkbox input[type='checkbox']").prop('checked', false);
225
+ var wasSelectEvalCheckedBeforeRestore = $selectEvalCheckbox.prop('checked');
226
+ $allCheckboxes.prop('checked', false);
135
227
  $("#contents-checkbox input[data-checked]").prop('checked', true);
228
+ if(shouldWarnForSelectEvalEnable(wasSelectEvalCheckedBeforeRestore) && $selectEvalCheckbox.is("[data-checked]")){
229
+ openSelectEvalDangerModal($selectEvalCheckbox)
230
+ }
136
231
  break;
137
232
  }
138
233
  return false;
@@ -45,7 +45,7 @@
45
45
  </tbody>
46
46
  </table>
47
47
  <%= content_tag("div", raw(t('camaleon_cms.admin.message.data_found_list')), class: "alert alert-warning") if @user_roles.empty? %>
48
- <%= raw cama_do_pagination(@user_roles) %>
48
+ <%= cama_do_pagination(@user_roles) %>
49
49
 
50
50
  </div>
51
51
 
@@ -13,6 +13,9 @@
13
13
 
14
14
  <div class="row">
15
15
  <%= form_for @user, as: "user", url:{action: @user.new_record? ? :create : :update, id: @user.id}, html:{class: 'validate-user form-horizontal cama_ajax_request', id: "user_form"} do |f| %>
16
+ <%# A non-admin may not change an admin's password or recovery identifiers (email/username), so
17
+ the enforcement in UsersController#user_params/#updated_ajax is mirrored here (H10). %>
18
+ <% credentials_locked = !cama_current_user.may_edit_credentials?(@user) %>
16
19
  <div class="col-md-3">
17
20
  <div class="panel panel-default">
18
21
  <div class="panel-body">
@@ -35,11 +38,11 @@
35
38
  </div>
36
39
  <div class="">
37
40
  <label class="control-label"><%= t('camaleon_cms.admin.table.login')%></label>
38
- <%= f.text_field :username, :class => "form-control required" %>
41
+ <%= f.text_field :username, :class => "form-control required", disabled: credentials_locked %>
39
42
  </div>
40
43
  <div class="">
41
44
  <label class="control-label"><%= t('camaleon_cms.admin.table.e_mail')%></label>
42
- <%= f.email_field :email, :class => "form-control required email"%>
45
+ <%= f.email_field :email, :class => "form-control required email", disabled: credentials_locked %>
43
46
  </div>
44
47
  <% if @user.new_record? %>
45
48
  <div class="">
@@ -54,14 +57,15 @@
54
57
  <div class="">
55
58
  <%= f.label "#{t('camaleon_cms.admin.table.role')}", class: "control-label" %>
56
59
  <div class="">
57
- <%= f.select :role,current_site.user_roles.reorder(:name).decorate.map{|role| [role.the_title, role.slug]}, {}, {:class => "form-control required", disabled: !cama_current_user.role_grantor?(@user)}%>
60
+ <%# Only offer the admin role to someone who may actually grant it (H10); keep it listed when editing a user who already holds it so their role still displays. %>
61
+ <%= f.select :role, current_site.user_roles.reorder(:name).decorate.reject { |role| role.slug == 'admin' && !cama_current_user.role_grantor?(@user, 'admin') && @user&.role.to_s != 'admin' }.map{|role| [role.the_title, role.slug]}, {}, {:class => "form-control required", disabled: !cama_current_user.role_grantor?(@user)}%>
58
62
  </div>
59
63
  </div>
60
64
  <hr>
61
65
  <div class="">
62
66
  <button type="submit" class="btn btn-success btn-block" ><%= @user.new_record? ? t('camaleon_cms.admin.button.create') : t('camaleon_cms.admin.button.update') %></button>
63
67
  </div>
64
- <% if !@user.new_record? %>
68
+ <% if !@user.new_record? && !credentials_locked %>
65
69
  <div class="">
66
70
  <a href="#" class="btn btn-warning btn-block btn-rounded" data-toggle="modal" data-target="#modal_change_password"><%= t('camaleon_cms.admin.button.change_password') %></a>
67
71
  </div>
@@ -45,7 +45,7 @@
45
45
  <%= link_to raw('<i class="fa fa-times"></i>'), { action: :destroy, id: f.id },
46
46
  method: :delete, data: { confirm: t('camaleon_cms.admin.message.delete_item') }, class: "btn btn-danger btn-xs cama_ajax_request", title: "#{t('camaleon_cms.admin.button.delete')}" %>
47
47
  <% end %>
48
- <%= link_to raw('<i class="fa fa-retweet"></i>'), {action: :impersonate, id: f.id }, class: "btn btn-default btn-xs cama_ajax_request", title: "#{cama_t('camaleon_cms.admin.users.impersonate')}" if can? :impersonate, f %>
48
+ <%= link_to raw('<i class="fa fa-retweet"></i>'), {action: :impersonate, id: f.id }, method: :post, class: "btn btn-default btn-xs cama_ajax_request", title: "#{cama_t('camaleon_cms.admin.users.impersonate')}" if can? :impersonate, f %>
49
49
  </td>
50
50
  </tr>
51
51
  <% end %>
@@ -53,7 +53,7 @@
53
53
  </tbody>
54
54
  </table>
55
55
  <%= content_tag("div", raw(t('camaleon_cms.admin.message.data_found_list')), class: "alert alert-warning") if @users.empty? %>
56
- <%= raw cama_do_pagination(@users) %>
56
+ <%= cama_do_pagination(@users) %>
57
57
  </div>
58
58
  </div>
59
59
  <!-- END BASIC TABLE SAMPLE -->
@@ -41,9 +41,15 @@
41
41
  %>
42
42
  <p><%= post.decorate.the_title if post.present? %></p>
43
43
  <% when 'field_attrs'
44
- post = JSON.parse(value) rescue {}
44
+ attrs = JSON.parse(value) rescue nil
45
45
  %>
46
- <p><%= raw("<strong>#{post['attr']}: </strong>#{post['attr']}") if post.present? %></p>
46
+ <% if attrs.is_a?(Hash) && attrs.present? %>
47
+ <%# Security (scan-and-reject policy): attr/value are gated at save like editor %>
48
+ <%# values -- CustomFieldsRelationship scans the DECODED JSON members (any shape) -- %>
49
+ <%# so stored values equal authored values and render verbatim. Guard on Hash so a %>
50
+ <%# non-object JSON value (array/scalar) renders nothing instead of raising a 500. %>
51
+ <p><strong><%= raw attrs['attr'] %>: </strong><%= raw attrs['value'] %></p>
52
+ <% end %>
47
53
  <% else %>
48
54
  <p><%= value %></p>
49
55
  <% end %>