diff --git a/app/controllers/users/invite_controller.rb b/app/controllers/users/invite_controller.rb index b1bb89841bb..1cba412ee00 100644 --- a/app/controllers/users/invite_controller.rb +++ b/app/controllers/users/invite_controller.rb @@ -31,16 +31,15 @@ class Users::InviteController < ApplicationController include OpTurbo::ComponentStream authorize_with_permission :manage_members, global: true - before_action :create_form_model, only: %i[start_dialog step] def start_dialog respond_with_dialog( - Users::Invitation::DialogComponent.new(@invitation) + Users::Invitation::DialogComponent.new(form_model) ) end def step - if @invitation.valid? + if form_model.valid? respond_with_next_step else handle_errors_in_step @@ -52,10 +51,10 @@ class Users::InviteController < ApplicationController def handle_errors_in_step case params[:step] when "project" - replace_via_turbo_stream(component: Users::Invitation::ProjectStep::FormComponent.new(@invitation)) + replace_via_turbo_stream(component: Users::Invitation::ProjectStep::FormComponent.new(form_model)) respond_with_turbo_streams when "principal" - replace_via_turbo_stream(component: Users::Invitation::PrincipalStep::FormComponent.new(@invitation)) + replace_via_turbo_stream(component: Users::Invitation::PrincipalStep::FormComponent.new(form_model)) respond_with_turbo_streams else render_400 message: "Invalid step" @@ -65,8 +64,8 @@ class Users::InviteController < ApplicationController def respond_with_next_step case params[:step] when "project" - replace_via_turbo_stream(component: Users::Invitation::PrincipalStep::FormComponent.new(@invitation)) - replace_via_turbo_stream(component: Users::Invitation::PrincipalStep::FooterComponent.new(@invitation)) + replace_via_turbo_stream(component: Users::Invitation::PrincipalStep::FormComponent.new(form_model)) + replace_via_turbo_stream(component: Users::Invitation::PrincipalStep::FooterComponent.new(form_model)) respond_with_turbo_streams when "principal" create_invitation @@ -76,23 +75,30 @@ class Users::InviteController < ApplicationController end def create_invitation - # TODO, handle invite by mail - call = Members::CreateService + project = Project.find_by(id: form_model.project_id) + + call = Members::BulkCreateService .new(user: current_user) - .call(form_model_params) + .call( + project:, + user_id: form_model.id_or_email, + role_ids: [form_model.role_id], + notification_message: form_model.message, + send_notification: true + ) if call.success? close_dialog_via_turbo_stream("##{Users::Invitation::DialogComponent::DIALOG_ID}", additional: {}) else - replace_via_turbo_stream(component: Users::Invitation::PrincipalStep::FormComponent.new(call.result)) + replace_via_turbo_stream(component: Users::Invitation::PrincipalStep::FormComponent.new(form_model)) end respond_with_turbo_streams end - def create_form_model - @invitation = Users::Invitation::FormModel.new(form_model_params) + def form_model + @form_model ||= Users::Invitation::FormModel.new(form_model_params) end def form_model_params diff --git a/app/forms/users/invitation/principal_step/form.rb b/app/forms/users/invitation/principal_step/form.rb index 031d58bd08e..b26678184ac 100644 --- a/app/forms/users/invitation/principal_step/form.rb +++ b/app/forms/users/invitation/principal_step/form.rb @@ -30,33 +30,42 @@ module Users::Invitation::PrincipalStep class Form < ApplicationForm + include OpenProject::StaticRouting::UrlHelpers + include Redmine::I18n + form do |f| + f.hidden name: :project_id + f.hidden name: :principal_type + f.autocompleter( name: :id_or_email, label: TimeEntry.human_attribute_name(:user), required: true, autocomplete_options: { defaultData: true, - component: "opce-user-autocompleter", - url: ::API::V3::Utilities::PathHelper::ApiV3Path.principals, - searchKey: "any_name_attribute", - resource: "principals", + component: "opce-members-autocompleter", + url: autocomplete_for_member_project_members_path(model.project_id) + ".json", focusDirectly: false, multiple: false, clearable: false, - appendTo: "##{Users::Invitation::DialogComponent::DIALOG_ID}", + appendTo: "##{Users::Invitation::DialogComponent::DIALOG_ID}" } ) f.select_list( name: "role_id", label: "Role", - caption: "This is the role..", - include_blank: true - ) do |city_list| - city_list.option(label: "Lopez Island", value: "lopez_island") - city_list.option(label: "Bellevue", value: "bellevue") - city_list.option(label: "Seattle", value: "seattle") + caption: link_translate("users.invite_user_modal.role.description", + links: { docs_link: %i[sysadmin_docs roles_and_permissions] }), + include_blank: false, + required: true + ) do |role_list| + ProjectRole + .givable + .ordered_by_builtin_and_position + .find_each do |role| + role_list.option(label: role.name, value: role.id) + end end end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 197d663fd00..4c3e543d28f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -755,13 +755,9 @@ en: role: label: "Role in %{project}" no_roles_found: "No roles were found" - description: >- + description: > This is the role that the user will receive when they join your project. The role defines which actions they are allowed to take and which information they are allowed to see. - - Learn more about roles and permissions. - + [Learn more about roles and permissions.](docs_url) required: "Please select a role" next_button: "Next" diff --git a/config/static_links.yml b/config/static_links.yml index d8dbde9be05..fa15fd0bddc 100644 --- a/config/static_links.yml +++ b/config/static_links.yml @@ -171,6 +171,8 @@ storage_docs: troubleshooting: href: https://www.openproject.org/docs/user-guide/file-management/nextcloud-integration/#possible-errors-and-troubleshooting sysadmin_docs: + roles_and_permissions: + href: https://www.openproject.org/docs/system-admin-guide/users-permissions/roles-permissions/#roles-and-permissions ldap: href: https://www.openproject.org/docs/system-admin-guide/authentication/ldap-connections/ oidc: