katalyst-koi 4.20.1 → 5.0.0.alpha.1

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 (236) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/koi/icons/add.svg +3 -0
  3. data/app/assets/images/koi/icons/close.svg +1 -0
  4. data/app/assets/images/koi/koi.png +0 -0
  5. data/app/assets/javascripts/koi/controllers/file_field_controller.js +2 -2
  6. data/app/assets/javascripts/koi/controllers/index.js +0 -3
  7. data/app/assets/javascripts/koi/controllers/koi/modal_controller.js +40 -0
  8. data/app/assets/javascripts/koi/controllers/navigation_controller.js +14 -21
  9. data/app/assets/javascripts/koi/controllers/webauthn_authentication_controller.js +12 -13
  10. data/app/assets/javascripts/koi/controllers/webauthn_registration_controller.js +11 -10
  11. data/app/assets/stylesheets/koi/blocks/actions.css +8 -0
  12. data/app/assets/stylesheets/koi/blocks/application-header.css +15 -0
  13. data/app/assets/stylesheets/koi/blocks/application-navigation.css +54 -0
  14. data/app/assets/stylesheets/koi/blocks/button.css +90 -0
  15. data/app/assets/stylesheets/koi/blocks/flash.css +19 -0
  16. data/app/assets/stylesheets/koi/blocks/icon.css +15 -0
  17. data/app/assets/stylesheets/koi/blocks/index.css +13 -0
  18. data/app/assets/stylesheets/koi/blocks/modal.css +26 -0
  19. data/app/assets/stylesheets/koi/blocks/navigation.css +23 -0
  20. data/app/assets/stylesheets/koi/blocks/page-header.css +31 -0
  21. data/app/assets/stylesheets/koi/blocks/pagy.css +82 -0
  22. data/app/assets/stylesheets/koi/blocks/prose.css +37 -0
  23. data/app/assets/stylesheets/koi/blocks/tables/index.css +4 -0
  24. data/app/assets/stylesheets/koi/{components/_query.scss → blocks/tables/query.css} +13 -13
  25. data/app/assets/stylesheets/koi/{base/_tables.scss → blocks/tables/table.css} +11 -59
  26. data/app/assets/stylesheets/koi/compositions/cover.css +17 -0
  27. data/app/assets/stylesheets/koi/{base/_flow.scss → compositions/flow.css} +1 -1
  28. data/app/assets/stylesheets/koi/compositions/index.css +4 -0
  29. data/app/assets/stylesheets/koi/compositions/wrapper.css +11 -0
  30. data/app/assets/stylesheets/koi/forms/caption.css +22 -0
  31. data/app/assets/stylesheets/koi/forms/checkboxes.css +153 -0
  32. data/app/assets/stylesheets/koi/forms/date-input.css +12 -0
  33. data/app/assets/stylesheets/koi/{components/_document-field.scss → forms/document-field.css} +20 -15
  34. data/app/assets/stylesheets/koi/forms/errors.css +38 -0
  35. data/app/assets/stylesheets/koi/forms/fieldset.css +73 -0
  36. data/app/assets/stylesheets/koi/forms/file-upload.css +20 -0
  37. data/app/assets/stylesheets/koi/forms/form-group.css +19 -0
  38. data/app/assets/stylesheets/koi/forms/hint.css +11 -0
  39. data/app/assets/stylesheets/koi/forms/image-field.css +96 -0
  40. data/app/assets/stylesheets/koi/forms/index.css +44 -0
  41. data/app/assets/stylesheets/koi/forms/input.css +194 -0
  42. data/app/assets/stylesheets/koi/forms/label.css +43 -0
  43. data/app/assets/stylesheets/koi/forms/password.css +18 -0
  44. data/app/assets/stylesheets/koi/forms/radios.css +162 -0
  45. data/app/assets/stylesheets/koi/forms/select.css +18 -0
  46. data/app/assets/stylesheets/koi/forms/textarea.css +3 -0
  47. data/app/assets/stylesheets/koi/forms/trix.css +33 -0
  48. data/app/assets/stylesheets/koi/global/fonts.css +22 -0
  49. data/app/assets/stylesheets/koi/global/global-styles.css +297 -0
  50. data/app/assets/stylesheets/koi/global/reset.css +98 -0
  51. data/app/assets/stylesheets/koi/global/variables.css +97 -0
  52. data/app/assets/stylesheets/koi/icons.css +14 -0
  53. data/app/assets/stylesheets/koi/{admin.scss → index.css} +16 -7
  54. data/app/assets/stylesheets/koi/login.css +26 -0
  55. data/app/assets/stylesheets/koi/themes/_index.scss +0 -1
  56. data/app/assets/stylesheets/koi/utilities/index.css +1 -0
  57. data/app/assets/stylesheets/koi/utilities/visually-hidden.css +18 -0
  58. data/app/components/concerns/koi/tables/cells.rb +3 -3
  59. data/app/components/koi/header/edit_component.rb +1 -1
  60. data/app/components/koi/header_component.html.erb +12 -11
  61. data/app/components/koi/header_component.rb +2 -0
  62. data/app/components/koi/table_component.rb +8 -0
  63. data/app/components/koi/tables/cells/link_component.rb +2 -2
  64. data/app/controllers/admin/admin_users_controller.rb +26 -20
  65. data/app/controllers/admin/application_controller.rb +1 -3
  66. data/app/controllers/admin/caches_controller.rb +1 -1
  67. data/app/controllers/admin/credentials_controller.rb +19 -15
  68. data/app/controllers/admin/otps_controller.rb +16 -14
  69. data/app/controllers/admin/sessions_controller.rb +18 -3
  70. data/app/controllers/admin/tokens_controller.rb +4 -2
  71. data/app/controllers/admin/url_rewrites_controller.rb +19 -17
  72. data/app/controllers/admin/well_knowns_controller.rb +20 -18
  73. data/app/controllers/concerns/koi/controller/records_authentication.rb +10 -23
  74. data/app/controllers/concerns/koi/controller.rb +25 -4
  75. data/app/controllers/well_knowns_controller.rb +1 -1
  76. data/app/helpers/koi/form_helper.rb +18 -0
  77. data/app/helpers/koi/header_helper.rb +122 -0
  78. data/app/helpers/koi/index_actions_helper.rb +3 -2
  79. data/app/helpers/koi/modal_helper.rb +71 -0
  80. data/app/models/admin/user.rb +7 -1
  81. data/app/models/url_rewrite.rb +1 -9
  82. data/app/views/admin/admin_users/_form.html+self.erb +8 -0
  83. data/app/views/admin/admin_users/_form.html.erb +8 -0
  84. data/app/views/admin/admin_users/archived.html.erb +7 -4
  85. data/app/views/admin/admin_users/edit.html+self.erb +12 -0
  86. data/app/views/admin/admin_users/edit.html.erb +13 -8
  87. data/app/views/admin/admin_users/index.html.erb +10 -5
  88. data/app/views/admin/admin_users/new.html.erb +8 -8
  89. data/app/views/admin/admin_users/show.html+self.erb +26 -14
  90. data/app/views/admin/admin_users/show.html.erb +22 -20
  91. data/app/views/admin/credentials/_credentials.html+self.erb +8 -6
  92. data/app/views/admin/credentials/_credentials.html.erb +3 -1
  93. data/app/views/admin/credentials/create.turbo_stream.erb +4 -3
  94. data/app/views/admin/credentials/destroy.turbo_stream.erb +4 -2
  95. data/app/views/admin/credentials/new.html.erb +42 -36
  96. data/app/views/admin/dashboards/show.html.erb +13 -1
  97. data/app/views/admin/otps/_form.html.erb +7 -7
  98. data/app/views/admin/otps/create.turbo_stream.erb +3 -3
  99. data/app/views/admin/otps/new.html.erb +5 -3
  100. data/app/views/admin/sessions/new.html.erb +2 -3
  101. data/app/views/admin/sessions/otp.html.erb +1 -3
  102. data/app/views/admin/sessions/password.html.erb +1 -3
  103. data/app/views/admin/tokens/show.html.erb +4 -6
  104. data/app/views/admin/url_rewrites/_form.html.erb +9 -0
  105. data/app/views/admin/url_rewrites/edit.html.erb +13 -9
  106. data/app/views/admin/url_rewrites/index.html.erb +10 -7
  107. data/app/views/admin/url_rewrites/new.html.erb +8 -8
  108. data/app/views/admin/url_rewrites/show.html.erb +17 -12
  109. data/app/views/admin/well_knowns/_form.html.erb +9 -0
  110. data/app/views/admin/well_knowns/edit.html.erb +13 -9
  111. data/app/views/admin/well_knowns/index.html.erb +8 -5
  112. data/app/views/admin/well_knowns/new.html.erb +8 -8
  113. data/app/views/admin/well_knowns/show.html.erb +14 -13
  114. data/app/views/katalyst/content/asides/_aside.html+form.erb +6 -4
  115. data/app/views/katalyst/content/columns/_column.html+form.erb +5 -3
  116. data/app/views/katalyst/content/contents/_content.html+form.erb +8 -6
  117. data/app/views/katalyst/content/figures/_figure.html+form.erb +8 -5
  118. data/app/views/katalyst/content/groups/_group.html+form.erb +5 -3
  119. data/app/views/katalyst/content/items/_item.html+form.erb +5 -3
  120. data/app/views/katalyst/content/sections/_section.html+form.erb +5 -3
  121. data/app/views/katalyst/content/tables/_table.html+form.erb +16 -11
  122. data/app/views/katalyst/navigation/items/_button.html.erb +6 -12
  123. data/app/views/katalyst/navigation/items/_heading.html.erb +3 -10
  124. data/app/views/katalyst/navigation/items/_link.html.erb +6 -11
  125. data/app/views/katalyst/navigation/menus/edit.html.erb +10 -6
  126. data/app/views/katalyst/navigation/menus/index.html.erb +4 -2
  127. data/app/views/katalyst/navigation/menus/new.html.erb +5 -3
  128. data/app/views/katalyst/navigation/menus/show.html.erb +8 -7
  129. data/app/views/layouts/koi/_application_header.html.erb +20 -0
  130. data/app/views/layouts/koi/_application_navigation.html.erb +34 -0
  131. data/app/views/layouts/koi/_flash.html.erb +6 -3
  132. data/app/views/layouts/koi/_navigation_header.html.erb +0 -2
  133. data/app/views/layouts/koi/application.html.erb +22 -27
  134. data/app/views/layouts/koi/frame.html.erb +1 -3
  135. data/app/views/layouts/koi/login.html.erb +12 -5
  136. data/config/importmap.rb +1 -0
  137. data/config/locales/koi.en.yml +9 -1
  138. data/config/locales/pagy.en.yml +11 -0
  139. data/config/routes.rb +1 -1
  140. data/lib/generators/koi/admin/admin_generator.rb +4 -13
  141. data/lib/generators/koi/admin_controller/admin_controller_generator.rb +7 -17
  142. data/lib/generators/koi/admin_controller/templates/controller.rb.tt +82 -18
  143. data/lib/generators/koi/admin_controller/templates/controller_spec.rb.tt +113 -47
  144. data/lib/generators/koi/admin_route/admin_route_generator.rb +63 -10
  145. data/lib/generators/koi/admin_views/USAGE +18 -7
  146. data/lib/generators/koi/admin_views/admin_views_generator.rb +19 -11
  147. data/lib/generators/koi/admin_views/templates/_form.html.erb.tt +8 -0
  148. data/lib/generators/koi/admin_views/templates/archived.html.erb.tt +33 -0
  149. data/lib/generators/koi/admin_views/templates/edit.html.erb.tt +17 -9
  150. data/lib/generators/koi/admin_views/templates/index.html.erb.tt +31 -3
  151. data/lib/generators/koi/admin_views/templates/new.html.erb.tt +8 -8
  152. data/lib/generators/koi/admin_views/templates/show.html.erb.tt +15 -18
  153. data/lib/generators/koi/helpers/attribute_helpers.rb +147 -0
  154. data/lib/generators/koi/helpers/attribute_types.rb +218 -0
  155. data/lib/generators/koi/helpers/resource_helpers.rb +121 -0
  156. data/lib/generators/koi/{active_record/active_record_generator.rb → model/model_generator.rb} +1 -1
  157. data/lib/koi/config.rb +3 -1
  158. data/lib/koi/engine.rb +1 -15
  159. data/lib/koi/form/builder.rb +6 -6
  160. data/lib/koi/form/content.rb +55 -0
  161. data/lib/koi/form/elements/document.rb +1 -1
  162. data/lib/koi/form/elements/file_element.rb +1 -1
  163. data/lib/koi/form/elements/image.rb +1 -1
  164. data/lib/koi/form_builder.rb +1 -0
  165. data/lib/koi/menu.rb +14 -1
  166. data/lib/koi/middleware/admin_authentication.rb +4 -32
  167. data/spec/factories/admins.rb +1 -1
  168. metadata +93 -109
  169. data/app/assets/builds/katalyst/koi.esm.js +0 -518
  170. data/app/assets/builds/katalyst/koi.js +0 -518
  171. data/app/assets/builds/katalyst/koi.min.js +0 -2
  172. data/app/assets/builds/katalyst/koi.min.js.map +0 -1
  173. data/app/assets/builds/koi/admin.css +0 -1
  174. data/app/assets/stylesheets/koi/base/_button.scss +0 -122
  175. data/app/assets/stylesheets/koi/base/_icon.scss +0 -29
  176. data/app/assets/stylesheets/koi/base/_index.scss +0 -21
  177. data/app/assets/stylesheets/koi/base/_input.scss +0 -19
  178. data/app/assets/stylesheets/koi/base/_link.scss +0 -26
  179. data/app/assets/stylesheets/koi/base/_list.scss +0 -11
  180. data/app/assets/stylesheets/koi/base/_typography.scss +0 -160
  181. data/app/assets/stylesheets/koi/components/_actions-group.scss +0 -7
  182. data/app/assets/stylesheets/koi/components/_image-field.scss +0 -95
  183. data/app/assets/stylesheets/koi/components/_index.scss +0 -9
  184. data/app/assets/stylesheets/koi/components/_pagy.scss +0 -30
  185. data/app/assets/stylesheets/koi/components/_summary-list.scss +0 -40
  186. data/app/assets/stylesheets/koi/layouts/_banner.scss +0 -7
  187. data/app/assets/stylesheets/koi/layouts/_content.scss +0 -40
  188. data/app/assets/stylesheets/koi/layouts/_flash.scss +0 -41
  189. data/app/assets/stylesheets/koi/layouts/_header.scss +0 -61
  190. data/app/assets/stylesheets/koi/layouts/_index.scss +0 -48
  191. data/app/assets/stylesheets/koi/layouts/_main.scss +0 -23
  192. data/app/assets/stylesheets/koi/layouts/_navigation.scss +0 -180
  193. data/app/assets/stylesheets/koi/layouts/_stack.scss +0 -13
  194. data/app/assets/stylesheets/koi/pages/_index.scss +0 -1
  195. data/app/assets/stylesheets/koi/pages/_login.scss +0 -46
  196. data/app/assets/stylesheets/koi/themes/_govuk.scss +0 -59
  197. data/app/assets/stylesheets/koi/themes/_kpop.scss +0 -5
  198. data/app/assets/stylesheets/koi/utils/_breakpoints.scss +0 -13
  199. data/app/assets/stylesheets/koi/utils/_hide.scss +0 -11
  200. data/app/assets/stylesheets/koi/utils/_index.scss +0 -2
  201. data/app/assets/stylesheets/koi/utils/_typography.scss +0 -42
  202. data/app/components/koi/content/editor/item_form_component.html.erb +0 -11
  203. data/app/components/koi/content/editor/item_form_component.rb +0 -94
  204. data/app/components/koi/summary_list/attachment_component.rb +0 -47
  205. data/app/components/koi/summary_list/base.rb +0 -59
  206. data/app/components/koi/summary_list/boolean_component.rb +0 -15
  207. data/app/components/koi/summary_list/date_component.rb +0 -17
  208. data/app/components/koi/summary_list/datetime_component.rb +0 -17
  209. data/app/components/koi/summary_list/item_component.rb +0 -26
  210. data/app/components/koi/summary_list/number_component.rb +0 -21
  211. data/app/components/koi/summary_list/rich_text_component.rb +0 -8
  212. data/app/components/koi/summary_list/text_component.rb +0 -8
  213. data/app/components/koi/summary_list_component.html.erb +0 -5
  214. data/app/components/koi/summary_list_component.rb +0 -75
  215. data/app/controllers/concerns/koi/controller/is_admin_controller.rb +0 -77
  216. data/app/helpers/koi/application_helper.rb +0 -7
  217. data/app/helpers/koi/date_helper.rb +0 -26
  218. data/app/helpers/koi/definition_list_helper.rb +0 -10
  219. data/app/views/admin/admin_users/_fields.html+self.erb +0 -3
  220. data/app/views/admin/admin_users/_fields.html.erb +0 -3
  221. data/app/views/admin/url_rewrites/_fields.html.erb +0 -4
  222. data/app/views/admin/well_knowns/_fields.html.erb +0 -6
  223. data/app/views/layouts/koi/_environment.html.erb +0 -4
  224. data/app/views/layouts/koi/_header.html.erb +0 -11
  225. data/app/views/layouts/koi/_navigation.html.erb +0 -23
  226. data/app/views/layouts/koi/_navigation_collapse.html.erb +0 -3
  227. data/config/initializers/dartsass.rb +0 -10
  228. data/config/locales/pagy/en.yml +0 -24
  229. data/db/migrate/20260501000000_add_last_sign_out_at_to_admin_users.rb +0 -7
  230. data/lib/generators/koi/admin_views/templates/_fields.html.erb.tt +0 -3
  231. data/lib/generators/koi/helpers/admin_generator_attributes.rb +0 -66
  232. data/lib/koi/extensions/dartsass.rb +0 -23
  233. /data/app/assets/stylesheets/koi/{components/_clipboard.scss → blocks/clipboard.css} +0 -0
  234. /data/app/assets/stylesheets/koi/{components/_index-actions.scss → blocks/index-actions.css} +0 -0
  235. /data/app/assets/stylesheets/koi/{components/_toolbar.scss → blocks/toolbar.css} +0 -0
  236. /data/app/assets/stylesheets/koi/{base/_repel.scss → compositions/repel.css} +0 -0
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Admin
4
4
  class AdminUsersController < ApplicationController
5
- before_action :set_admin, only: %i[show edit update destroy]
5
+ before_action :set_admin_user, only: %i[show edit update destroy]
6
6
 
7
- attr_reader :admin
7
+ attr_reader :admin_user
8
8
 
9
9
  def index
10
10
  collection = Collection.new.with_params(params).apply(Admin::User.strict_loading)
@@ -19,65 +19,71 @@ module Admin
19
19
  end
20
20
 
21
21
  def show
22
- render :show, locals: { admin: }
22
+ render locals: { admin_user: }
23
23
  end
24
24
 
25
25
  def new
26
- @admin = Admin::User.new
26
+ @admin_user = Admin::User.new
27
27
 
28
- render :new, locals: { admin: }
28
+ render locals: { admin_user: }
29
29
  end
30
30
 
31
31
  def edit
32
- render :edit, locals: { admin: }
32
+ render :edit, locals: { admin_user: }
33
33
  end
34
34
 
35
35
  def create
36
- admin = Admin::User.new(admin_user_params)
36
+ admin_user = Admin::User.new(admin_user_params)
37
37
 
38
- if admin.save
39
- redirect_to admin_admin_user_path(admin)
38
+ if admin_user.save
39
+ redirect_to admin_admin_user_path(admin_user)
40
40
  else
41
- render :new, locals: { admin: }, status: :unprocessable_content
41
+ render :new, locals: { admin_user: }, status: :unprocessable_content
42
42
  end
43
43
  end
44
44
 
45
45
  def update
46
- if admin.update(admin_user_params)
46
+ if admin_user.update(admin_user_params)
47
47
  redirect_to action: :show
48
48
  else
49
- render :edit, locals: { admin: }, status: :unprocessable_content
49
+ render :edit, locals: { admin_user: }, status: :unprocessable_content
50
50
  end
51
51
  end
52
52
 
53
53
  def archive
54
54
  Admin::User.where(id: params[:id]).where.not(id: current_admin.id).each(&:archive!)
55
55
 
56
- redirect_back_or_to(admin_admin_users_path, status: :see_other)
56
+ redirect_back(fallback_location: admin_admin_users_path, status: :see_other)
57
57
  end
58
58
 
59
59
  def restore
60
60
  Admin::User.archived.where(id: params[:id]).each(&:restore!)
61
61
 
62
- redirect_back_or_to(admin_admin_users_path, status: :see_other)
62
+ redirect_back(fallback_location: admin_admin_users_path, status: :see_other)
63
63
  end
64
64
 
65
65
  def destroy
66
- admin.destroy
66
+ if admin_user.archived?
67
+ admin_user.destroy!
67
68
 
68
- redirect_to admin_admin_users_path
69
+ redirect_to admin_admin_users_path
70
+ else
71
+ admin_user.archive!
72
+
73
+ redirect_back_or_to(admin_admin_user_path(admin_user), status: :see_other)
74
+ end
69
75
  end
70
76
 
71
77
  private
72
78
 
73
- def set_admin
74
- @admin = Admin::User.with_archived.find(params.expect(:id))
79
+ def set_admin_user
80
+ @admin_user = Admin::User.with_archived.find(params[:id])
75
81
 
76
- request.variant << :self if @admin == current_admin_user
82
+ request.variant << :self if admin_user == current_admin_user
77
83
  end
78
84
 
79
85
  def admin_user_params
80
- params.expect(admin: %i[name email password archived])
86
+ params.expect(admin_user: %i[name email password archived])
81
87
  end
82
88
 
83
89
  class Collection < Admin::Collection
@@ -2,8 +2,6 @@
2
2
 
3
3
  module Admin
4
4
  class ApplicationController < ActionController::Base
5
- include Koi::Controller::IsAdminController
6
-
7
- protect_from_forgery with: :exception
5
+ include Koi::Controller
8
6
  end
9
7
  end
@@ -5,7 +5,7 @@ module Admin
5
5
  def destroy
6
6
  Rails.logger.warn("[CACHE CLEAR] - Cleaning entire cache manually by #{current_admin} request")
7
7
  Rails.cache.clear
8
- redirect_back_or_to(admin_dashboard_path)
8
+ redirect_back(fallback_location: admin_dashboard_path)
9
9
  end
10
10
  end
11
11
  end
@@ -6,25 +6,29 @@ module Admin
6
6
 
7
7
  before_action :set_admin_user
8
8
 
9
+ attr_reader :admin_user
10
+
9
11
  def new
10
- unless @admin_user.webauthn_id
11
- @admin_user.update!(webauthn_id: WebAuthn.generate_user_id)
12
+ unless admin_user.webauthn_id
13
+ admin_user.update!(webauthn_id: WebAuthn.generate_user_id)
12
14
  end
13
15
 
14
16
  options = webauthn_relying_party.options_for_registration(
15
17
  user: {
16
- id: @admin_user.webauthn_id,
17
- name: @admin_user.email,
18
- display_name: @admin_user.to_s,
18
+ id: admin_user.webauthn_id,
19
+ name: admin_user.email,
20
+ display_name: admin_user.name,
19
21
  },
20
- exclude: @admin_user.credentials.map(&:external_id),
22
+ exclude: admin_user.credentials.map(&:external_id),
21
23
  )
22
24
 
23
25
  # Store the newly generated challenge somewhere so you can have it
24
26
  # for the verification phase.
25
27
  session[:creation_challenge] = options.challenge
26
28
 
27
- render :new, locals: { admin: @admin_user, options: }
29
+ credential = admin_user.credentials.new
30
+
31
+ render locals: { admin_user:, credential:, options: }
28
32
  end
29
33
 
30
34
  def create
@@ -35,7 +39,7 @@ module Admin
35
39
  session.delete(:creation_challenge),
36
40
  )
37
41
 
38
- credential = @admin_user.credentials.find_or_initialize_by(
42
+ credential = admin_user.credentials.find_or_initialize_by(
39
43
  external_id: webauthn_credential.id,
40
44
  )
41
45
 
@@ -46,18 +50,18 @@ module Admin
46
50
  )
47
51
 
48
52
  respond_to do |format|
49
- format.html { redirect_to admin_admin_user_path(@admin_user), status: :see_other }
50
- format.turbo_stream { render locals: { admin: @admin_user } }
53
+ format.html { redirect_to admin_admin_user_path(admin_user), status: :see_other }
54
+ format.turbo_stream { render locals: { admin_user: } }
51
55
  end
52
56
  end
53
57
 
54
58
  def destroy
55
- credential = @admin_user.credentials.find(params.expect(:id))
59
+ credential = admin_user.credentials.find(params[:id])
56
60
  credential.destroy!
57
61
 
58
62
  respond_to do |format|
59
- format.html { redirect_to admin_admin_user_path(@admin_user), status: :see_other }
60
- format.turbo_stream { render locals: { admin: @admin_user } }
63
+ format.html { redirect_to admin_admin_user_path(admin_user), status: :see_other }
64
+ format.turbo_stream { render locals: { admin_user: } }
61
65
  end
62
66
  end
63
67
 
@@ -68,9 +72,9 @@ module Admin
68
72
  end
69
73
 
70
74
  def set_admin_user
71
- @admin_user = Admin::User.find(params.expect(:admin_user_id))
75
+ @admin_user = Admin::User.find(params[:admin_user_id])
72
76
 
73
- if current_admin == @admin_user
77
+ if current_admin == admin_user
74
78
  request.variant = :self
75
79
  else
76
80
  head(:forbidden)
@@ -4,45 +4,47 @@ module Admin
4
4
  class OtpsController < ApplicationController
5
5
  before_action :set_admin_user
6
6
 
7
+ attr_reader :admin_user
8
+
7
9
  def new
8
- @admin_user.otp_secret = ROTP::Base32.random
10
+ admin_user.otp_secret = ROTP::Base32.random
9
11
 
10
- render :new, locals: { admin: @admin_user }
12
+ render :new, locals: { admin_user: }
11
13
  end
12
14
 
13
15
  def create
14
- @admin_user.otp_secret = otp_params[:otp_secret]
16
+ admin_user.otp_secret = otp_params[:otp_secret]
15
17
 
16
- if @admin_user.otp.verify(otp_params[:token])
17
- @admin_user.save
18
+ if admin_user.otp.verify(otp_params[:token])
19
+ admin_user.save
18
20
 
19
- redirect_to admin_admin_user_path(@admin_user), status: :see_other
21
+ redirect_to admin_admin_user_path(admin_user), status: :see_other
20
22
  else
21
- @admin_user.errors.add(:token, :invalid)
23
+ admin_user.errors.add(:token, :invalid)
22
24
 
23
25
  respond_to do |format|
24
- format.html { redirect_to admin_admin_user_path(@admin_user), status: :see_other }
25
- format.turbo_stream { render locals: { admin: @admin_user } }
26
+ format.html { redirect_to admin_admin_user_path(admin_user), status: :see_other }
27
+ format.turbo_stream { render locals: { admin_user: }, status: :unprocessable_entity }
26
28
  end
27
29
  end
28
30
  end
29
31
 
30
32
  def destroy
31
- @admin_user.update!(otp_secret: nil)
33
+ admin_user.update!(otp_secret: nil)
32
34
 
33
- redirect_to admin_admin_user_path(@admin_user), status: :see_other
35
+ redirect_to admin_admin_user_path(admin_user), status: :see_other
34
36
  end
35
37
 
36
38
  private
37
39
 
38
40
  def otp_params
39
- params.expect(admin: %i[otp_secret token])
41
+ params.expect(admin_user: %i[otp_secret token])
40
42
  end
41
43
 
42
44
  def set_admin_user
43
- @admin_user = Admin::User.find(params.expect(:admin_user_id))
45
+ @admin_user = Admin::User.find(params[:admin_user_id])
44
46
 
45
- if current_admin == @admin_user
47
+ if current_admin == admin_user
46
48
  request.variant = :self
47
49
  else
48
50
  head(:forbidden)
@@ -6,7 +6,7 @@ module Admin
6
6
  include Koi::Controller::RecordsAuthentication
7
7
 
8
8
  before_action :redirect_authenticated, only: %i[new], if: :admin_signed_in?
9
- before_action :authenticate_local_admin, only: %i[new], if: :authenticate_local_admins?
9
+ before_action :authenticate_local_admin, only: %i[new], if: -> { Koi.config.authenticate_local_admins? }
10
10
 
11
11
  layout "koi/login"
12
12
 
@@ -35,7 +35,9 @@ module Admin
35
35
  end
36
36
 
37
37
  def destroy
38
- destroy_admin_session!(current_admin_user)
38
+ record_sign_out!(current_admin_user)
39
+
40
+ session[:admin_user_id] = nil
39
41
 
40
42
  redirect_to new_admin_session_path
41
43
  end
@@ -92,8 +94,21 @@ module Admin
92
94
  redirect_to(admin_dashboard_path, status: :see_other)
93
95
  end
94
96
 
97
+ def authenticate_local_admin
98
+ return if admin_signed_in? || !Rails.env.development?
99
+
100
+ session[:admin_user_id] =
101
+ Admin::User.where(email: %W[#{ENV.fetch('USER', nil)}@katalyst.com.au admin@katalyst.com.au]).first&.id
102
+
103
+ flash.delete(:redirect) if (redirect = flash[:redirect])
104
+
105
+ redirect_to(redirect || admin_dashboard_path, status: :see_other)
106
+ end
107
+
95
108
  def admin_sign_in(admin_user)
96
- create_admin_session!(admin_user)
109
+ record_sign_in!(admin_user)
110
+
111
+ session[:admin_user_id] = admin_user.id
97
112
 
98
113
  redirect_to(url_from(params[:redirect].presence) || admin_dashboard_path, status: :see_other)
99
114
  end
@@ -22,7 +22,9 @@ module Admin
22
22
 
23
23
  def update
24
24
  if (@admin_user = Admin::User.find_by_token_for(:password_reset, params[:token]))
25
- create_admin_session!(admin_user)
25
+ record_sign_in!(admin_user)
26
+
27
+ session[:admin_user_id] = admin_user.id
26
28
 
27
29
  redirect_to admin_admin_user_path(admin_user), status: :see_other, notice: t("koi.auth.token_consumed")
28
30
  else
@@ -33,7 +35,7 @@ module Admin
33
35
  private
34
36
 
35
37
  def set_admin_user
36
- @admin_user = Admin::User.find(params.expect(:admin_user_id))
38
+ @admin_user = Admin::User.find(params[:admin_user_id])
37
39
  end
38
40
  end
39
41
  end
@@ -4,68 +4,70 @@ module Admin
4
4
  class UrlRewritesController < ApplicationController
5
5
  before_action :set_url_rewrite, only: %i[show edit update destroy]
6
6
 
7
+ attr_reader :collection, :url_rewrite
8
+
7
9
  def index
8
- collection = Collection.new.with_params(params).apply(UrlRewrite.strict_loading)
10
+ @collection = Collection.new.with_params(params).apply(UrlRewrite.strict_loading.all)
9
11
 
10
12
  render locals: { collection: }
11
13
  end
12
14
 
13
15
  def show
14
- render :show, locals: { url_rewrite: @url_rewrite }
16
+ render locals: { url_rewrite: }
15
17
  end
16
18
 
17
19
  def new
18
20
  @url_rewrite = UrlRewrite.new
19
- render :new, locals: { url_rewrite: @url_rewrite }
21
+
22
+ render locals: { url_rewrite: }
20
23
  end
21
24
 
22
25
  def edit
23
- render :edit, locals: { url_rewrite: @url_rewrite }
26
+ render locals: { url_rewrite: }
24
27
  end
25
28
 
26
29
  def create
27
30
  @url_rewrite = UrlRewrite.new(url_rewrite_params)
28
31
 
29
32
  if @url_rewrite.save
30
- redirect_to admin_url_rewrite_path(@url_rewrite)
33
+ redirect_to admin_url_rewrite_path(url_rewrite), status: :see_other
31
34
  else
32
- render :new, status: :unprocessable_content, locals: { url_rewrite: @url_rewrite }
35
+ render :new, locals: { url_rewrite: }, status: :unprocessable_content
33
36
  end
34
37
  end
35
38
 
36
39
  def update
37
- @url_rewrite.attributes = url_rewrite_params
38
-
39
- if @url_rewrite.save
40
- redirect_to admin_url_rewrite_path(@url_rewrite)
40
+ if url_rewrite.update(url_rewrite_params)
41
+ redirect_to admin_url_rewrite_path(url_rewrite), status: :see_other
41
42
  else
42
- render :edit, status: :unprocessable_content, locals: { url_rewrite: @url_rewrite }
43
+ render :edit, locals: { url_rewrite: }, status: :unprocessable_content
43
44
  end
44
45
  end
45
46
 
46
47
  def destroy
47
48
  @url_rewrite.destroy!
48
49
 
49
- redirect_to admin_url_rewrites_path
50
+ redirect_to admin_url_rewrites_path, status: :see_other
50
51
  end
51
52
 
52
53
  private
53
54
 
54
- def url_rewrite_params
55
- params.expect(url_rewrite: %i[from to status_code active])
55
+ def set_url_rewrite
56
+ @url_rewrite = ::UrlRewrite.find(params[:id])
56
57
  end
57
58
 
58
- def set_url_rewrite
59
- @url_rewrite = UrlRewrite.find(params.expect(:id))
59
+ def url_rewrite_params
60
+ params.expect(url_rewrite: %i[from to active status_code])
60
61
  end
61
62
 
62
63
  class Collection < Admin::Collection
63
- config.sorting = "from"
64
+ config.sorting = :from
64
65
  config.paginate = true
65
66
 
66
67
  attribute :from, :string
67
68
  attribute :to, :string
68
69
  attribute :active, :boolean
70
+ attribute :status_code, :enum
69
71
  end
70
72
  end
71
73
  end
@@ -4,58 +4,60 @@ module Admin
4
4
  class WellKnownsController < ApplicationController
5
5
  before_action :set_well_known, only: %i[show edit update destroy]
6
6
 
7
+ attr_reader :collection, :well_known
8
+
7
9
  def index
8
- collection = Collection.new.with_params(params).apply(::WellKnown.strict_loading.all)
10
+ @collection = Collection.new.with_params(params).apply(WellKnown.strict_loading.all)
9
11
 
10
12
  render locals: { collection: }
11
13
  end
12
14
 
13
15
  def show
14
- render locals: { well_known: @well_known }
16
+ render locals: { well_known: }
15
17
  end
16
18
 
17
19
  def new
18
- render locals: { well_known: ::WellKnown.new }
20
+ @well_known = WellKnown.new
21
+
22
+ render locals: { well_known: }
19
23
  end
20
24
 
21
25
  def edit
22
- render locals: { well_known: @well_known }
26
+ render locals: { well_known: }
23
27
  end
24
28
 
25
29
  def create
26
- @well_known = ::WellKnown.new(well_known_params)
30
+ @well_known = WellKnown.new(well_known_params)
27
31
 
28
32
  if @well_known.save
29
- redirect_to [:admin, @well_known], status: :see_other
33
+ redirect_to admin_well_known_path(well_known), status: :see_other
30
34
  else
31
- render :new, locals: { well_known: @well_known }, status: :unprocessable_content
35
+ render :new, locals: { well_known: }, status: :unprocessable_content
32
36
  end
33
37
  end
34
38
 
35
39
  def update
36
- if @well_known.update(well_known_params)
37
- redirect_to action: :show, status: :see_other
40
+ if well_known.update(well_known_params)
41
+ redirect_to admin_well_known_path(well_known), status: :see_other
38
42
  else
39
- render :edit, locals: { well_known: @well_known }, status: :unprocessable_content
43
+ render :edit, locals: { well_known: }, status: :unprocessable_content
40
44
  end
41
45
  end
42
46
 
43
47
  def destroy
44
48
  @well_known.destroy!
45
49
 
46
- redirect_to action: :index, status: :see_other
50
+ redirect_to admin_well_knowns_path, status: :see_other
47
51
  end
48
52
 
49
53
  private
50
54
 
51
- # Only allow a list of trusted parameters through.
52
- def well_known_params
53
- params.expect(well_known: %i[name purpose content_type content])
55
+ def set_well_known
56
+ @well_known = ::WellKnown.find(params[:id])
54
57
  end
55
58
 
56
- # Use callbacks to share common setup or constraints between actions.
57
- def set_well_known
58
- @well_known = ::WellKnown.find(params.expect(:id))
59
+ def well_known_params
60
+ params.expect(well_known: %i[name purpose content_type content])
59
61
  end
60
62
 
61
63
  class Collection < Admin::Collection
@@ -64,7 +66,7 @@ module Admin
64
66
 
65
67
  attribute :name, :string
66
68
  attribute :purpose, :string
67
- attribute :content_type, :string
69
+ attribute :content_type, :enum
68
70
  attribute :content, :string
69
71
  end
70
72
  end
@@ -3,46 +3,33 @@
3
3
  module Koi
4
4
  module Controller
5
5
  module RecordsAuthentication
6
- def create_admin_session!(admin_user)
7
- sign_in_at = Time.current
6
+ def update_last_sign_in(admin_user)
7
+ return if admin_user.current_sign_in_at.blank?
8
8
 
9
+ admin_user.last_sign_in_at = admin_user.current_sign_in_at
10
+ admin_user.last_sign_in_ip = admin_user.current_sign_in_ip
11
+ end
12
+
13
+ def record_sign_in!(admin_user)
9
14
  update_last_sign_in(admin_user)
10
15
 
11
- admin_user.current_sign_in_at = sign_in_at
16
+ admin_user.current_sign_in_at = Time.current
12
17
  admin_user.current_sign_in_ip = request.remote_ip
13
- admin_user.sign_in_count += 1
18
+ admin_user.sign_in_count += 1
14
19
 
15
20
  admin_user.save!
16
-
17
- session[:admin_user_id] = admin_user.id
18
- session[:admin_user_signed_in_at] = sign_in_at.iso8601(6)
19
21
  end
20
22
 
21
- def destroy_admin_session!(admin_user)
22
- session[:admin_user_id] = nil
23
- session[:admin_user_signed_in_at] = nil
24
-
23
+ def record_sign_out!(admin_user)
25
24
  return unless admin_user
26
25
 
27
- sign_out_at = Time.current
28
-
29
26
  update_last_sign_in(admin_user)
30
27
 
31
- admin_user.last_sign_out_at = sign_out_at
32
28
  admin_user.current_sign_in_at = nil
33
29
  admin_user.current_sign_in_ip = nil
34
30
 
35
31
  admin_user.save!
36
32
  end
37
-
38
- private
39
-
40
- def update_last_sign_in(admin_user)
41
- return if admin_user.current_sign_in_at.blank?
42
-
43
- admin_user.last_sign_in_at = admin_user.current_sign_in_at
44
- admin_user.last_sign_in_ip = admin_user.current_sign_in_ip
45
- end
46
33
  end
47
34
  end
48
35
  end
@@ -5,12 +5,33 @@ module Koi
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- include Koi::Controller::IsAdminController
8
+ include HasAdminUsers
9
+ include HasAttachments
10
+ include Katalyst::Tables::Backend
11
+ include ::Pagy::Backend
9
12
 
10
- authenticate_local_admins Rails.env.development?
13
+ default_form_builder "Koi::FormBuilder"
14
+ default_table_component "Koi::TableComponent"
15
+ default_table_query_component "Koi::TableQueryComponent"
16
+ default_summary_table_component "Koi::SummaryTableComponent"
11
17
 
12
- helper Koi::ApplicationHelper
13
- helper Koi::DefinitionListHelper
18
+ # Dependency helpers
19
+ helper Katalyst::GOVUK::Formbuilder::Frontend
20
+ helper Katalyst::Navigation::FrontendHelper
21
+ helper Katalyst::Tables::Frontend
22
+ helper ::Pagy::Frontend
23
+
24
+ # Koi Helpers
25
+ helper FormHelper
26
+ helper HeaderHelper
27
+ helper Pagy::Frontend
28
+
29
+ # @deprecated to be removed in Koi 5
30
+ helper IndexActionsHelper
31
+
32
+ layout -> { turbo_frame_request? ? "koi/frame" : "koi/application" }
33
+
34
+ protect_from_forgery with: :exception
14
35
  end
15
36
  end
16
37
  end
@@ -10,6 +10,6 @@ class WellKnownsController < ApplicationController
10
10
  private
11
11
 
12
12
  def set_well_known
13
- @well_known = WellKnown.find_by!(name: params.expect(:name))
13
+ @well_known = WellKnown.find_by!(name: params[:name])
14
14
  end
15
15
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Koi
4
+ module FormHelper
5
+ # Ensure that admin forms use `admin_` path helpers.
6
+ def form_with(model: false, url: nil, format: nil, **, &)
7
+ if model && (url != false)
8
+ url ||= if format.nil?
9
+ polymorphic_path([:admin, model], {})
10
+ else
11
+ polymorphic_path([:admin, model], format: format)
12
+ end
13
+ end
14
+
15
+ super
16
+ end
17
+ end
18
+ end