plutonium 0.41.1 → 0.42.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/CHANGELOG.md +25 -0
- data/app/assets/plutonium.css +2 -2
- data/app/assets/plutonium.js +46 -1
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +32 -32
- data/app/assets/plutonium.min.js.map +4 -4
- data/docs/guides/user-invites.md +1 -1
- data/docs/reference/generators/index.md +43 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/invites/templates/app/interactions/invite_user_interaction.rb.tt +2 -0
- data/lib/generators/pu/invites/templates/app/interactions/user_invite_user_interaction.rb.tt +3 -1
- data/lib/generators/pu/invites/templates/invitable/invite_user_interaction.rb.tt +3 -1
- data/lib/generators/pu/lib/plutonium_generators/concerns/package_selector.rb +1 -1
- data/lib/generators/pu/rodauth/account_generator.rb +20 -5
- data/lib/generators/pu/rodauth/admin_generator.rb +1 -1
- data/lib/generators/pu/rodauth/concerns/configuration.rb +1 -0
- data/lib/generators/pu/rodauth/concerns/gem_helpers.rb +19 -0
- data/lib/generators/pu/rodauth/install_generator.rb +7 -3
- data/lib/generators/pu/rodauth/migration/active_record/base.erb +4 -4
- data/lib/generators/pu/rodauth/migration_generator.rb +7 -0
- data/lib/generators/pu/rodauth/templates/app/models/account.rb.tt +1 -1
- data/lib/generators/pu/saas/USAGE +10 -1
- data/lib/generators/pu/saas/api_client/USAGE +32 -0
- data/lib/generators/pu/saas/api_client/templates/app/interactions/create_interaction.rb.tt +80 -0
- data/lib/generators/pu/saas/api_client/templates/app/interactions/disable_interaction.rb.tt +15 -0
- data/lib/generators/pu/saas/api_client/templates/lib/tasks/api_client.rake.tt +48 -0
- data/lib/generators/pu/saas/api_client_generator.rb +254 -0
- data/lib/generators/pu/saas/entity_generator.rb +5 -3
- data/lib/generators/pu/saas/membership_generator.rb +21 -9
- data/lib/generators/pu/saas/setup_generator.rb +23 -0
- data/lib/plutonium/api_client/concerns/create_api_client.rb +256 -0
- data/lib/plutonium/api_client/concerns/disable_api_client.rb +64 -0
- data/lib/plutonium/api_client.rb +21 -0
- data/lib/plutonium/interaction/concerns/scoping.rb +68 -0
- data/lib/plutonium/interaction/response/render.rb +16 -1
- data/lib/plutonium/invites/concerns/invite_user.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- data/src/js/controllers/clipboard_controller.js +37 -0
- data/src/js/controllers/register_controllers.js +2 -0
- data/src/js/controllers/remote_modal_controller.js +18 -4
- metadata +13 -2
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module ApiClient
|
|
5
|
+
module Concerns
|
|
6
|
+
# DisableApiClient provides the core logic for disabling API client accounts.
|
|
7
|
+
#
|
|
8
|
+
# Include this in your DisableInteraction and implement the required methods.
|
|
9
|
+
#
|
|
10
|
+
# @example Basic usage
|
|
11
|
+
# class ApiClient::DisableInteraction < Plutonium::Resource::Interaction
|
|
12
|
+
# include Plutonium::ApiClient::Concerns::DisableApiClient
|
|
13
|
+
#
|
|
14
|
+
# def rodauth_name
|
|
15
|
+
# :api_client
|
|
16
|
+
# end
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
module DisableApiClient
|
|
20
|
+
extend ActiveSupport::Concern
|
|
21
|
+
|
|
22
|
+
included do
|
|
23
|
+
presents label: "Disable",
|
|
24
|
+
description: "Disable this API client (cannot be undone)",
|
|
25
|
+
icon: Phlex::TablerIcons::Ban,
|
|
26
|
+
color: :danger
|
|
27
|
+
|
|
28
|
+
attribute :resource
|
|
29
|
+
|
|
30
|
+
validates :resource, presence: true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def execute
|
|
34
|
+
login = resource.login
|
|
35
|
+
|
|
36
|
+
rodauth_instance.close_account(account_login: login)
|
|
37
|
+
|
|
38
|
+
succeed(resource).with_message(success_message(login))
|
|
39
|
+
rescue => e
|
|
40
|
+
failed(base: e.message)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# Override to specify the Rodauth configuration name
|
|
46
|
+
# @return [Symbol]
|
|
47
|
+
def rodauth_name
|
|
48
|
+
raise NotImplementedError, "#{self.class}#rodauth_name must return the Rodauth configuration name (e.g., :api_client)"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Override to customize success message
|
|
52
|
+
# @param login [String] the login of the disabled API client
|
|
53
|
+
# @return [String]
|
|
54
|
+
def success_message(login)
|
|
55
|
+
"API client '#{login}' has been disabled"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def rodauth_instance
|
|
59
|
+
RodauthApp.rodauth(rodauth_name)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
# ApiClient module provides concerns for building API client account interactions.
|
|
5
|
+
#
|
|
6
|
+
# @example Creating an API client interaction
|
|
7
|
+
# class ApiClient::CreateInteraction < Plutonium::Resource::Interaction
|
|
8
|
+
# include Plutonium::ApiClient::Concerns::CreateApiClient
|
|
9
|
+
#
|
|
10
|
+
# def rodauth_name
|
|
11
|
+
# :api_client
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# def api_client_class
|
|
15
|
+
# ApiClient
|
|
16
|
+
# end
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
module ApiClient
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module Interaction
|
|
5
|
+
module Concerns
|
|
6
|
+
# Scoping concern provides access to scoped records from the controller context.
|
|
7
|
+
#
|
|
8
|
+
# This handles both:
|
|
9
|
+
# - Entity scoping: Portal-level multi-tenancy via `scope_to_entity` (accessed via `current_scoped_entity`)
|
|
10
|
+
# - Parent scoping: Nested routes (accessed via `current_parent`)
|
|
11
|
+
#
|
|
12
|
+
# The `scoped_record_of_type` method checks both contexts and ensures type safety.
|
|
13
|
+
#
|
|
14
|
+
# @example Using in an interaction
|
|
15
|
+
# class MyInteraction < Plutonium::Resource::Interaction
|
|
16
|
+
# include Plutonium::Interaction::Concerns::Scoping
|
|
17
|
+
#
|
|
18
|
+
# def execute
|
|
19
|
+
# organization = scoped_record_of_type(Organization)
|
|
20
|
+
# # Returns the Organization from either entity or parent scope
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
module Scoping
|
|
25
|
+
extend ActiveSupport::Concern
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
# Returns a scoped record that matches the expected type.
|
|
30
|
+
#
|
|
31
|
+
# Checks both entity scoping (`current_scoped_entity`) and parent scoping (`current_parent`),
|
|
32
|
+
# returning the first match that is an instance of the specified class.
|
|
33
|
+
#
|
|
34
|
+
# @param klass [Class] the expected model class
|
|
35
|
+
# @return [Object, nil] the scoped record if found and type matches, nil otherwise
|
|
36
|
+
def scoped_record_of_type(klass)
|
|
37
|
+
[current_scoped_entity, current_parent].find { |record| record.is_a?(klass) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns the parent record from the controller (nested routes).
|
|
41
|
+
#
|
|
42
|
+
# @return [Object, nil] the current parent or nil
|
|
43
|
+
def current_parent
|
|
44
|
+
view_context.controller.current_parent
|
|
45
|
+
rescue NoMethodError
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Returns the entity record from the controller (portal multi-tenancy).
|
|
50
|
+
#
|
|
51
|
+
# @return [Object, nil] the current scoped entity or nil
|
|
52
|
+
def current_scoped_entity
|
|
53
|
+
view_context.controller.current_scoped_entity
|
|
54
|
+
rescue NoMethodError
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Returns the appropriate parent for URL generation.
|
|
59
|
+
# Prefers entity scope over parent scope.
|
|
60
|
+
#
|
|
61
|
+
# @return [Object, nil] the entity or parent, whichever is available
|
|
62
|
+
def scoped_parent
|
|
63
|
+
current_scoped_entity || current_parent
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -12,7 +12,22 @@ module Plutonium
|
|
|
12
12
|
# @param controller [ActionController::Base] The controller instance.
|
|
13
13
|
# @return [void]
|
|
14
14
|
def execute(controller)
|
|
15
|
-
|
|
15
|
+
render_args = @args
|
|
16
|
+
render_options = @options
|
|
17
|
+
|
|
18
|
+
controller.instance_eval do
|
|
19
|
+
respond_to do |format|
|
|
20
|
+
format.turbo_stream do
|
|
21
|
+
# For Turbo requests, replace the form with the rendered content
|
|
22
|
+
render turbo_stream: turbo_stream.replace(
|
|
23
|
+
"interaction-form",
|
|
24
|
+
view_context.render(*render_args, **render_options)
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
format.any { render(*render_args, **render_options) }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
16
31
|
end
|
|
17
32
|
end
|
|
18
33
|
end
|
data/lib/plutonium/version.rb
CHANGED
data/package.json
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["source"]
|
|
5
|
+
|
|
6
|
+
copy(event) {
|
|
7
|
+
const text = this.sourceTarget.value || this.sourceTarget.textContent
|
|
8
|
+
const button = event.currentTarget
|
|
9
|
+
const originalText = button.textContent
|
|
10
|
+
|
|
11
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
12
|
+
button.textContent = "Copied!"
|
|
13
|
+
setTimeout(() => {
|
|
14
|
+
button.textContent = originalText
|
|
15
|
+
}, 2000)
|
|
16
|
+
}).catch((err) => {
|
|
17
|
+
// Fallback for browsers that don't support clipboard API
|
|
18
|
+
console.warn("Clipboard API failed, using fallback:", err)
|
|
19
|
+
this.fallbackCopy(text)
|
|
20
|
+
button.textContent = "Copied!"
|
|
21
|
+
setTimeout(() => {
|
|
22
|
+
button.textContent = originalText
|
|
23
|
+
}, 2000)
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
fallbackCopy(text) {
|
|
28
|
+
const textarea = document.createElement("textarea")
|
|
29
|
+
textarea.value = text
|
|
30
|
+
textarea.style.position = "fixed"
|
|
31
|
+
textarea.style.opacity = "0"
|
|
32
|
+
document.body.appendChild(textarea)
|
|
33
|
+
textarea.select()
|
|
34
|
+
document.execCommand("copy")
|
|
35
|
+
document.body.removeChild(textarea)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -23,6 +23,7 @@ import KeyValueStoreController from "./key_value_store_controller.js"
|
|
|
23
23
|
import BulkActionsController from "./bulk_actions_controller.js"
|
|
24
24
|
import FilterPanelController from "./filter_panel_controller.js"
|
|
25
25
|
import TextareaAutogrowController from "./textarea_autogrow_controller.js"
|
|
26
|
+
import ClipboardController from "./clipboard_controller.js"
|
|
26
27
|
|
|
27
28
|
export default function (application) {
|
|
28
29
|
// Register controllers here
|
|
@@ -50,4 +51,5 @@ export default function (application) {
|
|
|
50
51
|
application.register("bulk-actions", BulkActionsController)
|
|
51
52
|
application.register("filter-panel", FilterPanelController)
|
|
52
53
|
application.register("textarea-autogrow", TextareaAutogrowController)
|
|
54
|
+
application.register("clipboard", ClipboardController)
|
|
53
55
|
}
|
|
@@ -3,8 +3,13 @@ import { Controller } from "@hotwired/stimulus";
|
|
|
3
3
|
// Connects to data-controller="remote-modal"
|
|
4
4
|
export default class extends Controller {
|
|
5
5
|
connect() {
|
|
6
|
-
// Store original scroll position
|
|
6
|
+
// Store original scroll position and body overflow
|
|
7
7
|
this.originalScrollPosition = window.scrollY;
|
|
8
|
+
this.originalOverflow = document.body.style.overflow;
|
|
9
|
+
this.bodyStateRestored = false;
|
|
10
|
+
|
|
11
|
+
// Lock body scroll
|
|
12
|
+
document.body.style.overflow = "hidden";
|
|
8
13
|
|
|
9
14
|
// Show the modal
|
|
10
15
|
this.element.showModal();
|
|
@@ -15,17 +20,26 @@ export default class extends Controller {
|
|
|
15
20
|
close() {
|
|
16
21
|
// Close the modal
|
|
17
22
|
this.element.close();
|
|
18
|
-
|
|
19
|
-
window.scrollTo(0, this.originalScrollPosition);
|
|
23
|
+
this.restoreBodyState();
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
disconnect() {
|
|
23
27
|
// Clean up event listener when controller is disconnected
|
|
24
28
|
this.element.removeEventListener("close", this.handleClose);
|
|
29
|
+
this.restoreBodyState();
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
handleClose() {
|
|
28
|
-
|
|
33
|
+
this.restoreBodyState();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
restoreBodyState() {
|
|
37
|
+
if (this.bodyStateRestored) return;
|
|
38
|
+
this.bodyStateRestored = true;
|
|
39
|
+
|
|
40
|
+
// Restore body overflow
|
|
41
|
+
document.body.style.overflow = this.originalOverflow || "";
|
|
42
|
+
// Restore the original scroll position
|
|
29
43
|
window.scrollTo(0, this.originalScrollPosition);
|
|
30
44
|
}
|
|
31
45
|
}
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: plutonium
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.42.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefan Froelich
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-02-
|
|
11
|
+
date: 2026-02-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: zeitwerk
|
|
@@ -745,6 +745,7 @@ files:
|
|
|
745
745
|
- lib/generators/pu/rodauth/concerns/account_selector.rb
|
|
746
746
|
- lib/generators/pu/rodauth/concerns/configuration.rb
|
|
747
747
|
- lib/generators/pu/rodauth/concerns/feature_selector.rb
|
|
748
|
+
- lib/generators/pu/rodauth/concerns/gem_helpers.rb
|
|
748
749
|
- lib/generators/pu/rodauth/install_generator.rb
|
|
749
750
|
- lib/generators/pu/rodauth/migration/active_record/account_expiration.erb
|
|
750
751
|
- lib/generators/pu/rodauth/migration/active_record/active_sessions.erb
|
|
@@ -799,6 +800,11 @@ files:
|
|
|
799
800
|
- lib/generators/pu/rodauth/templates/lib/tasks/rodauth_admin.rake.tt
|
|
800
801
|
- lib/generators/pu/rodauth/views_generator.rb
|
|
801
802
|
- lib/generators/pu/saas/USAGE
|
|
803
|
+
- lib/generators/pu/saas/api_client/USAGE
|
|
804
|
+
- lib/generators/pu/saas/api_client/templates/app/interactions/create_interaction.rb.tt
|
|
805
|
+
- lib/generators/pu/saas/api_client/templates/app/interactions/disable_interaction.rb.tt
|
|
806
|
+
- lib/generators/pu/saas/api_client/templates/lib/tasks/api_client.rake.tt
|
|
807
|
+
- lib/generators/pu/saas/api_client_generator.rb
|
|
802
808
|
- lib/generators/pu/saas/entity/USAGE
|
|
803
809
|
- lib/generators/pu/saas/entity_generator.rb
|
|
804
810
|
- lib/generators/pu/saas/membership/USAGE
|
|
@@ -825,6 +831,9 @@ files:
|
|
|
825
831
|
- lib/plutonium/action/route_options.rb
|
|
826
832
|
- lib/plutonium/action/simple.rb
|
|
827
833
|
- lib/plutonium/action_policy/sti_policy_lookup.rb
|
|
834
|
+
- lib/plutonium/api_client.rb
|
|
835
|
+
- lib/plutonium/api_client/concerns/create_api_client.rb
|
|
836
|
+
- lib/plutonium/api_client/concerns/disable_api_client.rb
|
|
828
837
|
- lib/plutonium/auth.rb
|
|
829
838
|
- lib/plutonium/auth/public.rb
|
|
830
839
|
- lib/plutonium/auth/rodauth.rb
|
|
@@ -859,6 +868,7 @@ files:
|
|
|
859
868
|
- lib/plutonium/helpers/turbo_stream_actions_helper.rb
|
|
860
869
|
- lib/plutonium/interaction/README.md
|
|
861
870
|
- lib/plutonium/interaction/base.rb
|
|
871
|
+
- lib/plutonium/interaction/concerns/scoping.rb
|
|
862
872
|
- lib/plutonium/interaction/concerns/workflow_dsl.rb
|
|
863
873
|
- lib/plutonium/interaction/nested_attributes.rb
|
|
864
874
|
- lib/plutonium/interaction/outcome.rb
|
|
@@ -1023,6 +1033,7 @@ files:
|
|
|
1023
1033
|
- src/js/controllers/attachment_preview_container_controller.js
|
|
1024
1034
|
- src/js/controllers/attachment_preview_controller.js
|
|
1025
1035
|
- src/js/controllers/bulk_actions_controller.js
|
|
1036
|
+
- src/js/controllers/clipboard_controller.js
|
|
1026
1037
|
- src/js/controllers/color_mode_controller.js
|
|
1027
1038
|
- src/js/controllers/easymde_controller.js
|
|
1028
1039
|
- src/js/controllers/filter_panel_controller.js
|