pages_core 3.13.0 → 3.15.0

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 (257) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/assets/builds/pages_core/admin-dist.js +19 -8
  4. data/app/assets/builds/pages_core/admin-dist.js.map +4 -4
  5. data/app/assets/builds/pages_core/admin.css +704 -388
  6. data/app/assets/fonts/Inter-Black.woff2 +0 -0
  7. data/app/assets/fonts/Inter-BlackItalic.woff2 +0 -0
  8. data/app/assets/fonts/Inter-Bold.woff2 +0 -0
  9. data/app/assets/fonts/Inter-BoldItalic.woff2 +0 -0
  10. data/app/assets/fonts/Inter-ExtraBold.woff2 +0 -0
  11. data/app/assets/fonts/Inter-ExtraBoldItalic.woff2 +0 -0
  12. data/app/assets/fonts/Inter-ExtraLight.woff2 +0 -0
  13. data/app/assets/fonts/Inter-ExtraLightItalic.woff2 +0 -0
  14. data/app/assets/fonts/Inter-Italic.woff2 +0 -0
  15. data/app/assets/fonts/Inter-Light.woff2 +0 -0
  16. data/app/assets/fonts/Inter-LightItalic.woff2 +0 -0
  17. data/app/assets/fonts/Inter-Medium.woff2 +0 -0
  18. data/app/assets/fonts/Inter-MediumItalic.woff2 +0 -0
  19. data/app/assets/fonts/Inter-Regular.woff2 +0 -0
  20. data/app/assets/fonts/Inter-SemiBold.woff2 +0 -0
  21. data/app/assets/fonts/Inter-SemiBoldItalic.woff2 +0 -0
  22. data/app/assets/fonts/Inter-Thin.woff2 +0 -0
  23. data/app/assets/fonts/Inter-ThinItalic.woff2 +0 -0
  24. data/app/assets/fonts/InterDisplay-Black.woff2 +0 -0
  25. data/app/assets/fonts/InterDisplay-BlackItalic.woff2 +0 -0
  26. data/app/assets/fonts/InterDisplay-Bold.woff2 +0 -0
  27. data/app/assets/fonts/InterDisplay-BoldItalic.woff2 +0 -0
  28. data/app/assets/fonts/InterDisplay-ExtraBold.woff2 +0 -0
  29. data/app/assets/fonts/InterDisplay-ExtraBoldItalic.woff2 +0 -0
  30. data/app/assets/fonts/InterDisplay-ExtraLight.woff2 +0 -0
  31. data/app/assets/fonts/InterDisplay-ExtraLightItalic.woff2 +0 -0
  32. data/app/assets/fonts/InterDisplay-Italic.woff2 +0 -0
  33. data/app/assets/fonts/InterDisplay-Light.woff2 +0 -0
  34. data/app/assets/fonts/InterDisplay-LightItalic.woff2 +0 -0
  35. data/app/assets/fonts/InterDisplay-Medium.woff2 +0 -0
  36. data/app/assets/fonts/InterDisplay-MediumItalic.woff2 +0 -0
  37. data/app/assets/fonts/InterDisplay-Regular.woff2 +0 -0
  38. data/app/assets/fonts/InterDisplay-SemiBold.woff2 +0 -0
  39. data/app/assets/fonts/InterDisplay-SemiBoldItalic.woff2 +0 -0
  40. data/app/assets/fonts/InterDisplay-Thin.woff2 +0 -0
  41. data/app/assets/fonts/InterDisplay-ThinItalic.woff2 +0 -0
  42. data/app/assets/fonts/InterVariable-Italic.woff2 +0 -0
  43. data/app/assets/fonts/InterVariable.woff2 +0 -0
  44. data/app/assets/stylesheets/pages_core/admin/components/archive.css +1 -1
  45. data/app/assets/stylesheets/pages_core/admin/components/attachments.css +22 -34
  46. data/app/assets/stylesheets/pages_core/admin/components/base.css +1 -68
  47. data/app/assets/stylesheets/pages_core/admin/components/forms.css +107 -48
  48. data/app/assets/stylesheets/pages_core/admin/components/header.css +56 -58
  49. data/app/assets/stylesheets/pages_core/admin/components/image_editor.css +35 -24
  50. data/app/assets/stylesheets/pages_core/admin/components/image_grid.css +28 -27
  51. data/app/assets/stylesheets/pages_core/admin/components/image_uploader.css +5 -5
  52. data/app/assets/stylesheets/pages_core/admin/components/layout.css +7 -1
  53. data/app/assets/stylesheets/pages_core/admin/components/list_table.css +24 -15
  54. data/app/assets/stylesheets/pages_core/admin/components/page_tree.css +63 -104
  55. data/app/assets/stylesheets/pages_core/admin/components/pagination.css +12 -13
  56. data/app/assets/stylesheets/pages_core/admin/components/search.css +1 -16
  57. data/app/assets/stylesheets/pages_core/admin/components/sidebar.css +5 -11
  58. data/app/assets/stylesheets/pages_core/admin/components/tag_editor.css +22 -36
  59. data/app/assets/stylesheets/pages_core/admin/components/toast.css +1 -2
  60. data/app/assets/stylesheets/pages_core/admin/components/toolbar.css +10 -10
  61. data/app/assets/stylesheets/pages_core/admin/components/totp.css +26 -0
  62. data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +37 -51
  63. data/app/assets/stylesheets/pages_core/admin/global/fonts.css +271 -0
  64. data/app/assets/stylesheets/pages_core/admin/global/typography.css +109 -0
  65. data/app/assets/stylesheets/pages_core/admin/vars.css +1 -3
  66. data/app/assets/stylesheets/pages_core/admin.postcss.css +1 -0
  67. data/app/controllers/admin/account_recoveries_controller.rb +87 -0
  68. data/app/controllers/admin/invites_controller.rb +3 -2
  69. data/app/controllers/admin/otp_secrets_controller.rb +45 -0
  70. data/app/controllers/admin/pages_controller.rb +22 -42
  71. data/app/controllers/admin/recovery_codes_controller.rb +32 -0
  72. data/app/controllers/admin/sessions_controller.rb +65 -0
  73. data/app/controllers/admin/users_controller.rb +2 -8
  74. data/app/controllers/concerns/pages_core/authentication.rb +12 -10
  75. data/app/controllers/concerns/pages_core/error_reporting.rb +1 -1
  76. data/app/controllers/concerns/pages_core/page_parameters.rb +29 -0
  77. data/app/controllers/concerns/pages_core/policies_helper.rb +1 -1
  78. data/app/controllers/concerns/pages_core/preview_pages_controller.rb +20 -20
  79. data/app/controllers/pages_core/admin_controller.rb +1 -3
  80. data/app/controllers/pages_core/frontend/pages_controller.rb +2 -6
  81. data/app/formatters/pages_core/html_formatter.rb +2 -4
  82. data/app/helpers/admin/menu_helper.rb +5 -4
  83. data/app/helpers/admin/pages_helper.rb +1 -21
  84. data/app/helpers/pages_core/admin/admin_helper.rb +13 -3
  85. data/app/helpers/pages_core/admin/content_tabs_helper.rb +1 -2
  86. data/app/helpers/pages_core/admin/labelled_field_helper.rb +1 -1
  87. data/app/helpers/pages_core/frontend_helper.rb +1 -1
  88. data/app/helpers/pages_core/images_helper.rb +10 -8
  89. data/app/helpers/pages_core/labelled_form_builder.rb +2 -7
  90. data/app/helpers/pages_core/page_path_helper.rb +1 -1
  91. data/app/javascript/components/Attachments/Attachment.tsx +20 -18
  92. data/app/javascript/components/Attachments/AttachmentEditor.tsx +11 -9
  93. data/app/javascript/components/{Attachments.jsx → Attachments/List.tsx} +58 -63
  94. data/app/javascript/components/Attachments/useAttachments.ts +15 -0
  95. data/app/javascript/components/Attachments.tsx +14 -0
  96. data/app/javascript/components/DateRangeSelect.tsx +105 -0
  97. data/app/javascript/components/DateTimeSelect.tsx +136 -0
  98. data/app/javascript/components/EditableImage.tsx +11 -9
  99. data/app/javascript/components/FileUploadButton.tsx +7 -7
  100. data/app/javascript/components/ImageCropper/FocalPoint.tsx +9 -12
  101. data/app/javascript/components/ImageCropper/Image.tsx +10 -8
  102. data/app/javascript/components/ImageCropper/Toolbar.tsx +11 -12
  103. data/app/javascript/components/ImageCropper/useCrop.ts +24 -53
  104. data/app/javascript/components/ImageCropper.tsx +10 -15
  105. data/app/javascript/components/ImageEditor/Form.tsx +12 -8
  106. data/app/javascript/components/ImageEditor.tsx +12 -7
  107. data/app/javascript/components/ImageGrid/DragElement.tsx +9 -12
  108. data/app/javascript/components/{ImageGrid.jsx → ImageGrid/Grid.tsx} +62 -71
  109. data/app/javascript/components/ImageGrid/GridImage.tsx +22 -23
  110. data/app/javascript/components/ImageGrid/Placeholder.tsx +2 -2
  111. data/app/javascript/components/ImageGrid/useImageGrid.ts +26 -0
  112. data/app/javascript/components/ImageGrid.tsx +15 -0
  113. data/app/javascript/components/ImageUploader.tsx +35 -22
  114. data/app/javascript/components/LabelledField.tsx +34 -0
  115. data/app/javascript/components/Modal.tsx +2 -2
  116. data/app/javascript/components/PageForm/Block.tsx +81 -0
  117. data/app/javascript/components/PageForm/Content.tsx +54 -0
  118. data/app/javascript/components/PageForm/Dates.tsx +66 -0
  119. data/app/javascript/components/PageForm/Files.tsx +28 -0
  120. data/app/javascript/components/PageForm/Form.tsx +41 -0
  121. data/app/javascript/components/PageForm/Images.tsx +28 -0
  122. data/app/javascript/components/PageForm/LocaleLinks.tsx +36 -0
  123. data/app/javascript/components/PageForm/Metadata.tsx +67 -0
  124. data/app/javascript/components/PageForm/Options.tsx +180 -0
  125. data/app/javascript/components/PageForm/PageDescription.tsx +48 -0
  126. data/app/javascript/components/PageForm/PathSegment.tsx +65 -0
  127. data/app/javascript/components/PageForm/TabPanel.tsx +21 -0
  128. data/app/javascript/components/PageForm/Tabs.tsx +33 -0
  129. data/app/javascript/components/PageForm/UnconfiguredContent.tsx +42 -0
  130. data/app/javascript/components/PageForm/pageParams.ts +95 -0
  131. data/app/javascript/components/PageForm/preview.ts +23 -0
  132. data/app/javascript/components/PageForm/usePage.ts +169 -0
  133. data/app/javascript/components/PageForm/useTabs.ts +46 -0
  134. data/app/javascript/components/PageForm.tsx +163 -0
  135. data/app/javascript/components/PageImages.tsx +7 -9
  136. data/app/javascript/components/PageTree/Draggable.tsx +40 -39
  137. data/app/javascript/components/PageTree/Node.tsx +62 -56
  138. data/app/javascript/components/PageTree/PageName.tsx +28 -0
  139. data/app/javascript/components/PageTree.tsx +65 -53
  140. data/app/javascript/components/{RichTextArea.jsx → RichTextArea.tsx} +98 -79
  141. data/app/javascript/components/RichTextToolbarButton.tsx +4 -6
  142. data/app/javascript/components/TagEditor/AddTagForm.tsx +19 -12
  143. data/app/javascript/components/TagEditor/Editor.tsx +32 -0
  144. data/app/javascript/components/TagEditor/Tag.tsx +6 -4
  145. data/app/javascript/components/TagEditor/useTags.ts +58 -0
  146. data/app/javascript/components/TagEditor.tsx +8 -58
  147. data/app/javascript/components/Toast.tsx +3 -3
  148. data/app/javascript/components/drag/draggedOrder.ts +22 -14
  149. data/app/javascript/components/drag/useDragCollection.ts +35 -30
  150. data/app/javascript/components/drag/useDragUploader.ts +32 -21
  151. data/app/javascript/components/drag/useDraggable.ts +7 -6
  152. data/app/javascript/components/drag.ts +0 -1
  153. data/app/javascript/components.ts +1 -3
  154. data/app/javascript/features/RichText.tsx +2 -3
  155. data/app/javascript/features/contentTabs.ts +79 -0
  156. data/app/javascript/index.ts +5 -14
  157. data/app/javascript/lib/Tree.ts +31 -45
  158. data/app/javascript/lib/request.ts +11 -11
  159. data/app/javascript/stores/useToastStore.ts +1 -1
  160. data/app/javascript/types/Attachments.ts +29 -0
  161. data/app/javascript/types/Crop.ts +36 -0
  162. data/app/javascript/types/Drag.ts +34 -0
  163. data/app/javascript/types/Images.ts +47 -0
  164. data/app/javascript/types/PageEditor.ts +26 -0
  165. data/app/javascript/types/Pages.ts +75 -0
  166. data/app/javascript/types/Tags.ts +9 -0
  167. data/app/javascript/types/Template.ts +24 -0
  168. data/app/javascript/types/Trees.ts +19 -0
  169. data/app/javascript/types.ts +2 -25
  170. data/app/mailers/admin_mailer.rb +2 -2
  171. data/app/models/attachment.rb +1 -1
  172. data/app/models/concerns/pages_core/authenticable_user.rb +63 -0
  173. data/app/models/concerns/pages_core/emailable.rb +16 -0
  174. data/app/models/concerns/pages_core/page_model/templateable.rb +2 -16
  175. data/app/models/invite.rb +2 -6
  176. data/app/models/otp_secret.rb +101 -0
  177. data/app/models/page.rb +0 -3
  178. data/app/models/user.rb +2 -68
  179. data/app/policies/page_policy.rb +6 -2
  180. data/app/policies/user_policy.rb +4 -0
  181. data/app/resources/admin/page_resource.rb +95 -0
  182. data/app/resources/admin/page_tree_resource.rb +27 -0
  183. data/app/resources/admin/template_configuration_resource.rb +50 -0
  184. data/app/views/admin/account_recoveries/new.html.erb +22 -0
  185. data/app/views/admin/account_recoveries/show.html.erb +37 -0
  186. data/app/views/admin/invites/show.html.erb +1 -1
  187. data/app/views/admin/news/_sidebar.html.erb +2 -4
  188. data/app/views/admin/news/index.html.erb +0 -1
  189. data/app/views/admin/otp_secrets/create.html.erb +7 -0
  190. data/app/views/admin/otp_secrets/new.html.erb +60 -0
  191. data/app/views/admin/pages/_form.html.erb +10 -30
  192. data/app/views/admin/pages/_search_bar.html.erb +1 -1
  193. data/app/views/admin/pages/edit.html.erb +1 -57
  194. data/app/views/admin/pages/index.html.erb +1 -1
  195. data/app/views/admin/pages/new.html.erb +1 -44
  196. data/app/views/admin/recovery_codes/_codes.html.erb +14 -0
  197. data/app/views/admin/recovery_codes/create.html.erb +7 -0
  198. data/app/views/admin/recovery_codes/new.html.erb +11 -0
  199. data/app/views/admin/sessions/_otp_form.html.erb +13 -0
  200. data/app/views/admin/sessions/new.html.erb +31 -0
  201. data/app/views/admin/sessions/verify_otp.html.erb +19 -0
  202. data/app/views/admin/users/_access_control.html.erb +5 -1
  203. data/app/views/admin/users/_list.html.erb +12 -7
  204. data/app/views/admin/users/edit.html.erb +31 -1
  205. data/app/views/admin/users/new.html.erb +1 -1
  206. data/app/views/admin_mailer/account_recovery.text.erb +10 -0
  207. data/app/views/layouts/admin/_header.html.erb +3 -5
  208. data/app/views/layouts/admin/_page_header.html.erb +1 -2
  209. data/app/views/layouts/admin/_toast.html.erb +12 -0
  210. data/app/views/layouts/admin.html.erb +2 -2
  211. data/config/locales/en.yml +11 -7
  212. data/config/routes.rb +13 -12
  213. data/db/migrate/20240126160700_add_2fa_fields.rb +26 -0
  214. data/db/migrate/20240129201300_remove_password_reset_tokens.rb +13 -0
  215. data/db/migrate/20240131140700_change_email_to_citext.rb +18 -0
  216. data/db/migrate/20240201160700_remove_persistent_data.rb +7 -0
  217. data/db/migrate/20240508145300_remove_categories.rb +21 -0
  218. data/lib/pages_core/configuration/base.rb +2 -2
  219. data/lib/pages_core/templates/configuration.rb +1 -1
  220. data/lib/pages_core/templates/configuration_proxy.rb +2 -2
  221. data/lib/pages_core/templates/template_configuration.rb +11 -1
  222. data/lib/pages_core/templates.rb +6 -4
  223. data/lib/pages_core/version.rb +1 -1
  224. data/lib/pages_core.rb +6 -0
  225. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/gridOverlay.ts +6 -7
  226. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/responsiveEmbeds.ts +17 -12
  227. data/lib/rails/generators/pages_core/rspec/rspec_generator.rb +0 -2
  228. data/lib/rails/generators/pages_core/rspec/templates/rails_helper.rb +3 -4
  229. metadata +143 -35
  230. data/app/assets/stylesheets/pages_core/admin/components/login.css +0 -33
  231. data/app/controllers/admin/categories_controller.rb +0 -56
  232. data/app/controllers/admin/password_resets_controller.rb +0 -85
  233. data/app/controllers/concerns/pages_core/admin/persistent_params.rb +0 -75
  234. data/app/controllers/sessions_controller.rb +0 -27
  235. data/app/helpers/pages_core/admin/page_blocks_helper.rb +0 -66
  236. data/app/helpers/pages_core/admin/page_json_helper.rb +0 -23
  237. data/app/javascript/components/DateRangeSelect.jsx +0 -225
  238. data/app/javascript/components/PageDates.jsx +0 -73
  239. data/app/javascript/components/PageFiles.jsx +0 -25
  240. data/app/javascript/components/PageTree/types.ts +0 -15
  241. data/app/javascript/components/drag/types.ts +0 -28
  242. data/app/javascript/controllers/EditPageController.ts +0 -22
  243. data/app/javascript/controllers/LoginController.ts +0 -32
  244. data/app/javascript/controllers/MainController.ts +0 -74
  245. data/app/javascript/controllers/PageOptionsController.js +0 -67
  246. data/app/models/category.rb +0 -22
  247. data/app/models/page_category.rb +0 -6
  248. data/app/models/password_reset_token.rb +0 -34
  249. data/app/views/admin/pages/_edit_content.html.erb +0 -19
  250. data/app/views/admin/pages/_edit_files.html.erb +0 -4
  251. data/app/views/admin/pages/_edit_images.html.erb +0 -4
  252. data/app/views/admin/pages/_edit_metadata.html.erb +0 -35
  253. data/app/views/admin/pages/_edit_options.html.erb +0 -91
  254. data/app/views/admin/password_resets/show.html.erb +0 -21
  255. data/app/views/admin/users/login.html.erb +0 -65
  256. data/app/views/admin_mailer/password_reset.text.erb +0 -11
  257. data/lib/rails/generators/pages_core/rspec/templates/mailer_macros.rb +0 -11
@@ -62,11 +62,12 @@ module Admin
62
62
  return if @invite && secure_compare(@invite.token, params[:token])
63
63
 
64
64
  flash[:notice] = t("pages_core.invite_expired")
65
- redirect_to(login_admin_users_url) && return
65
+ redirect_to(admin_login_url)
66
66
  end
67
67
 
68
68
  def user_params
69
- params.require(:user).permit(:name, :email, :password, :confirm_password)
69
+ params.require(:user)
70
+ .permit(:name, :email, :password, :password_confirmation)
70
71
  end
71
72
 
72
73
  def invite_params
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Admin
4
+ class OtpSecretsController < Admin::AdminController
5
+ before_action :require_otp_disabled, only: %i[new create]
6
+ before_action :find_otp_secret
7
+
8
+ def new
9
+ @otp_secret.generate
10
+ end
11
+
12
+ def create
13
+ if @otp_secret.verify(otp_secret_params)
14
+ @recovery_codes = @otp_secret.generate_recovery_codes
15
+ @otp_secret.enable!(@recovery_codes)
16
+ else
17
+ flash[:error] = t("pages_core.otp.invalid_code")
18
+ redirect_to new_admin_otp_secret_path
19
+ end
20
+ end
21
+
22
+ def destroy
23
+ @otp_secret.disable!
24
+ flash[:notice] = t("pages_core.otp.disabled")
25
+ redirect_to edit_admin_user_path(current_user)
26
+ end
27
+
28
+ private
29
+
30
+ def find_otp_secret
31
+ @otp_secret = OtpSecret.new(current_user)
32
+ end
33
+
34
+ def otp_secret_params
35
+ params.permit(:signed_message, :otp)
36
+ end
37
+
38
+ def require_otp_disabled
39
+ return unless current_user.otp_enabled?
40
+
41
+ flash[:notice] = t("pages_core.otp.already_enabled")
42
+ redirect_to edit_admin_user_path(current_user)
43
+ end
44
+ end
45
+ end
@@ -2,9 +2,8 @@
2
2
 
3
3
  module Admin
4
4
  class PagesController < Admin::AdminController
5
- include PagesCore::Admin::PageJsonHelper
5
+ include PagesCore::PageParameters
6
6
 
7
- before_action :find_categories
8
7
  before_action :find_page, only: %i[show edit update destroy move]
9
8
 
10
9
  require_authorization
@@ -39,26 +38,26 @@ module Admin
39
38
  def edit; end
40
39
 
41
40
  def create
42
- @page = build_page(content_locale, page_params,
43
- param_categories).tap(&:save)
44
- if @page.valid?
45
- respond_with_page(@page) do
41
+ @page = build_page(content_locale, page_params).tap(&:save)
42
+
43
+ respond_with_page(@page) do
44
+ if @page.valid?
46
45
  redirect_to(edit_admin_page_url(content_locale, @page))
46
+ else
47
+ render action: :new
47
48
  end
48
- else
49
- render action: :new
50
49
  end
51
50
  end
52
51
 
53
52
  def update
54
- if @page.update(page_params)
55
- @page.categories = param_categories
56
- respond_with_page(@page) do
53
+ @page.update(page_params)
54
+ respond_with_page(@page) do
55
+ if @page.valid?
57
56
  flash[:notice] = t("pages_core.changes_saved")
58
57
  redirect_to edit_admin_page_url(content_locale, @page)
58
+ else
59
+ render action: :edit
59
60
  end
60
- else
61
- render action: :edit
62
61
  end
63
62
  end
64
63
 
@@ -75,53 +74,34 @@ module Admin
75
74
 
76
75
  private
77
76
 
78
- def build_page(locale, attributes = nil, categories = nil)
77
+ def build_page(locale, attributes = nil)
79
78
  Page.new.localize(locale).tap do |page|
80
79
  page.author = default_author || current_user
81
80
  page.attributes = attributes if attributes
82
- page.categories = categories if categories
83
81
  end
84
82
  end
85
83
 
86
84
  def default_author
87
- User.find_by_email(PagesCore.config.default_author)
88
- end
89
-
90
- def page_attributes
91
- %i[template user_id status feed_enabled published_at redirect_to
92
- image_link news_page unique_name pinned parent_page_id serialized_tags
93
- meta_image_id starts_at ends_at all_day image_id path_segment
94
- meta_title meta_description open_graph_title open_graph_description]
85
+ User.find_by(email: PagesCore.config.default_author)
95
86
  end
96
87
 
97
88
  def page_params
98
- params.require(:page).permit(
99
- PagesCore::Templates::TemplateConfiguration.all_blocks +
100
- page_attributes,
101
- page_images_attributes: %i[id position image_id primary _destroy],
102
- page_files_attributes: %i[id position attachment_id _destroy]
103
- )
104
- end
105
-
106
- def param_categories
107
- return [] unless params[:category]
108
-
109
- params.permit(category: {})[:category].to_hash
110
- .map { |id, _| Category.find(id) }
89
+ params.require(:page).permit(page_content_attributes)
111
90
  end
112
91
 
113
92
  def find_page
114
93
  @page = Page.find(params[:id]).localize(content_locale)
115
94
  end
116
95
 
117
- def find_categories
118
- @categories = Category.order("name")
119
- end
120
-
121
96
  def respond_with_page(page, &block)
122
97
  respond_to do |format|
123
- format.html { block.call }
124
- format.json { render json: page_json(page) }
98
+ format.html(&block)
99
+ format.json do
100
+ render json: ::Admin::PageResource.new(
101
+ page,
102
+ params: { user: current_user }
103
+ )
104
+ end
125
105
  end
126
106
  end
127
107
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Admin
4
+ class RecoveryCodesController < Admin::AdminController
5
+ before_action :require_otp_enabled
6
+ before_action :find_otp_secret
7
+
8
+ def new; end
9
+
10
+ def create
11
+ if @otp_secret.validate_otp!(params[:otp])
12
+ @recovery_codes = @otp_secret.regenerate_recovery_codes!
13
+ else
14
+ flash[:error] = t("pages_core.otp.invalid_code")
15
+ redirect_to new_admin_recovery_codes_path
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def find_otp_secret
22
+ @otp_secret = OtpSecret.new(current_user)
23
+ end
24
+
25
+ def require_otp_enabled
26
+ return if current_user.otp_enabled?
27
+
28
+ flash[:notice] = t("pages_core.otp.required")
29
+ redirect_to edit_admin_user_path(current_user)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Admin
4
+ class SessionsController < Admin::AdminController
5
+ before_action :require_authentication, only: %i[destroy]
6
+ before_action :find_user, only: %i[create]
7
+ before_action :find_signed_user, only: %i[verify_otp]
8
+ before_action :require_user, only: %i[create verify_otp]
9
+
10
+ def new
11
+ redirect_to admin_default_url if logged_in?
12
+ end
13
+
14
+ def create
15
+ if @user.otp_enabled?
16
+ @signed_user_id = message_verifier.generate(
17
+ @user.id, expires_in: 1.hour
18
+ )
19
+ render template: "admin/sessions/verify_otp"
20
+ else
21
+ authenticate!(@user)
22
+ redirect_to admin_default_url
23
+ end
24
+ end
25
+
26
+ def destroy
27
+ flash[:notice] = t("pages_core.logged_out")
28
+ deauthenticate!
29
+ redirect_to admin_login_url
30
+ end
31
+
32
+ def verify_otp
33
+ @otp_secret = OtpSecret.new(@user)
34
+ if @otp_secret.validate_otp!(params[:otp])
35
+ authenticate!(@user)
36
+ redirect_to admin_default_url
37
+ else
38
+ flash[:notice] = t("pages_core.otp.invalid_code")
39
+ render template: "admin/sessions/verify_otp"
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def find_signed_user
46
+ @signed_user_id = params[:signed_user_id]
47
+ @user = User.find(message_verifier.verify(@signed_user_id))
48
+ end
49
+
50
+ def find_user
51
+ @user = User.authenticate(params[:email], password: params[:password])
52
+ end
53
+
54
+ def message_verifier
55
+ Rails.application.message_verifier(:session)
56
+ end
57
+
58
+ def require_user
59
+ return if @user
60
+
61
+ flash[:notice] = t("pages_core.invalid_login")
62
+ redirect_to admin_login_url
63
+ end
64
+ end
65
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Admin
4
4
  class UsersController < Admin::AdminController
5
- before_action :require_authentication, except: %i[new create login]
5
+ before_action :require_authentication, except: %i[new create]
6
6
  before_action :require_no_users, only: %i[new create]
7
7
  before_action(
8
8
  :find_user,
@@ -19,12 +19,6 @@ module Admin
19
19
  @invites = []
20
20
  end
21
21
 
22
- def login
23
- return unless logged_in?
24
-
25
- redirect_to admin_default_url
26
- end
27
-
28
22
  def show; end
29
23
 
30
24
  def new
@@ -81,7 +75,7 @@ module Admin
81
75
  { role_names: [] }]
82
76
  end
83
77
  if User.none? || (@user && policy(@user).change_password?)
84
- permitted_params += %i[password confirm_password]
78
+ permitted_params += %i[password password_confirmation]
85
79
  end
86
80
  params.require(:user).permit(permitted_params)
87
81
  end
@@ -35,20 +35,22 @@ module PagesCore
35
35
  @current_user = user
36
36
  end
37
37
 
38
- def start_authenticated_session
39
- if session[:current_user_id]
40
- user = User.where(id: session[:current_user_id]).first
41
- end
42
-
43
- return unless user&.can_login?
38
+ def finalize_authenticated_session
39
+ return unless logged_in?
44
40
 
45
- authenticated(user)
41
+ session[:current_user] =
42
+ { id: current_user.id, token: current_user.session_token }
46
43
  end
47
44
 
48
- def finalize_authenticated_session
49
- return unless current_user
45
+ def start_authenticated_session
46
+ user_session = session.fetch(:current_user, nil)&.symbolize_keys
47
+
48
+ return unless user_session
50
49
 
51
- session[:current_user_id] = current_user.id
50
+ user = User.find_by(id: user_session[:id])
51
+ return unless user && user.session_token == user_session[:token]
52
+
53
+ authenticated(user)
52
54
  end
53
55
  end
54
56
  end
@@ -11,7 +11,7 @@ module PagesCore
11
11
  protected
12
12
 
13
13
  def configure_sentry_scope
14
- return if Rails.env.test? || !Object.const_defined?("Sentry")
14
+ return if Rails.env.test? || !Object.const_defined?(:Sentry)
15
15
 
16
16
  Sentry.set_context("params", params.to_unsafe_h)
17
17
  Sentry.set_tags(locale: params[:locale] || I18n.default_locale.to_s)
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PagesCore
4
+ module PageParameters
5
+ extend ActiveSupport::Concern
6
+
7
+ def page_attachment_attributes
8
+ { page_images_attributes: %i[id position image_id primary _destroy],
9
+ page_files_attributes: %i[id position attachment_id _destroy] }
10
+ end
11
+
12
+ def page_content_attributes
13
+ locales = PagesCore.config.locales&.keys || [I18n.default_locale]
14
+ [page_static_attributes,
15
+ PagesCore::Templates::TemplateConfiguration.all_blocks,
16
+ :path_segment,
17
+ (PagesCore::Templates::TemplateConfiguration
18
+ .localized_blocks + %i[path_segment])
19
+ .index_with { locales },
20
+ page_attachment_attributes]
21
+ end
22
+
23
+ def page_static_attributes
24
+ %i[template user_id status feed_enabled published_at redirect_to
25
+ news_page unique_name pinned parent_page_id serialized_tags
26
+ meta_image_id starts_at ends_at all_day]
27
+ end
28
+ end
29
+ end
@@ -35,7 +35,7 @@ module PagesCore
35
35
  end
36
36
 
37
37
  def verify_policy(record)
38
- return true if policy(record).public_send("#{action_name}?")
38
+ return true if policy(record).public_send(:"#{action_name}?")
39
39
 
40
40
  raise PagesCore::NotAuthorized
41
41
  end
@@ -3,11 +3,27 @@
3
3
  module PagesCore
4
4
  module PreviewPagesController
5
5
  extend ActiveSupport::Concern
6
+ include PagesCore::PageParameters
7
+
8
+ included do
9
+ before_action :disable_xss_protection, only: %i[preview]
10
+ end
6
11
 
7
12
  def preview?
8
13
  @preview || false
9
14
  end
10
15
 
16
+ def preview
17
+ render_error 403 unless logged_in?
18
+
19
+ @preview = true
20
+ @page = Page.find_by(id: params[:page_id]) || Page.new
21
+ @page.readonly!
22
+ @page.assign_attributes(preview_page_params)
23
+
24
+ render_page
25
+ end
26
+
11
27
  private
12
28
 
13
29
  def disable_xss_protection
@@ -17,31 +33,15 @@ module PagesCore
17
33
  response.headers["X-XSS-Protection"] = "0"
18
34
  end
19
35
 
20
- def permitted_page_attributes
21
- %i[template user_id status feed_enabled published_at
22
- redirect_to image_link news_page
23
- unique_name pinned parent_page_id]
24
- end
25
-
26
- def page_params
27
- params.require(:page).permit(
28
- Page.localized_attributes + permitted_page_attributes
29
- )
30
- end
31
-
32
- def preview_page(page)
33
- redirect_to(page_url(content_locale, page)) && return unless logged_in?
34
-
35
- disable_xss_protection
36
-
37
- @preview = true
38
- page.attributes = page_params.merge(
36
+ def preview_page_params
37
+ ActionController::Parameters.new(
38
+ JSON.parse(params.require(:preview_page))
39
+ ).permit(:id, page_content_attributes).merge(
39
40
  status: 2,
40
41
  published_at: Time.zone.now,
41
42
  locale: content_locale,
42
43
  redirect_to: nil
43
44
  )
44
- render_page
45
45
  end
46
46
  end
47
47
  end
@@ -4,8 +4,6 @@
4
4
  # authorization and other common code for the Admin set of controllers.
5
5
  module PagesCore
6
6
  class AdminController < ::ApplicationController
7
- include PagesCore::Admin::PersistentParams
8
-
9
7
  protect_from_forgery with: :exception
10
8
 
11
9
  before_action :set_i18n_locale
@@ -48,7 +46,7 @@ module PagesCore
48
46
  if User.count < 1
49
47
  redirect_to(new_admin_user_url)
50
48
  else
51
- redirect_to(login_admin_users_url)
49
+ redirect_to(admin_login_url)
52
50
  end
53
51
  end
54
52
 
@@ -12,8 +12,8 @@ module PagesCore
12
12
 
13
13
  before_action :load_root_pages
14
14
  before_action :find_page_by_path, only: [:show]
15
- before_action :find_page, only: %i[show preview]
16
- before_action :require_page, only: %i[show preview]
15
+ before_action :find_page, only: %i[show]
16
+ before_action :require_page, only: %i[show]
17
17
  before_action :canonicalize_url, only: [:show]
18
18
  static_cache :index, :show
19
19
 
@@ -27,10 +27,6 @@ module PagesCore
27
27
  end
28
28
  end
29
29
 
30
- def preview
31
- preview_page(@page)
32
- end
33
-
34
30
  def show
35
31
  respond_to do |format|
36
32
  format.html { render_published_page(@page) }
@@ -38,8 +38,7 @@ module PagesCore
38
38
  def find_attachments(str)
39
39
  str.match(attachment_expression)[1]
40
40
  .split(",")
41
- .map { |id| find_attachment(id) }
42
- .compact
41
+ .filter_map { |id| find_attachment(id) }
43
42
  end
44
43
 
45
44
  def find_file(id)
@@ -51,8 +50,7 @@ module PagesCore
51
50
  def find_files(str)
52
51
  str.match(file_expression)[1]
53
52
  .split(",")
54
- .map { |id| find_file(id) }
55
- .compact
53
+ .filter_map { |id| find_file(id) }
56
54
  end
57
55
 
58
56
  def fix_markup(str)
@@ -5,12 +5,13 @@ module Admin
5
5
  include PagesCore::LocalesHelper
6
6
 
7
7
  def header_tabs(group)
8
+ return unless menu_items_for(group).any?
9
+
8
10
  tag.ul(class: group.to_s) do
9
11
  safe_join(menu_items_for(group).map do |item|
10
12
  tag.li do
11
13
  path = instance_eval(&item.path)
12
- link_to(item.label,
13
- path,
14
+ link_to(item.label, path,
14
15
  class: (current_menu_item?(item) ? "current" : ""))
15
16
  end
16
17
  end)
@@ -24,9 +25,9 @@ module Admin
24
25
  .select { |_, routing| routing[:controller] == params[:controller] }
25
26
  end
26
27
 
27
- def find_menu_candidate(&block)
28
+ def find_menu_candidate(&)
28
29
  menu_item_candidates
29
- .select { |item, routing| block.call(item, routing) }
30
+ .select(&)
30
31
  .try(&:first)
31
32
  .try(&:first)
32
33
  end
@@ -2,8 +2,6 @@
2
2
 
3
3
  module Admin
4
4
  module PagesHelper
5
- include PagesCore::Admin::PageBlocksHelper
6
-
7
5
  def autopublish_notice(page)
8
6
  return unless page.autopublish?
9
7
 
@@ -13,22 +11,8 @@ module Admin
13
11
  end
14
12
  end
15
13
 
16
- def available_templates_for_select
17
- PagesCore::Templates.names.collect do |template|
18
- if template == "index"
19
- ["[Default]", "index"]
20
- else
21
- [template.humanize, template]
22
- end
23
- end
24
- end
25
-
26
- def file_embed_code(file)
27
- "[file:#{file.id}]"
28
- end
29
-
30
14
  def news_section_name(page, news_pages)
31
- if news_pages.select { |p| p.name == page.name }.length > 1
15
+ if news_pages.count { |p| p.name == page.name } > 1
32
16
  page_name(page, include_parents: true)
33
17
  else
34
18
  page_name(page)
@@ -83,10 +67,6 @@ module Admin
83
67
 
84
68
  private
85
69
 
86
- def nested_array?(array)
87
- array.present? && array.first.is_a?(Array)
88
- end
89
-
90
70
  def page_name_with_fallback(page)
91
71
  if page.name?
92
72
  page.name.to_s
@@ -7,7 +7,6 @@ module PagesCore
7
7
  include PagesCore::Admin::DateRangeHelper
8
8
  include PagesCore::Admin::ImageUploadsHelper
9
9
  include PagesCore::Admin::LocalesHelper
10
- include PagesCore::Admin::PageJsonHelper
11
10
  include PagesCore::Admin::LabelledFieldHelper
12
11
  include PagesCore::Admin::TagEditorHelper
13
12
 
@@ -18,12 +17,12 @@ module PagesCore
18
17
  value: content))
19
18
  end
20
19
 
21
- def locale_links(&block)
20
+ def locale_links
22
21
  return unless PagesCore.config.localizations?
23
22
 
24
23
  safe_join(
25
24
  PagesCore.config.locales.map do |locale, name|
26
- link_to(name, block.call(locale),
25
+ link_to(name, yield(locale),
27
26
  class: ("current" if locale == params[:locale].to_sym))
28
27
  end
29
28
  )
@@ -33,6 +32,17 @@ module PagesCore
33
32
  %w[January February March April May June July August September October
34
33
  November December][month - 1]
35
34
  end
35
+
36
+ def qr_code(url)
37
+ ActiveSupport::SafeBuffer.new(
38
+ RQRCode::QRCode.new(url)
39
+ .as_svg({ color: "000",
40
+ shape_rendering: "crispEdges",
41
+ module_size: 10,
42
+ use_path: true,
43
+ viewbox: true })
44
+ )
45
+ end
36
46
  end
37
47
  end
38
48
  end
@@ -31,8 +31,7 @@ module PagesCore
31
31
  class: "content-tab",
32
32
  id: "content-tab-#{key}",
33
33
  role: "tabpanel",
34
- data: { tab: key,
35
- "main-target" => "tab" })
34
+ data: { tab: key })
36
35
  end
37
36
  end
38
37
  end
@@ -53,7 +53,7 @@ module PagesCore
53
53
 
54
54
  def labelled_field_label(label, options = {})
55
55
  tag.label do
56
- safe_join([label, labelled_field_errors(options[:errors])])
56
+ safe_join([label, labelled_field_errors(options[:errors])], " ")
57
57
  end
58
58
  end
59
59
 
@@ -10,6 +10,6 @@ module PagesCore
10
10
  @root_page ||= root_pages.first
11
11
  end
12
12
 
13
- attr_reader :search_query, :search_category_id
13
+ attr_reader :search_query
14
14
  end
15
15
  end