katalyst-koi 4.18.0 → 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.
- checksums.yaml +4 -4
- data/app/assets/images/koi/icons/add.svg +3 -0
- data/app/assets/images/koi/icons/close.svg +1 -0
- data/app/assets/images/koi/koi.png +0 -0
- data/app/assets/javascripts/koi/controllers/file_field_controller.js +2 -2
- data/app/assets/javascripts/koi/controllers/index.js +0 -3
- data/app/assets/javascripts/koi/controllers/koi/modal_controller.js +40 -0
- data/app/assets/javascripts/koi/controllers/navigation_controller.js +14 -21
- data/app/assets/javascripts/koi/controllers/webauthn_registration_controller.js +4 -1
- data/app/assets/stylesheets/koi/blocks/actions.css +8 -0
- data/app/assets/stylesheets/koi/blocks/application-header.css +15 -0
- data/app/assets/stylesheets/koi/blocks/application-navigation.css +54 -0
- data/app/assets/stylesheets/koi/blocks/button.css +90 -0
- data/app/assets/stylesheets/koi/blocks/flash.css +19 -0
- data/app/assets/stylesheets/koi/blocks/icon.css +15 -0
- data/app/assets/stylesheets/koi/blocks/index.css +13 -0
- data/app/assets/stylesheets/koi/blocks/modal.css +26 -0
- data/app/assets/stylesheets/koi/blocks/navigation.css +23 -0
- data/app/assets/stylesheets/koi/blocks/page-header.css +31 -0
- data/app/assets/stylesheets/koi/blocks/pagy.css +82 -0
- data/app/assets/stylesheets/koi/blocks/prose.css +37 -0
- data/app/assets/stylesheets/koi/blocks/tables/index.css +4 -0
- data/app/assets/stylesheets/koi/{components/_query.scss → blocks/tables/query.css} +13 -13
- data/app/assets/stylesheets/koi/{base/_tables.scss → blocks/tables/table.css} +11 -59
- data/app/assets/stylesheets/koi/compositions/cover.css +17 -0
- data/app/assets/stylesheets/koi/{base/_flow.scss → compositions/flow.css} +1 -1
- data/app/assets/stylesheets/koi/compositions/index.css +4 -0
- data/app/assets/stylesheets/koi/compositions/wrapper.css +11 -0
- data/app/assets/stylesheets/koi/forms/caption.css +22 -0
- data/app/assets/stylesheets/koi/forms/checkboxes.css +153 -0
- data/app/assets/stylesheets/koi/forms/date-input.css +12 -0
- data/app/assets/stylesheets/koi/{components/_document-field.scss → forms/document-field.css} +20 -15
- data/app/assets/stylesheets/koi/forms/errors.css +38 -0
- data/app/assets/stylesheets/koi/forms/fieldset.css +73 -0
- data/app/assets/stylesheets/koi/forms/file-upload.css +20 -0
- data/app/assets/stylesheets/koi/forms/form-group.css +19 -0
- data/app/assets/stylesheets/koi/forms/hint.css +11 -0
- data/app/assets/stylesheets/koi/forms/image-field.css +96 -0
- data/app/assets/stylesheets/koi/forms/index.css +44 -0
- data/app/assets/stylesheets/koi/forms/input.css +194 -0
- data/app/assets/stylesheets/koi/forms/label.css +43 -0
- data/app/assets/stylesheets/koi/forms/password.css +18 -0
- data/app/assets/stylesheets/koi/forms/radios.css +162 -0
- data/app/assets/stylesheets/koi/forms/select.css +18 -0
- data/app/assets/stylesheets/koi/forms/textarea.css +3 -0
- data/app/assets/stylesheets/koi/forms/trix.css +33 -0
- data/app/assets/stylesheets/koi/global/fonts.css +22 -0
- data/app/assets/stylesheets/koi/global/global-styles.css +297 -0
- data/app/assets/stylesheets/koi/global/reset.css +98 -0
- data/app/assets/stylesheets/koi/global/variables.css +97 -0
- data/app/assets/stylesheets/koi/icons.css +14 -0
- data/app/assets/stylesheets/koi/{admin.scss → index.css} +16 -7
- data/app/assets/stylesheets/koi/login.css +26 -0
- data/app/assets/stylesheets/koi/themes/_index.scss +0 -1
- data/app/assets/stylesheets/koi/utilities/index.css +1 -0
- data/app/assets/stylesheets/koi/utilities/visually-hidden.css +18 -0
- data/app/components/concerns/koi/tables/cells.rb +3 -3
- data/app/components/koi/header/edit_component.rb +1 -1
- data/app/components/koi/header/index_component.rb +1 -1
- data/app/components/koi/header/new_component.rb +1 -1
- data/app/components/koi/header/show_component.rb +1 -1
- data/app/components/koi/header_component.html.erb +12 -11
- data/app/components/koi/header_component.rb +4 -2
- data/app/components/koi/table_component.rb +8 -0
- data/app/controllers/admin/admin_users_controller.rb +24 -18
- data/app/controllers/admin/application_controller.rb +1 -3
- data/app/controllers/admin/credentials_controller.rb +18 -14
- data/app/controllers/admin/otps_controller.rb +15 -13
- data/app/controllers/admin/sessions_controller.rb +12 -1
- data/app/controllers/admin/url_rewrites_controller.rb +19 -17
- data/app/controllers/admin/well_knowns_controller.rb +20 -18
- data/app/controllers/concerns/koi/controller.rb +37 -0
- data/app/helpers/koi/form_helper.rb +18 -0
- data/app/helpers/koi/header_helper.rb +122 -0
- data/app/helpers/koi/index_actions_helper.rb +3 -2
- data/app/helpers/koi/modal_helper.rb +71 -0
- data/app/models/admin/user.rb +7 -1
- data/app/models/url_rewrite.rb +1 -9
- data/app/views/admin/admin_users/_form.html+self.erb +8 -0
- data/app/views/admin/admin_users/_form.html.erb +8 -0
- data/app/views/admin/admin_users/archived.html.erb +7 -4
- data/app/views/admin/admin_users/edit.html+self.erb +12 -0
- data/app/views/admin/admin_users/edit.html.erb +13 -8
- data/app/views/admin/admin_users/index.html.erb +10 -5
- data/app/views/admin/admin_users/new.html.erb +8 -8
- data/app/views/admin/admin_users/show.html+self.erb +26 -14
- data/app/views/admin/admin_users/show.html.erb +22 -20
- data/app/views/admin/credentials/_credentials.html+self.erb +8 -6
- data/app/views/admin/credentials/_credentials.html.erb +3 -1
- data/app/views/admin/credentials/create.turbo_stream.erb +4 -3
- data/app/views/admin/credentials/destroy.turbo_stream.erb +4 -2
- data/app/views/admin/credentials/new.html.erb +42 -36
- data/app/views/admin/dashboards/show.html.erb +13 -1
- data/app/views/admin/otps/_form.html.erb +7 -7
- data/app/views/admin/otps/create.turbo_stream.erb +3 -3
- data/app/views/admin/otps/new.html.erb +5 -3
- data/app/views/admin/sessions/new.html.erb +2 -3
- data/app/views/admin/sessions/otp.html.erb +1 -3
- data/app/views/admin/sessions/password.html.erb +1 -3
- data/app/views/admin/tokens/show.html.erb +4 -6
- data/app/views/admin/url_rewrites/_form.html.erb +9 -0
- data/app/views/admin/url_rewrites/edit.html.erb +13 -9
- data/app/views/admin/url_rewrites/index.html.erb +10 -7
- data/app/views/admin/url_rewrites/new.html.erb +8 -8
- data/app/views/admin/url_rewrites/show.html.erb +17 -12
- data/app/views/admin/well_knowns/_form.html.erb +9 -0
- data/app/views/admin/well_knowns/edit.html.erb +13 -9
- data/app/views/admin/well_knowns/index.html.erb +8 -5
- data/app/views/admin/well_knowns/new.html.erb +8 -8
- data/app/views/admin/well_knowns/show.html.erb +14 -13
- data/app/views/katalyst/content/asides/_aside.html+form.erb +6 -4
- data/app/views/katalyst/content/columns/_column.html+form.erb +5 -3
- data/app/views/katalyst/content/contents/_content.html+form.erb +8 -6
- data/app/views/katalyst/content/figures/_figure.html+form.erb +8 -5
- data/app/views/katalyst/content/groups/_group.html+form.erb +5 -3
- data/app/views/katalyst/content/items/_item.html+form.erb +5 -3
- data/app/views/katalyst/content/sections/_section.html+form.erb +5 -3
- data/app/views/katalyst/content/tables/_table.html+form.erb +16 -11
- data/app/views/katalyst/navigation/items/_button.html.erb +6 -12
- data/app/views/katalyst/navigation/items/_heading.html.erb +3 -10
- data/app/views/katalyst/navigation/items/_link.html.erb +6 -11
- data/app/views/katalyst/navigation/menus/edit.html.erb +10 -6
- data/app/views/katalyst/navigation/menus/index.html.erb +4 -2
- data/app/views/katalyst/navigation/menus/new.html.erb +5 -3
- data/app/views/katalyst/navigation/menus/show.html.erb +8 -7
- data/app/views/layouts/koi/_application_header.html.erb +20 -0
- data/app/views/layouts/koi/_application_navigation.html.erb +34 -0
- data/app/views/layouts/koi/_flash.html.erb +6 -3
- data/app/views/layouts/koi/_navigation_header.html.erb +0 -2
- data/app/views/layouts/koi/application.html.erb +22 -27
- data/app/views/layouts/koi/frame.html.erb +1 -3
- data/app/views/layouts/koi/login.html.erb +12 -5
- data/config/locales/koi.en.yml +9 -1
- data/config/routes.rb +1 -1
- data/lib/generators/koi/admin/admin_generator.rb +3 -12
- data/lib/generators/koi/admin_controller/admin_controller_generator.rb +6 -16
- data/lib/generators/koi/admin_controller/templates/controller.rb.tt +82 -18
- data/lib/generators/koi/admin_controller/templates/controller_spec.rb.tt +113 -47
- data/lib/generators/koi/admin_route/admin_route_generator.rb +60 -6
- data/lib/generators/koi/admin_views/USAGE +18 -7
- data/lib/generators/koi/admin_views/admin_views_generator.rb +19 -11
- data/lib/generators/koi/admin_views/templates/_form.html.erb.tt +8 -0
- data/lib/generators/koi/admin_views/templates/archived.html.erb.tt +33 -0
- data/lib/generators/koi/admin_views/templates/edit.html.erb.tt +17 -9
- data/lib/generators/koi/admin_views/templates/index.html.erb.tt +31 -3
- data/lib/generators/koi/admin_views/templates/new.html.erb.tt +8 -8
- data/lib/generators/koi/admin_views/templates/show.html.erb.tt +15 -18
- data/lib/generators/koi/helpers/attribute_helpers.rb +147 -0
- data/lib/generators/koi/helpers/attribute_types.rb +218 -0
- data/lib/generators/koi/helpers/resource_helpers.rb +121 -0
- data/lib/generators/koi/{active_record/active_record_generator.rb → model/model_generator.rb} +1 -1
- data/lib/koi/config.rb +3 -1
- data/lib/koi/engine.rb +0 -9
- data/lib/koi/form/builder.rb +4 -4
- data/lib/koi/form/content.rb +55 -0
- data/lib/koi/form/elements/document.rb +1 -1
- data/lib/koi/form/elements/image.rb +1 -1
- data/lib/koi/form_builder.rb +1 -0
- data/lib/koi/menu.rb +14 -1
- data/spec/factories/admins.rb +1 -1
- metadata +92 -101
- data/app/assets/builds/koi/admin.css +0 -1
- data/app/assets/stylesheets/koi/base/_button.scss +0 -122
- data/app/assets/stylesheets/koi/base/_icon.scss +0 -29
- data/app/assets/stylesheets/koi/base/_index.scss +0 -21
- data/app/assets/stylesheets/koi/base/_input.scss +0 -19
- data/app/assets/stylesheets/koi/base/_link.scss +0 -26
- data/app/assets/stylesheets/koi/base/_list.scss +0 -11
- data/app/assets/stylesheets/koi/base/_typography.scss +0 -160
- data/app/assets/stylesheets/koi/components/_actions-group.scss +0 -7
- data/app/assets/stylesheets/koi/components/_image-field.scss +0 -95
- data/app/assets/stylesheets/koi/components/_index.scss +0 -9
- data/app/assets/stylesheets/koi/components/_pagy.scss +0 -29
- data/app/assets/stylesheets/koi/components/_summary-list.scss +0 -40
- data/app/assets/stylesheets/koi/layouts/_banner.scss +0 -7
- data/app/assets/stylesheets/koi/layouts/_content.scss +0 -40
- data/app/assets/stylesheets/koi/layouts/_flash.scss +0 -41
- data/app/assets/stylesheets/koi/layouts/_header.scss +0 -61
- data/app/assets/stylesheets/koi/layouts/_index.scss +0 -48
- data/app/assets/stylesheets/koi/layouts/_main.scss +0 -23
- data/app/assets/stylesheets/koi/layouts/_navigation.scss +0 -180
- data/app/assets/stylesheets/koi/layouts/_stack.scss +0 -13
- data/app/assets/stylesheets/koi/pages/_index.scss +0 -1
- data/app/assets/stylesheets/koi/pages/_login.scss +0 -46
- data/app/assets/stylesheets/koi/themes/_govuk.scss +0 -56
- data/app/assets/stylesheets/koi/themes/_kpop.scss +0 -5
- data/app/assets/stylesheets/koi/utils/_breakpoints.scss +0 -13
- data/app/assets/stylesheets/koi/utils/_hide.scss +0 -11
- data/app/assets/stylesheets/koi/utils/_index.scss +0 -2
- data/app/assets/stylesheets/koi/utils/_typography.scss +0 -42
- data/app/components/koi/content/editor/item_form_component.html.erb +0 -11
- data/app/components/koi/content/editor/item_form_component.rb +0 -94
- data/app/components/koi/summary_list/attachment_component.rb +0 -47
- data/app/components/koi/summary_list/base.rb +0 -59
- data/app/components/koi/summary_list/boolean_component.rb +0 -15
- data/app/components/koi/summary_list/date_component.rb +0 -17
- data/app/components/koi/summary_list/datetime_component.rb +0 -17
- data/app/components/koi/summary_list/item_component.rb +0 -26
- data/app/components/koi/summary_list/number_component.rb +0 -21
- data/app/components/koi/summary_list/rich_text_component.rb +0 -8
- data/app/components/koi/summary_list/text_component.rb +0 -8
- data/app/components/koi/summary_list_component.html.erb +0 -5
- data/app/components/koi/summary_list_component.rb +0 -75
- data/app/controllers/concerns/koi/controller/is_admin_controller.rb +0 -66
- data/app/helpers/koi/application_helper.rb +0 -7
- data/app/helpers/koi/date_helper.rb +0 -26
- data/app/helpers/koi/definition_list_helper.rb +0 -10
- data/app/views/admin/admin_users/_fields.html+self.erb +0 -3
- data/app/views/admin/admin_users/_fields.html.erb +0 -3
- data/app/views/admin/url_rewrites/_fields.html.erb +0 -4
- data/app/views/admin/well_knowns/_fields.html.erb +0 -6
- data/app/views/layouts/koi/_environment.html.erb +0 -4
- data/app/views/layouts/koi/_header.html.erb +0 -11
- data/app/views/layouts/koi/_navigation.html.erb +0 -23
- data/app/views/layouts/koi/_navigation_collapse.html.erb +0 -3
- data/lib/generators/koi/admin_views/templates/_fields.html.erb.tt +0 -3
- data/lib/generators/koi/helpers/admin_generator_attributes.rb +0 -66
- data/lib/koi/extensions/dartsass.rb +0 -23
- /data/app/assets/stylesheets/koi/{components/_clipboard.scss → blocks/clipboard.css} +0 -0
- /data/app/assets/stylesheets/koi/{components/_index-actions.scss → blocks/index-actions.css} +0 -0
- /data/app/assets/stylesheets/koi/{components/_toolbar.scss → blocks/toolbar.css} +0 -0
- /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 :
|
5
|
+
before_action :set_admin_user, only: %i[show edit update destroy]
|
6
6
|
|
7
|
-
attr_reader :
|
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,34 +19,34 @@ module Admin
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def show
|
22
|
-
render
|
22
|
+
render locals: { admin_user: }
|
23
23
|
end
|
24
24
|
|
25
25
|
def new
|
26
|
-
@
|
26
|
+
@admin_user = Admin::User.new
|
27
27
|
|
28
|
-
render
|
28
|
+
render locals: { admin_user: }
|
29
29
|
end
|
30
30
|
|
31
31
|
def edit
|
32
|
-
render :edit, locals: {
|
32
|
+
render :edit, locals: { admin_user: }
|
33
33
|
end
|
34
34
|
|
35
35
|
def create
|
36
|
-
|
36
|
+
admin_user = Admin::User.new(admin_user_params)
|
37
37
|
|
38
|
-
if
|
39
|
-
redirect_to admin_admin_user_path(
|
38
|
+
if admin_user.save
|
39
|
+
redirect_to admin_admin_user_path(admin_user)
|
40
40
|
else
|
41
|
-
render :new, locals: {
|
41
|
+
render :new, locals: { admin_user: }, status: :unprocessable_content
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
45
|
def update
|
46
|
-
if
|
46
|
+
if admin_user.update(admin_user_params)
|
47
47
|
redirect_to action: :show
|
48
48
|
else
|
49
|
-
render :edit, locals: {
|
49
|
+
render :edit, locals: { admin_user: }, status: :unprocessable_content
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -63,21 +63,27 @@ module Admin
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def destroy
|
66
|
-
|
66
|
+
if admin_user.archived?
|
67
|
+
admin_user.destroy!
|
67
68
|
|
68
|
-
|
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
|
74
|
-
@
|
79
|
+
def set_admin_user
|
80
|
+
@admin_user = Admin::User.with_archived.find(params[:id])
|
75
81
|
|
76
|
-
request.variant << :self if
|
82
|
+
request.variant << :self if admin_user == current_admin_user
|
77
83
|
end
|
78
84
|
|
79
85
|
def admin_user_params
|
80
|
-
params.expect(
|
86
|
+
params.expect(admin_user: %i[name email password archived])
|
81
87
|
end
|
82
88
|
|
83
89
|
class Collection < Admin::Collection
|
@@ -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
|
11
|
-
|
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:
|
17
|
-
name:
|
18
|
-
display_name:
|
18
|
+
id: admin_user.webauthn_id,
|
19
|
+
name: admin_user.email,
|
20
|
+
display_name: admin_user.name,
|
19
21
|
},
|
20
|
-
exclude:
|
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
|
-
|
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 =
|
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(
|
50
|
-
format.turbo_stream { render locals: {
|
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 =
|
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(
|
60
|
-
format.turbo_stream { render locals: {
|
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
|
|
@@ -70,7 +74,7 @@ module Admin
|
|
70
74
|
def set_admin_user
|
71
75
|
@admin_user = Admin::User.find(params[:admin_user_id])
|
72
76
|
|
73
|
-
if current_admin ==
|
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
|
-
|
10
|
+
admin_user.otp_secret = ROTP::Base32.random
|
9
11
|
|
10
|
-
render :new, locals: {
|
12
|
+
render :new, locals: { admin_user: }
|
11
13
|
end
|
12
14
|
|
13
15
|
def create
|
14
|
-
|
16
|
+
admin_user.otp_secret = otp_params[:otp_secret]
|
15
17
|
|
16
|
-
if
|
17
|
-
|
18
|
+
if admin_user.otp.verify(otp_params[:token])
|
19
|
+
admin_user.save
|
18
20
|
|
19
|
-
redirect_to admin_admin_user_path(
|
21
|
+
redirect_to admin_admin_user_path(admin_user), status: :see_other
|
20
22
|
else
|
21
|
-
|
23
|
+
admin_user.errors.add(:token, :invalid)
|
22
24
|
|
23
25
|
respond_to do |format|
|
24
|
-
format.html { redirect_to admin_admin_user_path(
|
25
|
-
format.turbo_stream { render locals: {
|
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
|
-
|
33
|
+
admin_user.update!(otp_secret: nil)
|
32
34
|
|
33
|
-
redirect_to admin_admin_user_path(
|
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(
|
41
|
+
params.expect(admin_user: %i[otp_secret token])
|
40
42
|
end
|
41
43
|
|
42
44
|
def set_admin_user
|
43
45
|
@admin_user = Admin::User.find(params[:admin_user_id])
|
44
46
|
|
45
|
-
if current_admin ==
|
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:
|
9
|
+
before_action :authenticate_local_admin, only: %i[new], if: -> { Koi.config.authenticate_local_admins? }
|
10
10
|
|
11
11
|
layout "koi/login"
|
12
12
|
|
@@ -94,6 +94,17 @@ module Admin
|
|
94
94
|
redirect_to(admin_dashboard_path, status: :see_other)
|
95
95
|
end
|
96
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
|
+
|
97
108
|
def admin_sign_in(admin_user)
|
98
109
|
record_sign_in!(admin_user)
|
99
110
|
|
@@ -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
|
16
|
+
render locals: { url_rewrite: }
|
15
17
|
end
|
16
18
|
|
17
19
|
def new
|
18
20
|
@url_rewrite = UrlRewrite.new
|
19
|
-
|
21
|
+
|
22
|
+
render locals: { url_rewrite: }
|
20
23
|
end
|
21
24
|
|
22
25
|
def edit
|
23
|
-
render
|
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(
|
33
|
+
redirect_to admin_url_rewrite_path(url_rewrite), status: :see_other
|
31
34
|
else
|
32
|
-
render :new,
|
35
|
+
render :new, locals: { url_rewrite: }, status: :unprocessable_content
|
33
36
|
end
|
34
37
|
end
|
35
38
|
|
36
39
|
def update
|
37
|
-
|
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,
|
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
|
55
|
-
|
55
|
+
def set_url_rewrite
|
56
|
+
@url_rewrite = ::UrlRewrite.find(params[:id])
|
56
57
|
end
|
57
58
|
|
58
|
-
def
|
59
|
-
|
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 =
|
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,67 +4,69 @@ 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(
|
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:
|
16
|
+
render locals: { well_known: }
|
15
17
|
end
|
16
18
|
|
17
19
|
def new
|
18
|
-
|
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:
|
26
|
+
render locals: { well_known: }
|
23
27
|
end
|
24
28
|
|
25
29
|
def create
|
26
|
-
@well_known =
|
30
|
+
@well_known = WellKnown.new(well_known_params)
|
27
31
|
|
28
32
|
if @well_known.save
|
29
|
-
redirect_to
|
33
|
+
redirect_to admin_well_known_path(well_known), status: :see_other
|
30
34
|
else
|
31
|
-
render :new, locals: { well_known:
|
35
|
+
render :new, locals: { well_known: }, status: :unprocessable_content
|
32
36
|
end
|
33
37
|
end
|
34
38
|
|
35
39
|
def update
|
36
|
-
if
|
37
|
-
redirect_to
|
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:
|
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
|
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])
|
54
|
-
end
|
55
|
-
|
56
|
-
# Use callbacks to share common setup or constraints between actions.
|
57
55
|
def set_well_known
|
58
56
|
@well_known = ::WellKnown.find(params[:id])
|
59
57
|
end
|
60
58
|
|
59
|
+
def well_known_params
|
60
|
+
params.expect(well_known: %i[name purpose content_type content])
|
61
|
+
end
|
62
|
+
|
61
63
|
class Collection < Admin::Collection
|
62
64
|
config.sorting = :name
|
63
65
|
config.paginate = true
|
64
66
|
|
65
67
|
attribute :name, :string
|
66
68
|
attribute :purpose, :string
|
67
|
-
attribute :content_type, :
|
69
|
+
attribute :content_type, :enum
|
68
70
|
attribute :content, :string
|
69
71
|
end
|
70
72
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Koi
|
4
|
+
module Controller
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
include HasAdminUsers
|
9
|
+
include HasAttachments
|
10
|
+
include Katalyst::Tables::Backend
|
11
|
+
include ::Pagy::Backend
|
12
|
+
|
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"
|
17
|
+
|
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
|
35
|
+
end
|
36
|
+
end
|
37
|
+
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
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Koi
|
4
|
+
module HeaderHelper
|
5
|
+
# This helper generates an accessible breadcrumb navigation structure with
|
6
|
+
# proper ARIA attributes for screen readers. The breadcrumb items should be
|
7
|
+
# provided as content within the block.
|
8
|
+
#
|
9
|
+
# @param ** Additional HTML attributes to merge with the default breadcrumb attributes
|
10
|
+
# @yield [Block] The block should contain the breadcrumb items (typically `<li>` elements)
|
11
|
+
# @return [String, nil] Returns the HTML `<nav>` element with breadcrumbs if content is present, nil otherwise
|
12
|
+
#
|
13
|
+
# @example Basic usage with list items
|
14
|
+
# <%= breadcrumb_list do %>
|
15
|
+
# <li><%= link_to "Home", root_path %></li>
|
16
|
+
# <li><%= link_to "Products", products_path %></li>
|
17
|
+
# <% end %>
|
18
|
+
#
|
19
|
+
# @see https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/ ARIA breadcrumb pattern
|
20
|
+
def breadcrumb_list(**, &)
|
21
|
+
return if (content = capture(&)).blank?
|
22
|
+
|
23
|
+
tag.nav(**_koi_breadcrumbs_attributes(**)) do
|
24
|
+
tag.ol(content, class: "breadcrumbs-list", role: "list")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# This helper generates an accessible actions navigation structure with
|
29
|
+
# proper ARIA attributes for screen readers. The action items should be
|
30
|
+
# provided as content within the block.
|
31
|
+
#
|
32
|
+
# @param ** Additional HTML attributes to merge with the default actions attributes
|
33
|
+
# @yield [Block] The block should contain the action items (typically `<li>` elements)
|
34
|
+
# @return [String, nil] Returns the HTML `<nav>` element with actions if content is present, nil otherwise
|
35
|
+
#
|
36
|
+
# @example Basic usage with CRUD and contextual actions
|
37
|
+
# <%= actions_list do %>
|
38
|
+
# <li><%= link_to "Edit", edit_article_path(article) %></li>
|
39
|
+
# <li><%= link_to_archive_or_delete(article) %></li>
|
40
|
+
# <li><%= link_to "Config", articles_config_path(article) %></li>
|
41
|
+
# <% end %>
|
42
|
+
#
|
43
|
+
# @see https://www.w3.org/WAI/ARIA/apg/practices/names-and-descriptions/ ARIA naming and descriptions
|
44
|
+
def actions_list(**, &)
|
45
|
+
return if (content = capture(&)).blank?
|
46
|
+
|
47
|
+
tag.nav(**_koi_actions_attributes(**)) do
|
48
|
+
tag.ul(content, class: "actions-list", role: "list")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Creates a delete link with confirmation.
|
53
|
+
#
|
54
|
+
# @param model [ActiveRecord::Base] The record to create a delete link for
|
55
|
+
# @param text [String] Text to display for delete link (default: "Delete")
|
56
|
+
# @param confirm [String] Confirmation message for delete action (default: "Are you sure?")
|
57
|
+
# @param url [String] Target url for delete actions (defaults to admin_<record>_path)
|
58
|
+
# @param ** Additional HTML attributes to pass to the link
|
59
|
+
# @return [String, nil] Returns the HTML link element, or nil if record is not persisted
|
60
|
+
#
|
61
|
+
# @example Basic usage
|
62
|
+
# <%= link_to_delete(user) %>
|
63
|
+
def link_to_delete(model,
|
64
|
+
text = "Delete",
|
65
|
+
confirm: "Are you sure?",
|
66
|
+
url: polymorphic_path([:admin, model]),
|
67
|
+
**)
|
68
|
+
return unless model.persisted?
|
69
|
+
|
70
|
+
link_to(text, url, data: { turbo_method: :delete, turbo_confirm: confirm }, **)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Conditionally creates an archive or delete link based on the record's status.
|
74
|
+
#
|
75
|
+
# @param model [ActiveRecord::Base] The record to create an archive/delete link for
|
76
|
+
# @param archive_text [String] Text to display for archive link (default: "Archive")
|
77
|
+
# @param delete_text [String] Text to display for delete link (default: "Delete")
|
78
|
+
# @param confirm [String] Confirmation message for delete action (default: "Are you sure?")
|
79
|
+
# @param url [String] Target url for delete actions (defaults to admin_<record>_path)
|
80
|
+
# @param ** Additional HTML attributes to pass to the link
|
81
|
+
# @return [String, nil] Returns the HTML link element, or nil if record is not persisted
|
82
|
+
#
|
83
|
+
# @example Basic usage
|
84
|
+
# <%= link_to_archive_or_delete(user) %>
|
85
|
+
#
|
86
|
+
# @see Koi::Model::Archivable For more information about the archivable concern
|
87
|
+
def link_to_archive_or_delete(model,
|
88
|
+
archive_text: "Archive",
|
89
|
+
delete_text: "Delete",
|
90
|
+
confirm: "Are you sure?",
|
91
|
+
url: polymorphic_path([:admin, model]),
|
92
|
+
**)
|
93
|
+
raise ArgumentError unless model.respond_to?(:archived?)
|
94
|
+
|
95
|
+
return unless model.persisted?
|
96
|
+
|
97
|
+
if model.archived?
|
98
|
+
link_to(delete_text, url, data: { turbo_method: :delete, turbo_confirm: confirm }, **)
|
99
|
+
else
|
100
|
+
link_to(archive_text, url, data: { turbo_method: :delete }, **)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
using Katalyst::HtmlAttributes::HasHtmlAttributes
|
107
|
+
|
108
|
+
def _koi_breadcrumbs_attributes(**attributes)
|
109
|
+
{
|
110
|
+
aria: { label: "Breadcrumb" },
|
111
|
+
class: "breadcrumb",
|
112
|
+
}.merge_html(attributes)
|
113
|
+
end
|
114
|
+
|
115
|
+
def _koi_actions_attributes(**attributes)
|
116
|
+
{
|
117
|
+
aria: { label: "Actions" },
|
118
|
+
class: "actions",
|
119
|
+
}.merge_html(attributes)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Koi
|
4
4
|
module IndexActionsHelper
|
5
|
+
# @deprecated to be removed in Koi 5
|
5
6
|
def koi_index_actions(search: false, create: false, &)
|
6
7
|
IndexActionsBuilder.new(self, search:, create:).render(&)
|
7
8
|
end
|
@@ -43,7 +44,7 @@ module Koi
|
|
43
44
|
end
|
44
45
|
|
45
46
|
def search(form)
|
46
|
-
tag.div class: "actions
|
47
|
+
tag.div class: "actions" do
|
47
48
|
concat(search_button(form)) if search?
|
48
49
|
concat(create_button(form)) if create?
|
49
50
|
concat(search_input(form)) if search?
|
@@ -75,7 +76,7 @@ module Koi
|
|
75
76
|
end
|
76
77
|
|
77
78
|
def links(form, &)
|
78
|
-
tag.div(class: "actions
|
79
|
+
tag.div(class: "actions") do
|
79
80
|
yield(form) if block_given?
|
80
81
|
end
|
81
82
|
end
|