plutonium 0.64.0 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/skills/plutonium/SKILL.md +4 -1
- data/.claude/skills/plutonium-app/SKILL.md +14 -0
- data/.claude/skills/plutonium-dashboard/SKILL.md +161 -0
- data/.claude/skills/plutonium-resource/SKILL.md +94 -14
- data/.claude/skills/plutonium-ui/SKILL.md +22 -1
- data/CHANGELOG.md +59 -0
- data/CLAUDE.md +282 -210
- data/SECURITY.md +1 -1
- data/app/assets/plutonium-charts.js +20604 -0
- data/app/assets/plutonium-charts.js.map +7 -0
- data/app/assets/plutonium-charts.min.js +41 -0
- data/app/assets/plutonium-charts.min.js.map +7 -0
- data/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.js +794 -629
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +48 -48
- data/app/assets/plutonium.min.js.map +4 -4
- data/app/views/plutonium/_flash_alerts.html.erb +2 -2
- data/app/views/plutonium/_resource_header.html.erb +2 -2
- data/app/views/plutonium/_resource_sidebar.html.erb +11 -2
- data/app/views/plutonium/_toast.html.erb +2 -2
- data/app/views/rodauth/_login_form.html.erb +1 -1
- data/app/views/rodauth/_password_visibility.html.erb +1 -1
- data/app/views/rodauth/add_recovery_codes.html.erb +2 -2
- data/app/views/rodauth/change_login.html.erb +2 -2
- data/app/views/rodauth/create_account.html.erb +4 -4
- data/app/views/rodauth/reset_password_request.html.erb +1 -1
- data/app/views/rodauth/verify_account_resend.html.erb +1 -1
- data/app/views/rodauth/webauthn_remove.html.erb +1 -1
- data/config/brakeman.ignore +4 -4
- data/config/initializers/rabl.rb +0 -40
- data/config/locales/en/api_client.yml +22 -0
- data/config/locales/en/async.yml +34 -0
- data/config/locales/en/dashboard.yml +25 -0
- data/config/locales/en/invites.yml +37 -0
- data/config/locales/en/js.yml +40 -0
- data/config/locales/en/kanban.yml +28 -0
- data/config/locales/en/plutonium.yml +26 -0
- data/config/locales/en/positioning.yml +14 -0
- data/config/locales/en/profile.yml +29 -0
- data/config/locales/en/resource.yml +60 -0
- data/config/locales/en/ui.yml +93 -0
- data/config/locales/en/wizard.yml +46 -0
- data/docs/.vitepress/config.ts +34 -1
- data/docs/.vitepress/theme/components/HomeHero.vue +1 -1
- data/docs/.vitepress/theme/components/HomeInTheBox.vue +14 -0
- data/docs/.vitepress/theme/custom.css +34 -377
- data/docs/blog/introducing-plutonium-dashboards.md +175 -0
- data/docs/blog/introducing-plutonium-i18n.md +91 -0
- data/docs/blog/introducing-plutonium.md +4 -5
- data/docs/blog/whats-new-async-kanban-wizards.md +1 -1
- data/docs/guides/dashboards.md +300 -0
- data/docs/guides/index.md +1 -0
- data/docs/guides/kanban.md +2 -2
- data/docs/public/images/blog/dashboards-card-error.png +0 -0
- data/docs/public/images/blog/dashboards-loading.png +0 -0
- data/docs/public/images/blog/dashboards-overview.png +0 -0
- data/docs/public/images/blog/dashboards-sidebar.png +0 -0
- data/docs/public/images/blog/i18n-es-admin.png +0 -0
- data/docs/public/images/guides/dashboard-card-error.png +0 -0
- data/docs/public/images/guides/dashboard-loading.png +0 -0
- data/docs/public/images/guides/dashboard-overview.png +0 -0
- data/docs/public/images/guides/dashboard-sidebar.png +0 -0
- data/docs/reference/app/generators.md +2 -2
- data/docs/reference/app/portals.md +2 -0
- data/docs/reference/configuration.md +84 -27
- data/docs/reference/dashboard/dsl.md +123 -0
- data/docs/reference/dashboard/index.md +35 -0
- data/docs/reference/dashboard/registration.md +76 -0
- data/docs/reference/i18n.md +239 -0
- data/docs/reference/index.md +10 -1
- data/docs/reference/kanban/dsl.md +1 -1
- data/docs/reference/kanban/positioning.md +2 -2
- data/docs/reference/resource/actions.md +1 -1
- data/docs/reference/resource/definition.md +96 -18
- data/docs/reference/resource/export.md +5 -1
- data/docs/reference/resource/index.md +2 -0
- data/docs/reference/{positioning.md → resource/positioning.md} +2 -1
- data/docs/reference/resource/query.md +2 -0
- data/docs/reference/ui/assets.md +8 -0
- data/docs/reference/ui/displays.md +5 -8
- data/docs/reference/ui/index.md +1 -1
- data/docs/reference/wizard/dsl.md +2 -2
- data/esbuild.config.js +5 -2
- data/gemfiles/rails_7.gemfile.lock +2 -2
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/active_model/validations/array_validator.rb +1 -1
- data/lib/active_model/validations/attached_validator.rb +1 -1
- data/lib/active_model/validations/url_validator.rb +2 -2
- data/lib/generators/pu/core/assets/assets_generator.rb +36 -21
- data/lib/generators/pu/core/assets/templates/postcss.config.js +1 -1
- data/lib/generators/pu/core/update/update_generator.rb +3 -2
- data/lib/generators/pu/dashboard/dashboard_generator.rb +96 -0
- data/lib/generators/pu/dashboard/templates/dashboard.rb.tt +25 -0
- data/lib/generators/pu/docker/install/install_generator.rb +58 -0
- data/lib/generators/pu/docker/install/templates/Dockerfile.dev.tt +18 -3
- data/lib/generators/pu/docker/install/templates/Dockerfile.tt +20 -5
- data/lib/generators/pu/docker/install/templates/{docker-compose.yml → docker-compose.yml.tt} +1 -1
- data/lib/generators/pu/gem/dotenv/dotenv_generator.rb +1 -1
- data/lib/generators/pu/invites/invitable_generator.rb +18 -1
- data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +0 -95
- data/lib/generators/pu/lib/plutonium_generators/concerns/js_package_manager.rb +146 -0
- data/lib/generators/pu/lib/plutonium_generators/generator.rb +1 -17
- data/lib/generators/pu/pkg/package/package_generator.rb +1 -0
- data/lib/generators/pu/pkg/package/templates/config/locales/en.yml.tt +28 -0
- data/lib/generators/pu/pkg/portal/portal_generator.rb +1 -0
- data/lib/generators/pu/pkg/portal/templates/config/locales/en.yml.tt +30 -0
- data/lib/generators/pu/res/scaffold/scaffold_generator.rb +0 -8
- data/lib/generators/pu/rodauth/concerns/feature_selector.rb +0 -18
- data/lib/plutonium/action/base.rb +27 -3
- data/lib/plutonium/action/interactive.rb +17 -4
- data/lib/plutonium/api_client/concerns/create_api_client.rb +12 -11
- data/lib/plutonium/api_client/concerns/disable_api_client.rb +3 -3
- data/lib/plutonium/auth/sequel_adapter.rb +1 -1
- data/lib/plutonium/configuration.rb +5 -0
- data/lib/plutonium/core/controller.rb +25 -1
- data/lib/plutonium/core/controllers/authorizable.rb +1 -1
- data/lib/plutonium/dashboard/base.rb +114 -0
- data/lib/plutonium/dashboard/card.rb +200 -0
- data/lib/plutonium/dashboard/controller.rb +101 -0
- data/lib/plutonium/dashboard/dsl.rb +104 -0
- data/lib/plutonium/dashboard/register.rb +40 -0
- data/lib/plutonium/dashboard/route_resolution.rb +30 -0
- data/lib/plutonium/dashboard.rb +42 -0
- data/lib/plutonium/definition/actions.rb +6 -1
- data/lib/plutonium/definition/base.rb +41 -3
- data/lib/plutonium/definition/index_views.rb +1 -1
- data/lib/plutonium/definition/positioning.rb +1 -1
- data/lib/plutonium/definition/presentable.rb +2 -2
- data/lib/plutonium/helpers/assets_helper.rb +15 -2
- data/lib/plutonium/helpers/display_helper.rb +10 -2
- data/lib/plutonium/interaction/async/context.rb +6 -8
- data/lib/plutonium/interaction/async/executor.rb +6 -7
- data/lib/plutonium/interaction/async/run.rb +2 -2
- data/lib/plutonium/interaction/async/run_definition.rb +1 -1
- data/lib/plutonium/interaction/base.rb +2 -0
- data/lib/plutonium/interaction/concerns/dispatchable.rb +15 -3
- data/lib/plutonium/invites/concerns/cancel_invite.rb +3 -3
- data/lib/plutonium/invites/concerns/invite_token.rb +25 -7
- data/lib/plutonium/invites/concerns/invite_user.rb +10 -5
- data/lib/plutonium/invites/concerns/resend_invite.rb +4 -4
- data/lib/plutonium/invites/controller.rb +11 -11
- data/lib/plutonium/kanban/column.rb +13 -3
- data/lib/plutonium/kanban/dsl.rb +7 -4
- data/lib/plutonium/models/has_cents.rb +1 -1
- data/lib/plutonium/profile/security_section.rb +10 -18
- data/lib/plutonium/query/filter.rb +10 -1
- data/lib/plutonium/query/filters/association.rb +27 -2
- data/lib/plutonium/query/filters/boolean.rb +4 -4
- data/lib/plutonium/query/filters/date.rb +4 -17
- data/lib/plutonium/query/filters/date_range.rb +2 -2
- data/lib/plutonium/query/filters/select.rb +10 -1
- data/lib/plutonium/query/filters/text.rb +9 -12
- data/lib/plutonium/railtie.rb +17 -0
- data/lib/plutonium/resource/controller.rb +10 -5
- data/lib/plutonium/resource/controllers/crud_actions.rb +17 -20
- data/lib/plutonium/resource/controllers/export_csv.rb +19 -9
- data/lib/plutonium/resource/controllers/kanban_actions.rb +16 -16
- data/lib/plutonium/resource/controllers/position_actions.rb +5 -5
- data/lib/plutonium/resource/controllers/typeahead.rb +24 -1
- data/lib/plutonium/resource/query_object.rb +30 -3
- data/lib/plutonium/routing/dashboard_registration.rb +137 -0
- data/lib/plutonium/routing/resource_registration.rb +5 -0
- data/lib/plutonium/routing/route_set_extensions.rb +2 -1
- data/lib/plutonium/translation.rb +227 -0
- data/lib/plutonium/ui/actions_dropdown.rb +2 -2
- data/lib/plutonium/ui/breadcrumbs.rb +3 -3
- data/lib/plutonium/ui/color_mode_selector.rb +1 -1
- data/lib/plutonium/ui/component/behaviour.rb +46 -0
- data/lib/plutonium/ui/component/methods.rb +4 -0
- data/lib/plutonium/ui/dashboard/board.rb +80 -0
- data/lib/plutonium/ui/dashboard/card.rb +127 -0
- data/lib/plutonium/ui/dashboard/chart.rb +43 -0
- data/lib/plutonium/ui/dashboard/custom.rb +32 -0
- data/lib/plutonium/ui/dashboard/frame.rb +25 -0
- data/lib/plutonium/ui/dashboard/metric.rb +137 -0
- data/lib/plutonium/ui/dashboard/skeleton.rb +40 -0
- data/lib/plutonium/ui/display/components/attachment.rb +1 -1
- data/lib/plutonium/ui/display/components/badge.rb +7 -1
- data/lib/plutonium/ui/display/components/boolean.rb +2 -2
- data/lib/plutonium/ui/display/resource.rb +23 -16
- data/lib/plutonium/ui/export_button.rb +3 -3
- data/lib/plutonium/ui/form/components/key_value_store.rb +5 -5
- data/lib/plutonium/ui/form/components/password.rb +2 -2
- data/lib/plutonium/ui/form/components/resource_select.rb +27 -0
- data/lib/plutonium/ui/form/components/secure_association.rb +2 -0
- data/lib/plutonium/ui/form/components/uppy.rb +11 -3
- data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +1 -1
- data/lib/plutonium/ui/form/concerns/renders_repeater_row_controls.rb +5 -4
- data/lib/plutonium/ui/form/concerns/renders_structured_inputs.rb +1 -1
- data/lib/plutonium/ui/form/query.rb +8 -8
- data/lib/plutonium/ui/form/resource.rb +54 -26
- data/lib/plutonium/ui/frame_navigator_panel.rb +4 -4
- data/lib/plutonium/ui/grid/card.rb +3 -3
- data/lib/plutonium/ui/grid/resource.rb +3 -3
- data/lib/plutonium/ui/interaction/async/run_progress.rb +6 -6
- data/lib/plutonium/ui/interaction/async/running_banner.rb +2 -2
- data/lib/plutonium/ui/kanban/column.rb +6 -6
- data/lib/plutonium/ui/layout/base.rb +19 -1
- data/lib/plutonium/ui/layout/header.rb +1 -1
- data/lib/plutonium/ui/layout/icon_rail.rb +4 -4
- data/lib/plutonium/ui/layout/rodauth_layout.rb +1 -1
- data/lib/plutonium/ui/layout/sidebar.rb +1 -1
- data/lib/plutonium/ui/layout/topbar.rb +2 -2
- data/lib/plutonium/ui/modal/base.rb +4 -4
- data/lib/plutonium/ui/nav_grid_menu.rb +1 -1
- data/lib/plutonium/ui/nav_user.rb +2 -2
- data/lib/plutonium/ui/page/base.rb +10 -2
- data/lib/plutonium/ui/page/dashboard.rb +54 -0
- data/lib/plutonium/ui/page/edit.rb +3 -3
- data/lib/plutonium/ui/page/interactive_action.rb +2 -2
- data/lib/plutonium/ui/page/new.rb +3 -3
- data/lib/plutonium/ui/page/show.rb +2 -2
- data/lib/plutonium/ui/page/wizard.rb +22 -16
- data/lib/plutonium/ui/page/wizard_chooser.rb +10 -10
- data/lib/plutonium/ui/page/wizard_completed.rb +2 -2
- data/lib/plutonium/ui/skeleton_table.rb +1 -1
- data/lib/plutonium/ui/table/base.rb +2 -2
- data/lib/plutonium/ui/table/components/attachment.rb +1 -1
- data/lib/plutonium/ui/table/components/bulk_actions_toolbar.rb +19 -3
- data/lib/plutonium/ui/table/components/drag_handle.rb +3 -3
- data/lib/plutonium/ui/table/components/filter_form.rb +11 -10
- data/lib/plutonium/ui/table/components/filter_pills.rb +4 -4
- data/lib/plutonium/ui/table/components/pagy_info.rb +61 -17
- data/lib/plutonium/ui/table/components/pagy_pagination.rb +9 -5
- data/lib/plutonium/ui/table/components/row_actions_dropdown.rb +1 -1
- data/lib/plutonium/ui/table/components/scopes_bar.rb +6 -2
- data/lib/plutonium/ui/table/components/scopes_pills.rb +3 -3
- data/lib/plutonium/ui/table/components/selection_column.rb +1 -1
- data/lib/plutonium/ui/table/components/toolbar.rb +2 -2
- data/lib/plutonium/ui/table/components/view_switcher.rb +12 -8
- data/lib/plutonium/ui/table/resource.rb +53 -17
- data/lib/plutonium/ui/wizard/review.rb +7 -7
- data/lib/plutonium/ui/wizard/stepper.rb +1 -1
- data/lib/plutonium/ui/wizard/summary_display.rb +6 -0
- data/lib/plutonium/version.rb +1 -1
- data/lib/plutonium/wizard/base.rb +2 -0
- data/lib/plutonium/wizard/driving.rb +8 -2
- data/lib/plutonium/wizard/dsl.rb +4 -2
- data/lib/plutonium/wizard/field_importer.rb +1 -8
- data/lib/plutonium/wizard/review_step.rb +8 -1
- data/lib/plutonium/wizard/step.rb +14 -3
- data/package.json +4 -2
- data/plutonium.gemspec +4 -1
- data/postcss-gem-import.cjs +34 -0
- data/postcss-gem-import.js +3 -28
- data/src/css/components.css +91 -0
- data/src/css/slim_select.css +0 -38
- data/src/css/tokens.css +24 -0
- data/src/js/controllers/attachment_input_controller.js +15 -6
- data/src/js/controllers/chart_controller.js +162 -0
- data/src/js/controllers/clipboard_controller.js +3 -2
- data/src/js/controllers/dirty_form_guard_controller.js +4 -3
- data/src/js/controllers/easymde_controller.js +3 -0
- data/src/js/controllers/filter_panel_controller.js +15 -6
- data/src/js/controllers/flatpickr_controller.js +8 -0
- data/src/js/controllers/frame_refresh_controller.js +59 -0
- data/src/js/controllers/intl_tel_input_controller.js +5 -0
- data/src/js/controllers/register_controllers.js +4 -0
- data/src/js/controllers/slim_select_controller.js +79 -176
- data/src/js/core.js +9 -1
- data/src/js/i18n.js +69 -0
- data/src/js/plutonium-charts.js +15 -0
- data/src/js/turbo/turbo_confirm.js +5 -3
- metadata +64 -16
- data/.yarnrc.yml +0 -8
- data/config/initializers/hotwire_turbo_monkey_patches.rb +0 -12
- data/lib/generators/pu/field/input/input_generator.rb +0 -32
- data/lib/generators/pu/field/input/templates/input.rb.tt +0 -15
- data/lib/generators/pu/lib/plutonium_generators/concerns/config.rb +0 -39
- data/lib/generators/pu/lib/plutonium_generators/concerns/serializer.rb +0 -39
- data/lib/generators/pu/lib/plutonium_generators/installer.rb +0 -205
- data/lib/generators/pu/res/scaffold/templates/presenter.rb.tt +0 -13
- data/lib/generators/pu/res/scaffold/templates/query_object.rb.tt +0 -17
- data/public/plutonium-assets/application.js +0 -31419
- data/public/plutonium-assets/plutonium.ico +0 -0
- data/yarn.lock +0 -6858
|
@@ -336,13 +336,25 @@ module Plutonium
|
|
|
336
336
|
# equivalent of that.
|
|
337
337
|
#
|
|
338
338
|
# No-op for ActiveStorage fields, and for uploaders declaring no rules.
|
|
339
|
+
#
|
|
340
|
+
# Which attributes are files is decided by their INPUT DECLARATION (an
|
|
341
|
+
# `as:` file alias), not by the shape of the value they currently hold.
|
|
342
|
+
# A direct upload arrives already staged, as a token String, so keying
|
|
343
|
+
# off IO-shape (respond_to?(:read)) skipped exactly those — an oversized
|
|
344
|
+
# cached token validated clean and dispatched, the rule surfacing as a
|
|
345
|
+
# run failure the submitter never sees. This is how a wizard decides the
|
|
346
|
+
# same question (Plutonium::Wizard::Attachments.field?).
|
|
339
347
|
def validate_dispatch_attachments
|
|
340
|
-
staged = attributes.except("resource", "resources").select { |_, v| dispatch_attachment?(v) }.keys
|
|
341
348
|
stage_dispatch_attachments!
|
|
342
349
|
|
|
343
|
-
|
|
350
|
+
self.class.defined_inputs.each do |name, config|
|
|
351
|
+
next unless Plutonium::Definition::InputAliases.file_input?(config.dig(:options, :as))
|
|
352
|
+
|
|
353
|
+
value = public_send(name)
|
|
354
|
+
next if value.blank?
|
|
355
|
+
|
|
344
356
|
messages = Plutonium::Attachments.validation_errors(
|
|
345
|
-
|
|
357
|
+
value, **dispatch_attachment_options(name)
|
|
346
358
|
)
|
|
347
359
|
messages.each { |message| errors.add(name, message) }
|
|
348
360
|
end
|
|
@@ -17,7 +17,7 @@ module Plutonium
|
|
|
17
17
|
extend ActiveSupport::Concern
|
|
18
18
|
|
|
19
19
|
included do
|
|
20
|
-
presents label: "
|
|
20
|
+
presents label: t("plutonium.invites.cancel_invite.label"), icon: Phlex::TablerIcons::CircleX
|
|
21
21
|
|
|
22
22
|
attribute :resource
|
|
23
23
|
end
|
|
@@ -34,11 +34,11 @@ module Plutonium
|
|
|
34
34
|
private
|
|
35
35
|
|
|
36
36
|
def success_message
|
|
37
|
-
"
|
|
37
|
+
I18n.t("plutonium.invites.cancel_invite.cancelled")
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def not_pending_message
|
|
41
|
-
"
|
|
41
|
+
I18n.t("plutonium.invites.cancel_invite.not_pending")
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
end
|
|
@@ -97,7 +97,7 @@ module Plutonium
|
|
|
97
97
|
# @raise [ActiveRecord::RecordInvalid] if constraints are violated
|
|
98
98
|
def validate_email_constraints!(user_email)
|
|
99
99
|
if enforce_email? && user_email.downcase != email.downcase
|
|
100
|
-
errors.add(:base,
|
|
100
|
+
errors.add(:base, :invite_email_mismatch, email: email)
|
|
101
101
|
raise ActiveRecord::RecordInvalid.new(self)
|
|
102
102
|
end
|
|
103
103
|
|
|
@@ -105,7 +105,7 @@ module Plutonium
|
|
|
105
105
|
user_domain = extract_domain(user_email)
|
|
106
106
|
|
|
107
107
|
if user_domain != required_domain
|
|
108
|
-
errors.add(:base,
|
|
108
|
+
errors.add(:base, :invite_domain_mismatch, domain: required_domain)
|
|
109
109
|
raise ActiveRecord::RecordInvalid.new(self)
|
|
110
110
|
end
|
|
111
111
|
end
|
|
@@ -115,16 +115,34 @@ module Plutonium
|
|
|
115
115
|
#
|
|
116
116
|
# This method:
|
|
117
117
|
# 1. Validates email constraints
|
|
118
|
-
# 2.
|
|
119
|
-
#
|
|
120
|
-
#
|
|
118
|
+
# 2. Acquires a row lock and re-checks the pending state to serialize
|
|
119
|
+
# concurrent acceptances (prevents a TOCTOU race from creating
|
|
120
|
+
# memberships for multiple users from a single invite when
|
|
121
|
+
# `enforce_email?` is false)
|
|
122
|
+
# 3. Marks the invite as accepted
|
|
123
|
+
# 4. Creates the entity membership
|
|
124
|
+
# 5. Notifies the invitable (if present)
|
|
121
125
|
#
|
|
122
126
|
# @param user [Object] the user accepting the invitation
|
|
123
|
-
# @raise [ActiveRecord::RecordInvalid] if acceptance fails
|
|
127
|
+
# @raise [ActiveRecord::RecordInvalid] if acceptance fails (e.g. the
|
|
128
|
+
# invite was already accepted by a concurrent request)
|
|
124
129
|
def accept_for_user!(user)
|
|
125
130
|
validate_email_constraints!(user.email)
|
|
126
131
|
|
|
127
|
-
|
|
132
|
+
with_lock do
|
|
133
|
+
unless pending?
|
|
134
|
+
error =
|
|
135
|
+
if accepted?
|
|
136
|
+
:invite_already_accepted
|
|
137
|
+
elsif cancelled?
|
|
138
|
+
:invite_cancelled
|
|
139
|
+
else
|
|
140
|
+
:invite_expired
|
|
141
|
+
end
|
|
142
|
+
errors.add(:base, error)
|
|
143
|
+
raise ActiveRecord::RecordInvalid.new(self)
|
|
144
|
+
end
|
|
145
|
+
|
|
128
146
|
update!(
|
|
129
147
|
:state => :accepted,
|
|
130
148
|
:accepted_at => Time.current,
|
|
@@ -69,10 +69,15 @@ module Plutonium
|
|
|
69
69
|
::Invites::UserInvite
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
# Override to specify the membership model class
|
|
72
|
+
# Override to specify the membership model class. There is no default
|
|
73
|
+
# because the membership join model is app-specific (e.g. OrganizationUser).
|
|
74
|
+
# Omitting the override raises NotImplementedError so the failure is
|
|
75
|
+
# actionable instead of a confusing NameError on a non-existent constant.
|
|
73
76
|
# @return [Class]
|
|
74
77
|
def membership_class
|
|
75
|
-
|
|
78
|
+
raise NotImplementedError,
|
|
79
|
+
"#{self.class}#membership_class must return the membership model class. " \
|
|
80
|
+
"Specify --membership-model when generating, or override this method."
|
|
76
81
|
end
|
|
77
82
|
|
|
78
83
|
# Override to specify the user model class
|
|
@@ -109,7 +114,7 @@ module Plutonium
|
|
|
109
114
|
|
|
110
115
|
# Override to customize success message
|
|
111
116
|
def success_message
|
|
112
|
-
"
|
|
117
|
+
I18n.t("plutonium.invites.invite_user.sent", email: email)
|
|
113
118
|
end
|
|
114
119
|
|
|
115
120
|
# Override to specify the entity association name on membership
|
|
@@ -141,7 +146,7 @@ module Plutonium
|
|
|
141
146
|
membership_entity_attribute => entity,
|
|
142
147
|
user_association => existing_user
|
|
143
148
|
)
|
|
144
|
-
errors.add(:email,
|
|
149
|
+
errors.add(:email, :already_a_member) if membership
|
|
145
150
|
end
|
|
146
151
|
|
|
147
152
|
def no_pending_invitation
|
|
@@ -152,7 +157,7 @@ module Plutonium
|
|
|
152
157
|
:email => email,
|
|
153
158
|
:state => :pending
|
|
154
159
|
)
|
|
155
|
-
errors.add(:email,
|
|
160
|
+
errors.add(:email, :already_has_pending_invitation) if pending
|
|
156
161
|
end
|
|
157
162
|
|
|
158
163
|
# Override if user association has a different name
|
|
@@ -26,14 +26,14 @@ module Plutonium
|
|
|
26
26
|
extend ActiveSupport::Concern
|
|
27
27
|
|
|
28
28
|
included do
|
|
29
|
-
presents label: "
|
|
29
|
+
presents label: t("plutonium.invites.resend_invite.label"), icon: Phlex::TablerIcons::MailForward
|
|
30
30
|
|
|
31
31
|
attribute :resource
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def execute
|
|
35
35
|
unless resource.pending?
|
|
36
|
-
return failed("
|
|
36
|
+
return failed(I18n.t("plutonium.invites.resend_invite.not_pending"))
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
resource.update!(expires_at: new_expiry)
|
|
@@ -41,7 +41,7 @@ module Plutonium
|
|
|
41
41
|
|
|
42
42
|
succeed(resource).with_message(success_message)
|
|
43
43
|
rescue => error
|
|
44
|
-
failed("
|
|
44
|
+
failed(I18n.t("plutonium.invites.resend_invite.failed", error: error.message))
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
private
|
|
@@ -58,7 +58,7 @@ module Plutonium
|
|
|
58
58
|
|
|
59
59
|
# Override to customize success message
|
|
60
60
|
def success_message
|
|
61
|
-
"
|
|
61
|
+
I18n.t("plutonium.invites.resend_invite.resent", email: resource.email)
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
64
|
end
|
|
@@ -57,7 +57,7 @@ module Plutonium
|
|
|
57
57
|
@invite.validate_email_constraints!(current_user.email)
|
|
58
58
|
render :show
|
|
59
59
|
rescue ActiveRecord::RecordInvalid => e
|
|
60
|
-
@error_title = "
|
|
60
|
+
@error_title = t("plutonium.invites.controller.email_validation_error")
|
|
61
61
|
@error_message = e.record.errors.full_messages.join(", ")
|
|
62
62
|
render :error, status: :forbidden
|
|
63
63
|
end
|
|
@@ -74,7 +74,7 @@ module Plutonium
|
|
|
74
74
|
|
|
75
75
|
unless current_user
|
|
76
76
|
redirect_to invitation_path_for(params[:token]),
|
|
77
|
-
alert: "
|
|
77
|
+
alert: t("plutonium.invites.controller.sign_in_to_accept")
|
|
78
78
|
return
|
|
79
79
|
end
|
|
80
80
|
|
|
@@ -82,9 +82,9 @@ module Plutonium
|
|
|
82
82
|
cookies.delete(:pending_invitation)
|
|
83
83
|
|
|
84
84
|
redirect_to after_accept_path,
|
|
85
|
-
notice: "
|
|
85
|
+
notice: t("plutonium.invites.controller.accepted", entity: @invite.entity.to_label)
|
|
86
86
|
rescue ActiveRecord::RecordInvalid => e
|
|
87
|
-
@error_title = "
|
|
87
|
+
@error_title = t("plutonium.invites.controller.acceptance_error")
|
|
88
88
|
@error_message = e.record.errors.full_messages.join(", ")
|
|
89
89
|
render :error, status: :forbidden
|
|
90
90
|
end
|
|
@@ -112,8 +112,8 @@ module Plutonium
|
|
|
112
112
|
invite = invite_class.find_for_acceptance(token)
|
|
113
113
|
|
|
114
114
|
unless invite
|
|
115
|
-
@error_title = "
|
|
116
|
-
@error_message = "
|
|
115
|
+
@error_title = t("plutonium.invites.controller.invalid_or_expired")
|
|
116
|
+
@error_message = t("plutonium.invites.controller.no_longer_valid")
|
|
117
117
|
render :error, status: :not_found
|
|
118
118
|
return nil
|
|
119
119
|
end
|
|
@@ -131,7 +131,7 @@ module Plutonium
|
|
|
131
131
|
password_confirmation = params[:password_confirmation]
|
|
132
132
|
|
|
133
133
|
if password != password_confirmation
|
|
134
|
-
flash.now[:alert] =
|
|
134
|
+
flash.now[:alert] = t("plutonium.invites.controller.passwords_dont_match")
|
|
135
135
|
render :signup
|
|
136
136
|
return
|
|
137
137
|
end
|
|
@@ -140,7 +140,7 @@ module Plutonium
|
|
|
140
140
|
ActiveRecord::Base.transaction do
|
|
141
141
|
existing_user = user_class.find_by(email: email)
|
|
142
142
|
if existing_user
|
|
143
|
-
flash.now[:alert] = "
|
|
143
|
+
flash.now[:alert] = t("plutonium.invites.controller.account_exists")
|
|
144
144
|
render :signup
|
|
145
145
|
return
|
|
146
146
|
end
|
|
@@ -153,7 +153,7 @@ module Plutonium
|
|
|
153
153
|
sign_in_user(user)
|
|
154
154
|
redirect_to after_accept_path
|
|
155
155
|
else
|
|
156
|
-
flash.now[:alert] = "
|
|
156
|
+
flash.now[:alert] = t("plutonium.invites.controller.create_account_failed")
|
|
157
157
|
render :signup
|
|
158
158
|
end
|
|
159
159
|
end
|
|
@@ -161,11 +161,11 @@ module Plutonium
|
|
|
161
161
|
flash.now[:alert] = if e.record.is_a?(invite_class)
|
|
162
162
|
e.record.errors.full_messages.join(", ")
|
|
163
163
|
else
|
|
164
|
-
"
|
|
164
|
+
t("plutonium.invites.controller.create_account_failed_with_error", error: e.record.errors.full_messages.join(", "))
|
|
165
165
|
end
|
|
166
166
|
render :signup
|
|
167
167
|
rescue => e
|
|
168
|
-
flash.now[:alert] = "
|
|
168
|
+
flash.now[:alert] = t("plutonium.invites.controller.create_account_failed_with_error", error: e.message)
|
|
169
169
|
render :signup
|
|
170
170
|
end
|
|
171
171
|
end
|
|
@@ -12,10 +12,19 @@ module Plutonium
|
|
|
12
12
|
lost: {color: :red, collapsed: true}
|
|
13
13
|
}.freeze
|
|
14
14
|
|
|
15
|
-
attr_reader :key, :
|
|
15
|
+
attr_reader :key, :color, :wip, :scope, :on_enter, :on_exit, :accepts, :actions, :enter_interaction
|
|
16
|
+
|
|
17
|
+
# `label:` if given, else plutonium.kanban_columns.<model>.<key>, else the
|
|
18
|
+
# titleized key. Resolved on every read so it follows the request locale.
|
|
19
|
+
def label
|
|
20
|
+
Plutonium::Translation.resolve(@label) ||
|
|
21
|
+
Plutonium::Translation.label_for(:kanban_column, @resource_class, key) ||
|
|
22
|
+
key.to_s.titleize
|
|
23
|
+
end
|
|
16
24
|
|
|
17
25
|
def initialize(key, label: nil, color: nil, wip: nil, scope: nil, on_enter: nil, on_exit: nil, on_drop: nil,
|
|
18
|
-
collapsed: nil, add: nil, accepts: nil, locked: nil, role: nil, enter_interaction: nil, drop_interaction: nil
|
|
26
|
+
collapsed: nil, add: nil, accepts: nil, locked: nil, role: nil, enter_interaction: nil, drop_interaction: nil,
|
|
27
|
+
resource_class: nil)
|
|
19
28
|
# on_drop:/drop_interaction: were renamed to on_enter:/enter_interaction:.
|
|
20
29
|
# Resolve the deprecated aliases first (dev/test raise; deployed envs warn
|
|
21
30
|
# and map — see resolve_renamed_option) so the rest of initialize only
|
|
@@ -48,7 +57,8 @@ module Plutonium
|
|
|
48
57
|
raise ArgumentError, "kanban column `accepts:` no longer accepts a Proc; use true/false or an Array of source keys, and put record/user conditions in the kanban_move? policy."
|
|
49
58
|
end
|
|
50
59
|
@key = key.to_sym
|
|
51
|
-
@label = label
|
|
60
|
+
@label = label
|
|
61
|
+
@resource_class = resource_class
|
|
52
62
|
@color = color.nil? ? preset[:color] : color
|
|
53
63
|
@wip = wip
|
|
54
64
|
@scope = scope
|
data/lib/plutonium/kanban/dsl.rb
CHANGED
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
module Plutonium
|
|
4
4
|
module Kanban
|
|
5
5
|
class DSL
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
# `resource_class:` lets column labels resolve through the
|
|
7
|
+
# plutonium.kanban_columns.<model>.<key> convention.
|
|
8
|
+
def self.build(resource_class: nil, &block)
|
|
9
|
+
dsl = new(resource_class:)
|
|
8
10
|
dsl.instance_eval(&block) if block
|
|
9
11
|
dsl.to_board
|
|
10
12
|
end
|
|
11
13
|
|
|
12
|
-
def initialize
|
|
14
|
+
def initialize(resource_class: nil)
|
|
15
|
+
@resource_class = resource_class
|
|
13
16
|
@columns = []
|
|
14
17
|
@columns_block = nil
|
|
15
18
|
@card_fields = nil
|
|
@@ -24,7 +27,7 @@ module Plutonium
|
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
def column(key, **opts, &blk)
|
|
27
|
-
col = Column.new(key, **opts)
|
|
30
|
+
col = Column.new(key, resource_class: @resource_class, **opts)
|
|
28
31
|
col.instance_eval(&blk) if blk
|
|
29
32
|
@columns << col
|
|
30
33
|
end
|
|
@@ -4,46 +4,34 @@ module Plutonium
|
|
|
4
4
|
module Profile
|
|
5
5
|
# Renders security settings links based on enabled Rodauth features.
|
|
6
6
|
class SecuritySection < Plutonium::UI::Component::Base
|
|
7
|
+
# Labels and descriptions come from
|
|
8
|
+
# plutonium.profile.security_section.features.<feature>.{label,description}.
|
|
7
9
|
FEATURES = {
|
|
8
10
|
change_password: {
|
|
9
|
-
label: "Change Password",
|
|
10
|
-
description: "Update your account password",
|
|
11
11
|
icon: Phlex::TablerIcons::Key,
|
|
12
12
|
path_method: :change_password_path
|
|
13
13
|
},
|
|
14
14
|
change_login: {
|
|
15
|
-
label: "Change Email",
|
|
16
|
-
description: "Update your email address",
|
|
17
15
|
icon: Phlex::TablerIcons::Mail,
|
|
18
16
|
path_method: :change_login_path
|
|
19
17
|
},
|
|
20
18
|
otp: {
|
|
21
|
-
label: "Two-Factor Authentication",
|
|
22
|
-
description: "Add an extra layer of security",
|
|
23
19
|
icon: Phlex::TablerIcons::DeviceMobile,
|
|
24
20
|
path_method: :otp_setup_path
|
|
25
21
|
},
|
|
26
22
|
recovery_codes: {
|
|
27
|
-
label: "Recovery Codes",
|
|
28
|
-
description: "View or regenerate backup codes",
|
|
29
23
|
icon: Phlex::TablerIcons::FileCode,
|
|
30
24
|
path_method: :recovery_codes_path
|
|
31
25
|
},
|
|
32
26
|
webauthn: {
|
|
33
|
-
label: "Security Keys",
|
|
34
|
-
description: "Manage passkeys and security keys",
|
|
35
27
|
icon: Phlex::TablerIcons::Fingerprint,
|
|
36
28
|
path_method: :webauthn_setup_path
|
|
37
29
|
},
|
|
38
30
|
active_sessions: {
|
|
39
|
-
label: "Active Sessions",
|
|
40
|
-
description: "View and manage your sessions",
|
|
41
31
|
icon: Phlex::TablerIcons::DevicesCheck,
|
|
42
32
|
path_method: :active_sessions_path
|
|
43
33
|
},
|
|
44
34
|
close_account: {
|
|
45
|
-
label: "Close Account",
|
|
46
|
-
description: "Permanently delete your account",
|
|
47
35
|
icon: Phlex::TablerIcons::Trash,
|
|
48
36
|
path_method: :close_account_path,
|
|
49
37
|
danger: true
|
|
@@ -61,8 +49,8 @@ module Plutonium
|
|
|
61
49
|
|
|
62
50
|
def render_section_header
|
|
63
51
|
div(class: "mb-4") do
|
|
64
|
-
h2(class: "text-lg font-semibold text-[var(--pu-text)]") { "
|
|
65
|
-
p(class: "text-sm text-[var(--pu-text-muted)]") { "
|
|
52
|
+
h2(class: "text-lg font-semibold text-[var(--pu-text)]") { t("plutonium.profile.security_section.title") }
|
|
53
|
+
p(class: "text-sm text-[var(--pu-text-muted)]") { t("plutonium.profile.security_section.description") }
|
|
66
54
|
end
|
|
67
55
|
end
|
|
68
56
|
|
|
@@ -95,8 +83,8 @@ module Plutonium
|
|
|
95
83
|
|
|
96
84
|
# Content
|
|
97
85
|
div(class: "flex-grow") do
|
|
98
|
-
div(class: "font-medium") {
|
|
99
|
-
div(class: "text-sm text-[var(--pu-text-muted)]") {
|
|
86
|
+
div(class: "font-medium") { feature_text(feature, :label) }
|
|
87
|
+
div(class: "text-sm text-[var(--pu-text-muted)]") { feature_text(feature, :description) }
|
|
100
88
|
end
|
|
101
89
|
|
|
102
90
|
# Arrow
|
|
@@ -106,6 +94,10 @@ module Plutonium
|
|
|
106
94
|
end
|
|
107
95
|
end
|
|
108
96
|
|
|
97
|
+
def feature_text(feature, slot)
|
|
98
|
+
t("plutonium.profile.security_section.features.#{feature}.#{slot}")
|
|
99
|
+
end
|
|
100
|
+
|
|
109
101
|
def enabled_features
|
|
110
102
|
FEATURES.select { |feature, _config| feature_enabled?(feature) }
|
|
111
103
|
end
|
|
@@ -19,10 +19,19 @@ module Plutonium
|
|
|
19
19
|
|
|
20
20
|
attr_reader :resource_class
|
|
21
21
|
|
|
22
|
-
def initialize(key:, resource_class: nil)
|
|
22
|
+
def initialize(key:, resource_class: nil, label: nil)
|
|
23
23
|
super()
|
|
24
24
|
@key = key
|
|
25
25
|
@resource_class = resource_class
|
|
26
|
+
@label = label
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# The filter's display label: `label:` if given, else the
|
|
30
|
+
# plutonium.filters.<model>.<key> convention, else the humanized key.
|
|
31
|
+
def label
|
|
32
|
+
Plutonium::Translation.resolve(@label) ||
|
|
33
|
+
Plutonium::Translation.label_for(:filter, resource_class, key) ||
|
|
34
|
+
key.to_s.humanize
|
|
26
35
|
end
|
|
27
36
|
end
|
|
28
37
|
end
|
|
@@ -27,7 +27,14 @@ module Plutonium
|
|
|
27
27
|
return "" if value.blank?
|
|
28
28
|
ids = decode_ids(value)
|
|
29
29
|
return "" if ids.empty?
|
|
30
|
-
|
|
30
|
+
# Resolve labels through the same scope-honouring relation that the
|
|
31
|
+
# dropdown (ResourceSelect#authorized_relation) and the typeahead
|
|
32
|
+
# (Typeahead#filter_association) already enforce. A bare
|
|
33
|
+
# `association_class.where(id: ids)` here would widen past the
|
|
34
|
+
# configured `scope:` and let a crafted `?q[<key>][value]=<out-of-scope
|
|
35
|
+
# id>` render the hidden record's `to_label` into the active-filter
|
|
36
|
+
# pill even though the matching filter was dropped at submission.
|
|
37
|
+
records = scoped_relation.where(id: ids)
|
|
31
38
|
records.map { |r| r.respond_to?(:to_label) ? r.to_label : r.to_s }.join(", ")
|
|
32
39
|
rescue
|
|
33
40
|
Array(value).reject(&:blank?).join(", ")
|
|
@@ -45,7 +52,8 @@ module Plutonium
|
|
|
45
52
|
as: :resource_select,
|
|
46
53
|
association_class: association_class,
|
|
47
54
|
multiple: @multiple,
|
|
48
|
-
include_blank: @multiple ? false : "
|
|
55
|
+
include_blank: @multiple ? false : Plutonium::Translation.t("plutonium.query.all"),
|
|
56
|
+
scope: @scope_proc
|
|
49
57
|
end
|
|
50
58
|
|
|
51
59
|
private
|
|
@@ -86,6 +94,23 @@ module Plutonium
|
|
|
86
94
|
def infer_class_from_key
|
|
87
95
|
key.to_s.classify.constantize
|
|
88
96
|
end
|
|
97
|
+
|
|
98
|
+
# The relation used to resolve `humanize_value` labels, narrowed by the
|
|
99
|
+
# configured `scope:` exactly like ResourceSelect#apply_scope and
|
|
100
|
+
# Typeahead#apply_scope. The arity dispatch — Symbol, zero-arg Proc
|
|
101
|
+
# (kanban form `-> { ... }`), one-arg Proc (documented form
|
|
102
|
+
# `->(s) { s.active }`), nil — must stay in lock-step with those two
|
|
103
|
+
# sites; any divergence re-opens the leak this method closes. Unsupported
|
|
104
|
+
# scope types raise ArgumentError loudly rather than silently widening.
|
|
105
|
+
def scoped_relation
|
|
106
|
+
relation = association_class.all
|
|
107
|
+
case @scope_proc
|
|
108
|
+
when Symbol then relation.public_send(@scope_proc)
|
|
109
|
+
when Proc then @scope_proc.arity.zero? ? relation.instance_exec(&@scope_proc) : @scope_proc.call(relation)
|
|
110
|
+
when nil then relation
|
|
111
|
+
else raise ArgumentError, "Unsupported association scope: #{@scope_proc.inspect} (expected Symbol, Proc, or nil)"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
89
114
|
end
|
|
90
115
|
end
|
|
91
116
|
end
|
|
@@ -10,10 +10,10 @@ module Plutonium
|
|
|
10
10
|
# filter :published, with: :boolean, true_label: "Published", false_label: "Draft"
|
|
11
11
|
#
|
|
12
12
|
class Boolean < Filter
|
|
13
|
-
def initialize(true_label:
|
|
13
|
+
def initialize(true_label: nil, false_label: nil, **)
|
|
14
14
|
super(**)
|
|
15
|
-
@true_label = true_label
|
|
16
|
-
@false_label = false_label
|
|
15
|
+
@true_label = true_label || Plutonium::Translation.t("plutonium.boolean.true")
|
|
16
|
+
@false_label = false_label || Plutonium::Translation.t("plutonium.boolean.false")
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def humanize_value(value)
|
|
@@ -32,7 +32,7 @@ module Plutonium
|
|
|
32
32
|
input :value,
|
|
33
33
|
as: :select,
|
|
34
34
|
choices: [[@true_label, "true"], [@false_label, "false"]],
|
|
35
|
-
include_blank: "
|
|
35
|
+
include_blank: Plutonium::Translation.t("plutonium.query.all")
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -73,23 +73,10 @@ module Plutonium
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def generate_placeholder
|
|
76
|
-
base =
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
when :not_eq
|
|
81
|
-
"#{base} not on..."
|
|
82
|
-
when :lt
|
|
83
|
-
"#{base} before..."
|
|
84
|
-
when :lteq
|
|
85
|
-
"#{base} on or before..."
|
|
86
|
-
when :gt
|
|
87
|
-
"#{base} after..."
|
|
88
|
-
when :gteq
|
|
89
|
-
"#{base} on or after..."
|
|
90
|
-
else
|
|
91
|
-
base
|
|
92
|
-
end
|
|
76
|
+
base = label
|
|
77
|
+
return base if @predicate == :eq
|
|
78
|
+
|
|
79
|
+
Plutonium::Translation.t("plutonium.query.filters.date.#{@predicate}", label: base)
|
|
93
80
|
end
|
|
94
81
|
end
|
|
95
82
|
end
|
|
@@ -34,8 +34,8 @@ module Plutonium
|
|
|
34
34
|
def customize_inputs
|
|
35
35
|
input :from, as: :date
|
|
36
36
|
input :to, as: :date
|
|
37
|
-
field :from, placeholder: @from_label || "
|
|
38
|
-
field :to, placeholder: @to_label || "
|
|
37
|
+
field :from, placeholder: @from_label || Plutonium::Translation.t("plutonium.query.filters.date_range.from", label: label)
|
|
38
|
+
field :to, placeholder: @to_label || Plutonium::Translation.t("plutonium.query.filters.date_range.to", label: label)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
private
|
|
@@ -29,12 +29,21 @@ module Plutonium
|
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
# Active-filter pill text: each chosen value through the enum-value
|
|
33
|
+
# convention (activerecord.attributes.<model>.<key>/<value> or
|
|
34
|
+
# plutonium.values.<model>.<key>.<value>), else the raw value.
|
|
35
|
+
def humanize_value(value)
|
|
36
|
+
Array(value).reject(&:blank?).map do |choice|
|
|
37
|
+
Plutonium::Translation.value_label(resource_class, key, choice) || choice.to_s
|
|
38
|
+
end.join(", ")
|
|
39
|
+
end
|
|
40
|
+
|
|
32
41
|
def customize_inputs
|
|
33
42
|
input :value,
|
|
34
43
|
as: :select,
|
|
35
44
|
choices: resolved_choices,
|
|
36
45
|
multiple: @multiple,
|
|
37
|
-
include_blank: @multiple ? false : "
|
|
46
|
+
include_blank: @multiple ? false : Plutonium::Translation.t("plutonium.query.all")
|
|
38
47
|
end
|
|
39
48
|
|
|
40
49
|
private
|
|
@@ -52,19 +52,16 @@ module Plutonium
|
|
|
52
52
|
private
|
|
53
53
|
|
|
54
54
|
def generate_placeholder
|
|
55
|
-
base =
|
|
56
|
-
case @predicate
|
|
57
|
-
when :matches, :not_matches
|
|
58
|
-
|
|
59
|
-
when :
|
|
60
|
-
|
|
61
|
-
when :ends_with
|
|
62
|
-
"#{base} ends with..."
|
|
63
|
-
when :contains, :not_contains
|
|
64
|
-
"#{base} contains..."
|
|
65
|
-
else
|
|
66
|
-
base
|
|
55
|
+
base = label
|
|
56
|
+
phrase = case @predicate
|
|
57
|
+
when :matches, :not_matches then :matches
|
|
58
|
+
when :starts_with then :starts_with
|
|
59
|
+
when :ends_with then :ends_with
|
|
60
|
+
when :contains, :not_contains then :contains
|
|
67
61
|
end
|
|
62
|
+
return base unless phrase
|
|
63
|
+
|
|
64
|
+
Plutonium::Translation.t("plutonium.query.filters.text.#{phrase}", label: base)
|
|
68
65
|
end
|
|
69
66
|
|
|
70
67
|
def sanitize_like(string)
|
data/lib/plutonium/railtie.rb
CHANGED
|
@@ -16,6 +16,7 @@ module Plutonium
|
|
|
16
16
|
# end
|
|
17
17
|
PRECOMPILE_ASSETS = %w[
|
|
18
18
|
plutonium.js plutonium.js.map plutonium.min.js plutonium.min.js.map
|
|
19
|
+
plutonium-charts.js plutonium-charts.js.map plutonium-charts.min.js plutonium-charts.min.js.map
|
|
19
20
|
plutonium.css plutonium.png plutonium.ico
|
|
20
21
|
].freeze
|
|
21
22
|
|
|
@@ -25,6 +26,8 @@ module Plutonium
|
|
|
25
26
|
|
|
26
27
|
initializer "plutonium.rescue_responses", before: "action_dispatch.configure" do |app|
|
|
27
28
|
app.config.action_dispatch.rescue_responses["ActionPolicy::Unauthorized"] = :forbidden
|
|
29
|
+
app.config.action_dispatch.rescue_responses["Plutonium::Dashboard::UnknownCardError"] = :not_found
|
|
30
|
+
app.config.action_dispatch.rescue_responses["Plutonium::Dashboard::UnknownDashboardError"] = :not_found
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
initializer "plutonium.deprecator" do |app|
|
|
@@ -39,6 +42,19 @@ module Plutonium
|
|
|
39
42
|
load_plutonium_initializers
|
|
40
43
|
end
|
|
41
44
|
|
|
45
|
+
# Plutonium is a Railtie, not an Engine, so it gets no config/locales path
|
|
46
|
+
# for free. Register it the way Rails::Engine#add_locales does: through
|
|
47
|
+
# railties_load_path, which the I18n railtie unshifts ahead of the app's
|
|
48
|
+
# own locales in registration order. Registering before every engine's
|
|
49
|
+
# add_locales puts the gem's strings at the lowest precedence: a package or
|
|
50
|
+
# portal engine's config/locales overrides them, and the host app overrides
|
|
51
|
+
# both.
|
|
52
|
+
initializer "plutonium.i18n", before: :add_locales do
|
|
53
|
+
locales = Rails::Paths::Root.new(Plutonium.root.to_s)
|
|
54
|
+
locales.add "config/locales", glob: "**/*.{rb,yml}"
|
|
55
|
+
config.i18n.railties_load_path << locales["config/locales"]
|
|
56
|
+
end
|
|
57
|
+
|
|
42
58
|
initializer "plutonium.register_migrations" do
|
|
43
59
|
Plutonium::Migrations.register(:wizards, Plutonium.root.join("db/migrate/wizard").to_s)
|
|
44
60
|
Plutonium::Migrations.register(:async_interactions, Plutonium.root.join("db/migrate/async_interactions").to_s)
|
|
@@ -131,6 +147,7 @@ module Plutonium
|
|
|
131
147
|
def extend_action_dispatch
|
|
132
148
|
ActionDispatch::Routing::Mapper.prepend Plutonium::Routing::MapperExtensions
|
|
133
149
|
ActionDispatch::Routing::Mapper.prepend Plutonium::Routing::WizardRegistration
|
|
150
|
+
ActionDispatch::Routing::Mapper.prepend Plutonium::Routing::DashboardRegistration
|
|
134
151
|
ActionDispatch::Routing::RouteSet.prepend Plutonium::Routing::RouteSetExtensions
|
|
135
152
|
Rails::Engine.include Plutonium::Routing::ResourceRegistration
|
|
136
153
|
end
|