DEV: Allow plugins to hook into user preferences update process on the server (#21737)

This commit introduces a new `within_user_updater_transaction` event that's triggered inside the transaction that saves user updates in `UserUpdater`. Plugins can hook into the transaction using the event to include custom changes in the transaction. Callbacks for this event receive 2 arguments:

1. the user being saved
2. the changed attributes that are passed to `UserUpdater`.

There's also new modifier in this commit called `users_controller_update_user_params` to allow plugins to allowlist custom params in the `UsersController` which eventually end up getting passed as attributes to the `UserUpdater` and the new `within_user_updater_transaction` event where they can be used to perform additional updates using the custom params.

-----

New API is used in https://github.com/discourse/discourse-mailinglist-integration/pull/1.
This commit is contained in:
Osama Sayegh 2023-05-26 03:26:38 +03:00 committed by GitHub
parent 9268bb92bd
commit bb3c05ba0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 1 deletions

View File

@ -2015,7 +2015,13 @@ class UsersController < ApplicationController
end
deprecate_modify_user_params_method
modify_user_params(result)
result = modify_user_params(result)
DiscoursePluginRegistry.apply_modifier(
:users_controller_update_user_params,
result,
current_user,
params,
)
end
# Plugins can use this to modify user parameters

View File

@ -237,6 +237,7 @@ class UserUpdater
attributes.fetch(:name) { "" },
)
end
DiscourseEvent.trigger(:within_user_updater_transaction, user, attributes)
rescue Addressable::URI::InvalidURIError => e
# Prevent 500 for crazy url input
return saved

View File

@ -2973,6 +2973,32 @@ RSpec.describe UsersController do
end
end
end
context "when a plugin introduces a users_controller_update_user_params modifier" do
before { sign_in(user) }
after { DiscoursePluginRegistry.clear_modifiers! }
it "allows the plugin to modify the user params" do
block_called = false
plugin = Plugin::Instance.new
plugin.register_modifier(
:users_controller_update_user_params,
) do |result, current_user, params|
block_called = true
expect(current_user.id).to eq(user.id)
result[:location] = params[:plugin_location_alias]
result
end
put "/u/#{user.username}.json", params: { location: "abc", plugin_location_alias: "xyz" }
expect(response.status).to eq(200)
expect(user.reload.user_profile.location).to eq("xyz")
expect(block_called).to eq(true)
end
end
end
describe "#badge_title" do

View File

@ -46,6 +46,42 @@ RSpec.describe UserUpdater do
expect(user.reload.name).to eq "Jim Tom"
end
describe "the within_user_updater_transaction event" do
it "allows plugins to perform additional updates" do
update_attributes = { name: "Jimmmy Johnny" }
handler =
Proc.new do |user, attrs|
user.user_profile.update!(bio_raw: "hello world I'm Jimmmy")
expect(attrs).to eq(update_attributes)
end
DiscourseEvent.on(:within_user_updater_transaction, &handler)
updater = UserUpdater.new(user, user)
updater.update(update_attributes)
expect(user.reload.name).to eq("Jimmmy Johnny")
expect(user.user_profile.bio_raw).to eq("hello world I'm Jimmmy")
ensure
DiscourseEvent.off(:within_user_updater_transaction, &handler)
end
it "can cancel the whole update transaction if a handler raises" do
error_class = Class.new(StandardError)
handler = Proc.new { raise error_class.new }
DiscourseEvent.on(:within_user_updater_transaction, &handler)
old_name = user.name
updater = UserUpdater.new(user, user)
expect { updater.update(name: "Failure McClario") }.to raise_error(error_class)
expect(user.reload.name).to eq(old_name)
ensure
DiscourseEvent.off(:within_user_updater_transaction, &handler)
end
end
it "can update categories and tags" do
updater = UserUpdater.new(user, user)
updater.update(watched_tags: "#{tag.name},#{tag2.name}", muted_category_ids: [category.id])