plutonium 0.14.0 → 0.15.0.pre.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README copy.md +1 -1
- data/README.md +1 -1
- data/app/assets/plutonium.css +1 -1
- data/app/views/{application → plutonium}/_resource_header.html copy.erb +1 -1
- data/app/views/{application → plutonium}/_resource_header.html.erb +1 -1
- data/app/views/{application → plutonium}/_resource_sidebar.html.erb +2 -0
- data/app/views/resource/_resource_details.html.erb +1 -36
- data/app/views/resource/_resource_form.html.erb +1 -5
- data/app/views/resource/_resource_table.html.erb +315 -85
- data/app/views/resource/edit.html.erb +1 -5
- data/app/views/resource/index.html.erb +1 -5
- data/app/views/resource/new.html.erb +1 -5
- data/app/views/resource/show.html.erb +1 -5
- data/config/initializers/pagy.rb +1 -0
- data/config/initializers/rabl.rb +27 -20
- data/gemfiles/rails_7.gemfile.lock +5 -1
- data/lib/generators/pu/core/assets/assets_generator.rb +2 -2
- data/lib/generators/pu/core/install/install_generator.rb +0 -3
- data/lib/generators/pu/core/install/templates/app/controllers/plutonium_controller.rb.tt +2 -0
- data/lib/generators/pu/core/install/templates/app/controllers/resource_controller.rb.tt +21 -1
- data/lib/generators/pu/core/install/templates/app/definitions/resource_definition.rb.tt +2 -0
- data/lib/generators/pu/core/install/templates/app/models/resource_record.rb.tt +0 -2
- data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +5 -2
- data/lib/generators/pu/eject/shell/shell_generator.rb +2 -2
- data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +19 -0
- data/lib/generators/pu/lib/plutonium_generators/concerns/logger.rb +1 -1
- data/lib/generators/pu/lib/plutonium_generators/generator.rb +5 -3
- data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +26 -2
- data/lib/generators/pu/pkg/{feature/feature_generator.rb → package/package_generator.rb} +4 -4
- data/lib/generators/pu/pkg/{feature → package}/templates/app/controllers/resource_controller.rb.tt +0 -2
- data/lib/generators/pu/pkg/package/templates/app/definitions/resource_definition.rb.tt +4 -0
- data/lib/generators/pu/pkg/package/templates/app/query_objects/resource_query_object.rb.tt +4 -0
- data/lib/generators/pu/pkg/{app/app_generator.rb → portal/portal_generator.rb} +10 -8
- data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/concerns/controller.rb.tt +3 -7
- data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/dashboard_controller.rb.tt +1 -1
- data/lib/generators/pu/pkg/portal/templates/app/controllers/plutonium_controller.rb.tt +5 -0
- data/lib/generators/pu/pkg/{app/templates/app/controllers/controller.rb.tt → portal/templates/app/controllers/resource_controller.rb.tt} +1 -1
- data/lib/generators/pu/pkg/portal/templates/app/definitions/resource_definition.rb.tt +4 -0
- data/lib/generators/pu/pkg/{app → portal}/templates/app/views/package/dashboard/index.html.erb +2 -1
- data/lib/generators/pu/res/conn/conn_generator.rb +78 -3
- data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +1 -1
- data/lib/generators/pu/res/conn/templates/app/definitions/resource_definition.rb.tt +3 -0
- data/lib/generators/pu/res/conn/templates/app/policies/resource_policy.rb.tt +29 -1
- data/lib/generators/pu/res/conn/templates/app/presenters/resource_presenter.rb.tt +1 -1
- data/lib/generators/pu/res/conn/templates/app/query_objects/resource_query_object.rb.tt +1 -1
- data/lib/generators/pu/res/model/model_generator.rb +0 -7
- data/lib/generators/pu/res/model/templates/model.rb.tt +4 -1
- data/lib/generators/pu/res/scaffold/scaffold_generator.rb +22 -4
- data/lib/generators/pu/res/scaffold/templates/controller.rb.tt +0 -1
- data/lib/generators/pu/res/scaffold/templates/definition.rb.tt +4 -0
- data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +2 -2
- data/lib/generators/pu/rodauth/templates/app/controllers/rodauth_controller.rb.tt +1 -1
- data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +270 -0
- data/lib/plutonium/action/README.md +0 -0
- data/lib/plutonium/action/base.rb +103 -0
- data/lib/plutonium/action/interactive.rb +117 -0
- data/lib/plutonium/action/route_options.rb +65 -0
- data/lib/plutonium/action/simple.rb +8 -0
- data/lib/plutonium/auth.rb +1 -1
- data/lib/plutonium/configuration.rb +130 -0
- data/lib/plutonium/core/actions/collection.rb +1 -1
- data/lib/plutonium/core/associations/renderers/factory.rb +3 -1
- data/lib/plutonium/core/autodiscovery/association_renderer_discoverer.rb +1 -1
- data/lib/plutonium/core/autodiscovery/input_discoverer.rb +1 -1
- data/lib/plutonium/core/autodiscovery/renderer_discoverer.rb +1 -1
- data/lib/plutonium/core/controller.rb +110 -0
- data/lib/plutonium/core/controllers/authorizable.rb +12 -35
- data/lib/plutonium/core/controllers/bootable.rb +38 -7
- data/lib/plutonium/core/controllers/entity_scoping.rb +6 -2
- data/lib/plutonium/core/fields/renderers/association_renderer.rb +1 -1
- data/lib/plutonium/core/ui/collection.rb +1 -1
- data/lib/plutonium/core/ui/detail.rb +1 -1
- data/lib/plutonium/core/ui/form.rb +1 -1
- data/lib/plutonium/definition/actions.rb +50 -0
- data/lib/plutonium/definition/base.rb +92 -0
- data/lib/plutonium/definition/config_attr.rb +30 -0
- data/lib/plutonium/definition/defineable_props.rb +96 -0
- data/lib/plutonium/definition/search.rb +21 -0
- data/lib/plutonium/engine/validator.rb +30 -0
- data/lib/plutonium/engine.rb +25 -0
- data/lib/plutonium/helpers/assets_helper.rb +73 -20
- data/lib/plutonium/helpers/form_helper.rb +1 -3
- data/lib/plutonium/interaction/README.md +369 -0
- data/lib/plutonium/interaction/base.rb +75 -0
- data/lib/plutonium/interaction/concerns/presentable.rb +61 -0
- data/lib/plutonium/interaction/concerns/workflow_dsl.rb +82 -0
- data/lib/plutonium/interaction/outcome.rb +129 -0
- data/lib/plutonium/interaction/response/base.rb +63 -0
- data/lib/plutonium/interaction/response/null.rb +33 -0
- data/lib/plutonium/interaction/response/redirect.rb +30 -0
- data/lib/plutonium/interaction/response/render.rb +28 -0
- data/lib/plutonium/lib/bit_flags.rb +70 -9
- data/lib/plutonium/lib/overlayed_hash.rb +86 -0
- data/lib/plutonium/lib/smart_cache.rb +171 -0
- data/lib/plutonium/models/has_cents.rb +170 -0
- data/lib/plutonium/{pkg/base.rb → package/engine.rb} +10 -2
- data/lib/plutonium/{application → portal}/controller.rb +3 -11
- data/lib/plutonium/{application → portal}/dynamic_controllers.rb +4 -4
- data/lib/plutonium/portal/engine.rb +15 -0
- data/lib/plutonium/railtie.rb +35 -15
- data/lib/plutonium/reloader.rb +71 -29
- data/lib/plutonium/resource/controller.rb +51 -34
- data/lib/plutonium/resource/controllers/authorizable.rb +128 -0
- data/lib/plutonium/{core → resource}/controllers/crud_actions.rb +23 -22
- data/lib/plutonium/resource/controllers/defineable.rb +26 -0
- data/lib/plutonium/{core → resource}/controllers/interactive_actions.rb +12 -12
- data/lib/plutonium/resource/controllers/presentable.rb +41 -0
- data/lib/plutonium/resource/controllers/queryable.rb +44 -0
- data/lib/plutonium/resource/definition.rb +6 -0
- data/lib/plutonium/resource/policy.rb +25 -13
- data/lib/plutonium/resource/query_object.rb +50 -51
- data/lib/plutonium/resource/record.rb +6 -89
- data/lib/plutonium/resource/register.rb +82 -0
- data/lib/plutonium/routing/mapper_extensions.rb +1 -1
- data/lib/plutonium/routing/resource_registration.rb +1 -1
- data/lib/plutonium/routing/route_set_extensions.rb +6 -18
- data/lib/plutonium/ui/action_button.rb +125 -0
- data/lib/plutonium/ui/breadcrumbs.rb +163 -0
- data/lib/plutonium/ui/component/base.rb +13 -0
- data/lib/plutonium/ui/component/behaviour.rb +38 -0
- data/lib/plutonium/ui/component/kit.rb +31 -0
- data/lib/plutonium/ui/component/methods.rb +54 -0
- data/lib/plutonium/ui/display/base.rb +25 -0
- data/lib/plutonium/ui/display/component/association.rb +26 -0
- data/lib/plutonium/ui/display/resource.rb +77 -0
- data/lib/plutonium/ui/display/theme.rb +27 -0
- data/lib/plutonium/ui/dyna_frame/content.rb +20 -0
- data/lib/plutonium/ui/empty_card.rb +20 -0
- data/lib/plutonium/ui/form/base.rb +37 -0
- data/lib/plutonium/ui/form/resource.rb +75 -0
- data/lib/plutonium/ui/form/theme.rb +42 -0
- data/lib/plutonium/ui/page/base.rb +112 -0
- data/lib/plutonium/ui/page/edit.rb +23 -0
- data/lib/plutonium/ui/page/index.rb +27 -0
- data/lib/plutonium/ui/page/new.rb +23 -0
- data/lib/plutonium/ui/page/show.rb +27 -0
- data/lib/plutonium/ui/page_header.rb +49 -0
- data/lib/plutonium/ui/table/base.rb +13 -0
- data/lib/plutonium/ui/table/components/pagy_info.rb +70 -0
- data/lib/plutonium/ui/table/components/pagy_page_info.rb +70 -0
- data/lib/plutonium/ui/table/components/pagy_pagination.rb +105 -0
- data/lib/plutonium/ui/table/components/scopes_bar.rb +136 -0
- data/lib/plutonium/ui/table/components/search_bar.rb +158 -0
- data/lib/plutonium/ui/table/display_theme.rb +21 -0
- data/lib/plutonium/ui/table/resource.rb +98 -0
- data/lib/plutonium/ui/table/theme.rb +35 -0
- data/lib/plutonium/ui.rb +9 -0
- data/lib/plutonium/version.rb +5 -1
- data/lib/plutonium.rb +53 -26
- data/package-lock.json +19 -22
- data/package.json +4 -4
- data/sig/.keep +0 -0
- data/src/css/plutonium.css +15 -0
- data/tailwind.options.js +11 -3
- metadata +220 -81
- data/lib/generators/pu/core/install/templates/app/presenters/resource_presenter.rb.tt +0 -2
- data/lib/generators/pu/core/install/templates/app/query_objects/resource_query_object.rb.tt +0 -2
- data/lib/generators/pu/pkg/feature/templates/app/query_objects/resource_query_object.rb.tt +0 -4
- data/lib/plutonium/concerns/resource_validatable.rb +0 -34
- data/lib/plutonium/config.rb +0 -9
- data/lib/plutonium/core/controllers/base.rb +0 -101
- data/lib/plutonium/core/controllers/presentable.rb +0 -65
- data/lib/plutonium/core/controllers/queryable.rb +0 -28
- data/lib/plutonium/pkg/app.rb +0 -35
- data/lib/plutonium/pkg/concerns/resource_validatable.rb +0 -36
- data/lib/plutonium/pkg/feature.rb +0 -18
- data/lib/plutonium/policy/initializer.rb +0 -22
- data/lib/plutonium/policy/scope.rb +0 -19
- data/lib/plutonium/pundit/context.rb +0 -18
- data/lib/plutonium/pundit/policy_finder.rb +0 -25
- data/lib/plutonium/resource/policy_context.rb +0 -5
- data/lib/plutonium/resource_register.rb +0 -83
- data/lib/plutonium/smart_cache.rb +0 -151
- data/sig/plutonium.rbs +0 -12
- /data/app/views/{application → plutonium}/_flash.html.erb +0 -0
- /data/app/views/{application → plutonium}/_flash_alerts.html.erb +0 -0
- /data/app/views/{application → plutonium}/_flash_toasts.html.erb +0 -0
- /data/lib/generators/pu/pkg/{app/templates/app/views/package → package/templates}/.keep +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/interactions/resource_interaction.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/models/resource_record.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/policies/resource_policy.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/presenters/resource_presenter.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/lib/engine.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/app/policies/resource_policy.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/app/presenters/resource_presenter.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/app/query_objects/resource_query_object.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature/templates → portal/templates/app/views/package}/.keep +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/config/routes.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/lib/engine.rb.tt +0 -0
@@ -0,0 +1,270 @@
|
|
1
|
+
require "sequel/core"
|
2
|
+
|
3
|
+
class <%= account_path.classify %>RodauthPlugin < RodauthPlugin
|
4
|
+
configure do
|
5
|
+
# This block is running inside of
|
6
|
+
# plugin :rodauth do
|
7
|
+
# ...
|
8
|
+
# end
|
9
|
+
|
10
|
+
# ==> Features
|
11
|
+
# See the Rodauth documentation for the list of available config options:
|
12
|
+
# http://rodauth.jeremyevans.net/documentation.html
|
13
|
+
|
14
|
+
# List of authentication features that are loaded.
|
15
|
+
enable <%= selected_features.map(&:inspect).join ', ' %>
|
16
|
+
|
17
|
+
# ==> General
|
18
|
+
|
19
|
+
# Change prefix of table and foreign key column names from default "account"
|
20
|
+
# accounts_table: 'players'
|
21
|
+
|
22
|
+
# The secret key used for hashing public-facing tokens for various features.
|
23
|
+
# Defaults to Rails `secret_key_base`, but you can use your own secret key.
|
24
|
+
# hmac_secret "<SECRET_KEY>"
|
25
|
+
|
26
|
+
# Use path prefix for all routes.
|
27
|
+
<%= '# ' if primary? %>prefix "/<%= account_path.pluralize %>"
|
28
|
+
<% unless separate_passwords? -%>
|
29
|
+
|
30
|
+
# Store password hash in a column instead of a separate table.
|
31
|
+
account_password_hash_column :password_hash
|
32
|
+
<% end -%>
|
33
|
+
|
34
|
+
# Specify the controller used for view rendering, CSRF, and callbacks.
|
35
|
+
rails_controller { Rodauth::<%= account_path.classify %>Controller }
|
36
|
+
|
37
|
+
# Specify the model to be used.
|
38
|
+
rails_account_model { <%= account_path.classify %> }
|
39
|
+
<% if verify_account? -%>
|
40
|
+
|
41
|
+
# Set password password during create account.
|
42
|
+
# verify_account_set_password? false
|
43
|
+
<% end -%>
|
44
|
+
|
45
|
+
# Change some default param keys.
|
46
|
+
# login_param "email"
|
47
|
+
# password_confirm_param "confirm_password"
|
48
|
+
<% if login? -%>
|
49
|
+
|
50
|
+
# Redirect back to originally requested location after authentication.
|
51
|
+
login_return_to_requested_location? true
|
52
|
+
<% end -%>
|
53
|
+
# two_factor_auth_return_to_requested_location? true # if using MFA
|
54
|
+
|
55
|
+
# Autologin the user after they have reset their password.
|
56
|
+
# reset_password_autologin? true
|
57
|
+
|
58
|
+
# Delete the account record when the user has closed their account.
|
59
|
+
# delete_account_on_close? true
|
60
|
+
|
61
|
+
# Redirect to the app from login and registration pages if already logged in.
|
62
|
+
# already_logged_in { redirect login_redirect }
|
63
|
+
<% if jwt? -%>
|
64
|
+
|
65
|
+
# ==> JWT
|
66
|
+
|
67
|
+
# Set JWT secret, which is used to cryptographically protect the token.
|
68
|
+
jwt_secret Rails.application.credentials.secret_key_base
|
69
|
+
<% end -%>
|
70
|
+
<% if only_json? -%>
|
71
|
+
|
72
|
+
# ==> Api only
|
73
|
+
|
74
|
+
# Accept only JSON requests.
|
75
|
+
only_json? true
|
76
|
+
|
77
|
+
# Handle login and password confirmation fields on the client side.
|
78
|
+
require_password_confirmation? false
|
79
|
+
require_login_confirmation? false
|
80
|
+
<% else -%>
|
81
|
+
|
82
|
+
# Accept both api and form requests
|
83
|
+
# Requires the JSON feature
|
84
|
+
<%= '# ' unless json? %>only_json? false
|
85
|
+
<% end -%>
|
86
|
+
<% if mails? -%>
|
87
|
+
|
88
|
+
# ==> Emails
|
89
|
+
# Use a custom mailer for delivering authentication emails.
|
90
|
+
<% if reset_password? -%>
|
91
|
+
|
92
|
+
create_reset_password_email do
|
93
|
+
Rodauth::<%= account_path.classify %>Mailer.reset_password(self.class.configuration_name, account_id, reset_password_key_value)
|
94
|
+
end
|
95
|
+
<% end -%>
|
96
|
+
<% if verify_account? -%>
|
97
|
+
|
98
|
+
create_verify_account_email do
|
99
|
+
Rodauth::<%= account_path.classify %>Mailer.verify_account(self.class.configuration_name, account_id, verify_account_key_value)
|
100
|
+
end
|
101
|
+
<% end -%>
|
102
|
+
<% if verify_login_change? -%>
|
103
|
+
|
104
|
+
create_verify_login_change_email do |_login|
|
105
|
+
Rodauth::<%= account_path.classify %>Mailer.verify_login_change(self.class.configuration_name, account_id, verify_login_change_key_value)
|
106
|
+
end
|
107
|
+
<% end -%>
|
108
|
+
<% if change_password_notify? -%>
|
109
|
+
|
110
|
+
create_password_changed_email do
|
111
|
+
Rodauth::<%= account_path.classify %>Mailer.change_password_notify(self.class.configuration_name, account_id)
|
112
|
+
end
|
113
|
+
<% end -%>
|
114
|
+
<% if reset_password_notify? -%>
|
115
|
+
|
116
|
+
create_reset_password_notify_email do
|
117
|
+
Rodauth::<%= account_path.classify %>Mailer.reset_password_notify(self.class.configuration_name, account_id)
|
118
|
+
end
|
119
|
+
<% end -%>
|
120
|
+
<% if email_auth? -%>
|
121
|
+
|
122
|
+
create_email_auth_email do
|
123
|
+
Rodauth::<%= account_path.classify %>Mailer.email_auth(self.class.configuration_name, account_id, email_auth_key_value)
|
124
|
+
end
|
125
|
+
<% end -%>
|
126
|
+
<% if lockout? -%>
|
127
|
+
|
128
|
+
create_unlock_account_email do
|
129
|
+
Rodauth::<%= account_path.classify %>Mailer.unlock_account(self.class.configuration_name, account_id, unlock_account_key_value)
|
130
|
+
end
|
131
|
+
<% end -%>
|
132
|
+
|
133
|
+
send_email do |email|
|
134
|
+
# queue email delivery on the mailer after the transaction commits
|
135
|
+
db.after_commit { email.deliver_later }
|
136
|
+
end
|
137
|
+
<% end -%>
|
138
|
+
<% unless only_json? -%>
|
139
|
+
|
140
|
+
# ==> Flash
|
141
|
+
# Does not work with only_json?
|
142
|
+
|
143
|
+
# Match flash keys with ones already used in the Rails app.
|
144
|
+
# flash_notice_key :success # default is :notice
|
145
|
+
# flash_error_key :error # default is :alert
|
146
|
+
|
147
|
+
# Override default flash messages.
|
148
|
+
# create_account_notice_flash "Your account has been created. Please verify your account by visiting the confirmation link sent to your email address."
|
149
|
+
# require_login_error_flash "Login is required for accessing this page"
|
150
|
+
# login_notice_flash nil
|
151
|
+
<% end -%>
|
152
|
+
|
153
|
+
# ==> Validation
|
154
|
+
# Override default validation error messages.
|
155
|
+
# no_matching_login_message "user with this email address doesn't exist"
|
156
|
+
# already_an_account_with_this_login_message "user with this email address already exists"
|
157
|
+
# password_too_short_message { "needs to have at least #{password_minimum_length} characters" }
|
158
|
+
# login_does_not_meet_requirements_message { "invalid email#{", #{login_requirement_message}" if login_requirement_message}" }
|
159
|
+
|
160
|
+
# ==> Passwords
|
161
|
+
|
162
|
+
# Passwords shorter than 8 characters are considered weak according to OWASP.
|
163
|
+
<%= '# ' unless login? %>password_minimum_length 8
|
164
|
+
|
165
|
+
# Custom password complexity requirements (alternative to password_complexity feature).
|
166
|
+
# password_meets_requirements? do |password|
|
167
|
+
# super(password) && password_complex_enough?(password)
|
168
|
+
# end
|
169
|
+
# auth_class_eval do
|
170
|
+
# def password_complex_enough?(password)
|
171
|
+
# return true if password.match?(/\d/) && password.match?(/[^a-zA-Z\d]/)
|
172
|
+
# set_password_requirement_error_message(:password_simple, "requires one number and one special character")
|
173
|
+
# false
|
174
|
+
# end
|
175
|
+
# end
|
176
|
+
<% unless argon2? -%>
|
177
|
+
|
178
|
+
# = bcrypt
|
179
|
+
|
180
|
+
# bcrypt has a maximum input length of 72 bytes, truncating any extra bytes.
|
181
|
+
password_maximum_bytes 72 if respond_to?(:password_maximum_bytes)
|
182
|
+
<% else -%>
|
183
|
+
|
184
|
+
# = argon2
|
185
|
+
|
186
|
+
# Use a rotatable password pepper when hashing passwords with Argon2.
|
187
|
+
argon2_secret "TODO: <SECRET_KEY>"
|
188
|
+
|
189
|
+
# Since we're using argon2, prevent loading the bcrypt gem to save memory.
|
190
|
+
require_bcrypt? false
|
191
|
+
|
192
|
+
# Having a maximum password length set prevents long password DoS attacks.
|
193
|
+
password_maximum_length 64 if respond_to?(:password_maximum_length)
|
194
|
+
<% end -%>
|
195
|
+
<% if remember? -%>
|
196
|
+
|
197
|
+
# ==> Remember Feature
|
198
|
+
|
199
|
+
# Remember all logged in users.
|
200
|
+
after_login { remember_login }
|
201
|
+
|
202
|
+
# Or only remember users that have ticked a "Remember Me" checkbox on login.
|
203
|
+
# after_login { remember_login if param_or_nil("remember") }
|
204
|
+
|
205
|
+
# Extend user's remember period when remembered via a cookie
|
206
|
+
extend_remember_deadline? true
|
207
|
+
|
208
|
+
# Store the user's remember cookie under a namespace
|
209
|
+
remember_cookie_key "_<%= table_prefix %>_remember"
|
210
|
+
<% end -%>
|
211
|
+
|
212
|
+
# ==> Hooks
|
213
|
+
|
214
|
+
# Validate custom fields in the create account form.
|
215
|
+
# before_create_account do
|
216
|
+
# throw_error_status(422, "name", "must be present") if param("name").empty?
|
217
|
+
# end
|
218
|
+
|
219
|
+
# Perform additional actions after the account is created.
|
220
|
+
# after_create_account do
|
221
|
+
# Profile.create!(account_id: account_id, name: param("name"))
|
222
|
+
# end
|
223
|
+
|
224
|
+
# Do additional cleanup after the account is closed.
|
225
|
+
# after_close_account do
|
226
|
+
# Profile.find_by!(account_id: account_id).destroy
|
227
|
+
# end
|
228
|
+
<% unless only_json? -%>
|
229
|
+
|
230
|
+
# ==> Redirects
|
231
|
+
<% if create_account? -%>
|
232
|
+
|
233
|
+
# Redirect to home after login.
|
234
|
+
create_account_redirect "/"
|
235
|
+
<% end -%>
|
236
|
+
<% if login? -%>
|
237
|
+
|
238
|
+
# Redirect to home after login.
|
239
|
+
login_redirect "/"
|
240
|
+
<% end -%>
|
241
|
+
<% if logout? -%>
|
242
|
+
|
243
|
+
# Redirect to home page after logout.
|
244
|
+
logout_redirect "/"
|
245
|
+
<% end -%>
|
246
|
+
<% if verify_account? -%>
|
247
|
+
|
248
|
+
# Redirect to wherever login redirects to after account verification.
|
249
|
+
verify_account_redirect { login_redirect }
|
250
|
+
<% end -%>
|
251
|
+
<% if reset_password? -%>
|
252
|
+
|
253
|
+
# Redirect to login page after password reset.
|
254
|
+
reset_password_redirect { login_path }
|
255
|
+
<% end -%>
|
256
|
+
|
257
|
+
# Ensure requiring login follows login route changes.
|
258
|
+
require_login_redirect { login_path }
|
259
|
+
<% end -%>
|
260
|
+
|
261
|
+
# ==> Deadlines
|
262
|
+
# Change default deadlines for some actions.
|
263
|
+
# verify_account_grace_period 3.days.to_i
|
264
|
+
# reset_password_deadline_interval Hash[hours: 6]
|
265
|
+
# verify_login_change_deadline_interval Hash[days: 2]
|
266
|
+
<% unless only_json? -%>
|
267
|
+
# remember_deadline_interval Hash[days: 30]
|
268
|
+
<% end -%>
|
269
|
+
end
|
270
|
+
end
|
File without changes
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/string_inquirer"
|
4
|
+
|
5
|
+
module Plutonium
|
6
|
+
module Action
|
7
|
+
# Base class for all actions in the Plutonium framework.
|
8
|
+
#
|
9
|
+
# @attr_reader [Symbol] name The name of the action.
|
10
|
+
# @attr_reader [String] label The human-readable label for the action.
|
11
|
+
# @attr_reader [String, nil] icon The icon associated with the action.
|
12
|
+
# @attr_reader [RouteOptions] route_options The routing options for the action.
|
13
|
+
# @attr_reader [String, nil] confirmation The confirmation message for the action.
|
14
|
+
# @attr_reader [String, nil] turbo_frame The Turbo Frame ID for the action.
|
15
|
+
# @attr_reader [Symbol, nil] color The color associated with the action.
|
16
|
+
# @attr_reader [Symbol, nil] category The category of the action.
|
17
|
+
# @attr_reader [Integer] position The position of the action within its category.
|
18
|
+
class Base
|
19
|
+
attr_reader :name, :label, :icon, :route_options, :confirmation, :turbo_frame, :color, :category, :position
|
20
|
+
|
21
|
+
# Initialize a new action.
|
22
|
+
#
|
23
|
+
# @param [Symbol] name The name of the action.
|
24
|
+
# @param [Hash] options The options for the action.
|
25
|
+
# @option options [String] :label The human-readable label for the action.
|
26
|
+
# @option options [String] :icon The icon associated with the action (e.g., 'fa-edit' for Font Awesome).
|
27
|
+
# @option options [Symbol] :color The color associated with the action (e.g., :primary, :secondary, :success, :warning, :danger).
|
28
|
+
# @option options [String] :confirmation The confirmation message to display before executing the action.
|
29
|
+
# @option options [RouteOptions, Hash] :route_options The routing options for the action.
|
30
|
+
# @option options [String] :turbo_frame The Turbo Frame ID for the action (used in Hotwire/Turbo Drive applications).
|
31
|
+
# @option options [Boolean] :bulk_action (false) If true, applies to a bulk selection of records (e.g., "Mark Selected as Read").
|
32
|
+
# @option options [Boolean] :collection_record_action (false) If true, applies to records in a collection (e.g., "Edit Record" button in a table).
|
33
|
+
# @option options [Boolean] :record_action (false) If true, applies to an individual record (e.g., "Delete" button on a Show page).
|
34
|
+
# @option options [Boolean] :resource_action (false) If true, applies to the entire resource and can be used in any context (e.g., "Import from CSV").
|
35
|
+
# @option options [Symbol] :category The category of the action. Determines visibility and grouping.
|
36
|
+
# Valid values include:
|
37
|
+
# @option options [Symbol] :primary Always shown and given prominence in the UI.
|
38
|
+
# @option options [Symbol] :secondary Shown in secondary menus or less prominent areas.
|
39
|
+
# @option options [Symbol] :danger Actions that require caution, often destructive operations.
|
40
|
+
# @option options [Integer] :position (50) The position of the action in its group. Lower numbers appear first.
|
41
|
+
def initialize(name, **options)
|
42
|
+
@name = name.to_sym
|
43
|
+
@label = options[:label] || name.to_s.humanize
|
44
|
+
@icon = options[:icon]
|
45
|
+
@color = options[:color]
|
46
|
+
@confirmation = options[:confirmation]
|
47
|
+
@route_options = build_route_options(options[:route_options])
|
48
|
+
@turbo_frame = options[:turbo_frame]
|
49
|
+
@bulk_action = options[:bulk_action] || false
|
50
|
+
@collection_record_action = options[:collection_record_action] || false
|
51
|
+
@record_action = options[:record_action] || false
|
52
|
+
@resource_action = options[:resource_action] || false
|
53
|
+
@category = ActiveSupport::StringInquirer.new((options[:category] || :secondary).to_s)
|
54
|
+
@position = options[:position] || 50
|
55
|
+
|
56
|
+
freeze
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Boolean] Whether this is a bulk action.
|
60
|
+
def bulk_action?
|
61
|
+
@bulk_action
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Boolean] Whether this is a collection record action.
|
65
|
+
def collection_record_action?
|
66
|
+
@collection_record_action
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [Boolean] Whether this is a record action.
|
70
|
+
def record_action?
|
71
|
+
@record_action
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [Boolean] Whether this is a resource action.
|
75
|
+
def resource_action?
|
76
|
+
@resource_action
|
77
|
+
end
|
78
|
+
|
79
|
+
def permitted_by?(policy)
|
80
|
+
policy.allowed_to?(:"#{name}?")
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# Build RouteOptions from the provided options
|
86
|
+
#
|
87
|
+
# @param [RouteOptions, Hash, nil] options The routing options
|
88
|
+
# @return [RouteOptions] The built RouteOptions object
|
89
|
+
def build_route_options(options)
|
90
|
+
case options
|
91
|
+
when RouteOptions
|
92
|
+
options
|
93
|
+
when Hash
|
94
|
+
RouteOptions.new(**options)
|
95
|
+
when nil
|
96
|
+
RouteOptions.new
|
97
|
+
else
|
98
|
+
raise ArgumentError, "Invalid route_options. Expected RouteOptions, Hash, or nil."
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module Action
|
5
|
+
# InteractiveAction class for handling interactive actions in the Plutonium framework
|
6
|
+
#
|
7
|
+
# @attr_reader [Class] interaction The interaction class associated with this action
|
8
|
+
# @attr_reader [Boolean] immediate Whether the action is executed immediately
|
9
|
+
class Interactive < Base
|
10
|
+
attr_reader :interaction, :immediate
|
11
|
+
|
12
|
+
# Initialize a new InteractiveAction
|
13
|
+
#
|
14
|
+
# @param [Symbol] name The name of the action
|
15
|
+
# @param [Class] interaction The interaction class for this action
|
16
|
+
# @param [Boolean] immediate Whether the action is executed immediately
|
17
|
+
# @param [Hash] options Additional options for the action
|
18
|
+
def initialize(name, interaction:, immediate:, **)
|
19
|
+
@interaction = interaction
|
20
|
+
@immediate = immediate
|
21
|
+
|
22
|
+
super(name, **)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get the confirmation message for the action
|
26
|
+
#
|
27
|
+
# @return [String, nil] The confirmation message or nil if not applicable
|
28
|
+
def confirmation
|
29
|
+
super || (@immediate ? "#{label}?" : nil)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Factory for creating Interactive actions
|
33
|
+
class Factory
|
34
|
+
# Create a new Interactive action based on the interaction type
|
35
|
+
#
|
36
|
+
# @param [Symbol] name The name of the action
|
37
|
+
# @param [Class] interaction The interaction class
|
38
|
+
# @param [Hash] options Additional options for the action
|
39
|
+
# @return [Interactive] A new Interactive action instance
|
40
|
+
def self.create(name, interaction:, **options)
|
41
|
+
attribute_names = symbolized_attribute_names(interaction)
|
42
|
+
action_type = determine_action_type(attribute_names)
|
43
|
+
input_fields = determine_input_fields(attribute_names)
|
44
|
+
action_options = determine_action_options(action_type)
|
45
|
+
immediate = options.fetch(:immediate) { input_fields.blank? }
|
46
|
+
route_options = build_route_options(name, action_type, immediate)
|
47
|
+
|
48
|
+
Interactive.new(
|
49
|
+
name,
|
50
|
+
interaction: interaction,
|
51
|
+
immediate: immediate,
|
52
|
+
route_options: route_options,
|
53
|
+
**action_options,
|
54
|
+
**options
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get symbolized attribute names for the interaction
|
59
|
+
#
|
60
|
+
# @param [Class] interaction The interaction class
|
61
|
+
# @return [Array<Symbol>] Symbolized attribute names
|
62
|
+
def self.symbolized_attribute_names(interaction)
|
63
|
+
interaction.attribute_names.map(&:to_sym)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Determine the action type based on the interaction's attributes
|
67
|
+
#
|
68
|
+
# @param [Array<Symbol>] attribute_names Symbolized attribute names
|
69
|
+
# @return [Symbol] The determined action type
|
70
|
+
def self.determine_action_type(attribute_names)
|
71
|
+
if attribute_names.include?(:resource)
|
72
|
+
:interactive_resource_record_action
|
73
|
+
elsif attribute_names.include?(:resources)
|
74
|
+
:interactive_resource_collection_action
|
75
|
+
else
|
76
|
+
:interactive_resource_recordless_action
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Determine the input fields for the action
|
81
|
+
#
|
82
|
+
# @param [Array<Symbol>] attribute_names Symbolized attribute names
|
83
|
+
# @return [Array<Symbol>] The input fields
|
84
|
+
def self.determine_input_fields(attribute_names)
|
85
|
+
attribute_names - [:resource, :resources]
|
86
|
+
end
|
87
|
+
|
88
|
+
# Determine the action options based on the action type
|
89
|
+
#
|
90
|
+
# @param [Symbol] action_type The type of the action
|
91
|
+
# @return [Hash] The action options
|
92
|
+
def self.determine_action_options(action_type)
|
93
|
+
{
|
94
|
+
bulk_action: action_type == :interactive_resource_collection_action,
|
95
|
+
record_action: action_type == :interactive_resource_record_action,
|
96
|
+
collection_record_action: action_type == :interactive_resource_record_action,
|
97
|
+
resource_action: action_type == :interactive_resource_recordless_action
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
# Build the route options for the action
|
102
|
+
#
|
103
|
+
# @param [Symbol] name The name of the action
|
104
|
+
# @param [Symbol] action_type The type of the action
|
105
|
+
# @param [Boolean] immediate Whether the action is executed immediately
|
106
|
+
# @return [RouteOptions] The route options for the action
|
107
|
+
def self.build_route_options(name, action_type, immediate)
|
108
|
+
RouteOptions.new(
|
109
|
+
method: immediate ? :post : :get,
|
110
|
+
action: action_type,
|
111
|
+
interactive_action: name
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module Action
|
5
|
+
# RouteOptions class for handling routing options in the Plutonium framework.
|
6
|
+
#
|
7
|
+
# @attr_reader [Symbol] method The HTTP method for the route.
|
8
|
+
# @attr_reader [Array] url_args The positional arguments for URL generation.
|
9
|
+
# @attr_reader [Hash] url_options URL options for the route.
|
10
|
+
# @attr_reader [Symbol] url_resolver The method to use for resolving URLs.
|
11
|
+
class RouteOptions
|
12
|
+
attr_reader :method, :url_args, :url_options, :url_resolver
|
13
|
+
|
14
|
+
# Initialize a new RouteOptions instance.
|
15
|
+
#
|
16
|
+
# @param [Array] url_args The positional arguments for URL generation.
|
17
|
+
# @param [Symbol] method The HTTP method for the route (default: :get).
|
18
|
+
# @param [Symbol] url_resolver The method to use for resolving URLs (default: :resource_url_for).
|
19
|
+
# @param [Hash] url_options URL options for the route.
|
20
|
+
def initialize(*url_args, method: :get, url_resolver: :resource_url_for, **url_options)
|
21
|
+
@method = method
|
22
|
+
@url_resolver = url_resolver
|
23
|
+
@url_args = url_args
|
24
|
+
@url_options = url_options.freeze
|
25
|
+
freeze
|
26
|
+
end
|
27
|
+
|
28
|
+
# Convert the RouteOptions to arguments suitable for URL helpers.
|
29
|
+
#
|
30
|
+
# @return [Array] The arguments for URL generation.
|
31
|
+
def to_url_args
|
32
|
+
@url_args + [@url_options]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Merge this RouteOptions with another RouteOptions instance.
|
36
|
+
#
|
37
|
+
# @param [RouteOptions] other The other RouteOptions instance to merge with.
|
38
|
+
# @return [RouteOptions] A new RouteOptions instance with merged values.
|
39
|
+
def merge(other)
|
40
|
+
self.class.new(
|
41
|
+
*(@url_args | other.url_args),
|
42
|
+
method: other.method || @method,
|
43
|
+
url_resolver: other.url_resolver || @url_resolver,
|
44
|
+
**@url_options.merge(other.url_options)
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def ==(other)
|
49
|
+
self.class == other.class &&
|
50
|
+
method == other.method &&
|
51
|
+
url_resolver == other.url_resolver &&
|
52
|
+
url_args == other.url_args &&
|
53
|
+
url_options == other.url_options
|
54
|
+
end
|
55
|
+
|
56
|
+
def eql?(other)
|
57
|
+
self == other
|
58
|
+
end
|
59
|
+
|
60
|
+
def hash
|
61
|
+
[self.class, method, url_resolver, url_args, url_options].hash
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|