diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb index 15a959a444be..708f06bd4ae7 100644 --- a/app/controllers/roles_controller.rb +++ b/app/controllers/roles_controller.rb @@ -28,7 +28,6 @@ class RolesController < ApplicationController include PaginationHelper - include Roles::NotifyMixin layout 'admin' @@ -54,7 +53,7 @@ def edit end def create - @call = create_role + @call = Roles::CreateService.new(user: current_user).call(create_params) @role = @call.result if @call.success? @@ -81,10 +80,17 @@ def update def destroy @role = Role.find(params[:id]) + # after destroy permissions can not be reached + permissions = @role.permissions @role.destroy + flash[:notice] = I18n.t(:notice_successful_delete) redirect_to action: 'index' - notify_changed_roles(:removed, @role) + + OpenProject::Notifications.send( + OpenProject::Events::ROLE_DESTROYED, + permissions: + ) rescue StandardError flash[:error] = I18n.t(:error_can_not_remove_role) redirect_to action: 'index' @@ -148,12 +154,6 @@ def bulk_update_roles(roles) end end - def create_role - Roles::CreateService - .new(user: current_user) - .call(create_params) - end - def roles_scope Role.order(Arel.sql('builtin, position')) end diff --git a/app/services/oauth_clients/connection_manager.rb b/app/services/oauth_clients/connection_manager.rb index e83171c75863..cee446293d0b 100644 --- a/app/services/oauth_clients/connection_manager.rb +++ b/app/services/oauth_clients/connection_manager.rb @@ -118,6 +118,10 @@ def code_to_token(code) expires_in: rack_access_token.raw_attributes[:expires_in], scope: rack_access_token.scope ) + OpenProject::Notifications.send( + OpenProject::Events::OAUTH_CLIENT_TOKEN_CREATED, + integration_type: @oauth_client.integration_type + ) end ServiceResult.success(result: oauth_client_token) diff --git a/app/services/roles/create_service.rb b/app/services/roles/create_service.rb index 92f166656cab..20044048d73f 100644 --- a/app/services/roles/create_service.rb +++ b/app/services/roles/create_service.rb @@ -27,8 +27,6 @@ #++ class Roles::CreateService < BaseServices::Create - include Roles::NotifyMixin - private def perform(params) @@ -38,13 +36,21 @@ def perform(params) if super_call.success? copy_workflows(copy_workflow_id, super_call.result) - - notify_changed_roles(:added, super_call.result) end super_call end + def after_perform(call) + role = call.result + OpenProject::Notifications.send( + OpenProject::Events::ROLE_CREATED, + permissions: role.permissions + ) + + call + end + def instance(params) if params.delete(:global_role) GlobalRole.new diff --git a/app/services/roles/notify_mixin.rb b/app/services/roles/notify_mixin.rb deleted file mode 100644 index 9ee13bac1f17..000000000000 --- a/app/services/roles/notify_mixin.rb +++ /dev/null @@ -1,35 +0,0 @@ -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) 2012-2023 the OpenProject GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. -# -# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2013 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# See COPYRIGHT and LICENSE files for more details. -#++ - -module Roles::NotifyMixin - private - - def notify_changed_roles(action, changed_role) - OpenProject::Notifications.send(:roles_changed, action:, role: changed_role) - end -end diff --git a/app/services/roles/update_service.rb b/app/services/roles/update_service.rb index 6e0c08c98681..cb67f717c20e 100644 --- a/app/services/roles/update_service.rb +++ b/app/services/roles/update_service.rb @@ -27,11 +27,20 @@ #++ class Roles::UpdateService < BaseServices::Update - include Roles::NotifyMixin - private - def after_safe - notify_changed_roles(:updated, model) + def before_perform(params, service_call) + @permissions_old = service_call.result.permissions + super + end + + def after_perform(call) + permissions_new = call.result.permissions + permissions_diff = (@permissions_old - permissions_new) | (permissions_new - @permissions_old) + OpenProject::Notifications.send( + OpenProject::Events::ROLE_UPDATED, + permissions_diff: + ) + call end end diff --git a/lib/open_project/events.rb b/lib/open_project/events.rb index 0437b543a4cf..ef552448c50e 100644 --- a/lib/open_project/events.rb +++ b/lib/open_project/events.rb @@ -51,6 +51,8 @@ module Events # Called like this for historic reasons, should be called 'member_destroyed' MEMBER_DESTROYED = 'member_removed'.freeze + OAUTH_CLIENT_TOKEN_CREATED = 'oauth_client_token_created'.freeze + TIME_ENTRY_CREATED = "time_entry_created".freeze NEWS_COMMENT_CREATED = "news_comment_created".freeze @@ -59,6 +61,14 @@ module Events PROJECT_UPDATED = "project_updated".freeze PROJECT_RENAMED = "project_renamed".freeze + PROJECTS_STORAGE_CREATED = "projects_storage_created".freeze + PROJECTS_STORAGE_UPDATED = "projects_storage_updated".freeze + PROJECTS_STORAGE_DESTROYED = "projects_storage_destroyed".freeze + + ROLE_CREATED = "role_created".freeze + ROLE_UPDATED = "role_updated".freeze + ROLE_DESTROYED = "role_destroyed".freeze + WATCHER_ADDED = 'watcher_added'.freeze WATCHER_REMOVED = 'watcher_removed'.freeze end diff --git a/modules/storages/app/services/storages/project_storages/create_service.rb b/modules/storages/app/services/storages/project_storages/create_service.rb index 20fac5fc1162..78caa92e285c 100644 --- a/modules/storages/app/services/storages/project_storages/create_service.rb +++ b/modules/storages/app/services/storages/project_storages/create_service.rb @@ -35,8 +35,12 @@ def after_perform(service_call) super(service_call) project_storage = service_call.result - add_historical_data(service_call) if project_storage.project_folder_mode.to_sym != :inactive - Helper.trigger_nextcloud_synchronization(project_storage.project_folder_mode) + project_folder_mode = project_storage.project_folder_mode.to_sym + add_historical_data(service_call) if project_folder_mode != :inactive + OpenProject::Notifications.send( + OpenProject::Events::PROJECTS_STORAGE_CREATED, + project_folder_mode: + ) service_call end diff --git a/modules/storages/app/services/storages/project_storages/delete_service.rb b/modules/storages/app/services/storages/project_storages/delete_service.rb index 31bfe2638ccc..4640e2ab33cd 100644 --- a/modules/storages/app/services/storages/project_storages/delete_service.rb +++ b/modules/storages/app/services/storages/project_storages/delete_service.rb @@ -38,8 +38,13 @@ class DeleteService < ::BaseServices::Delete def persist(service_result) # Perform the @object.destroy etc. in the super-class super(service_result).tap do |deletion_result| - delete_associated_file_links if deletion_result.success? - Helper.trigger_nextcloud_synchronization(model.project_folder_mode) + if deletion_result.success? + delete_associated_file_links + OpenProject::Notifications.send( + OpenProject::Events::PROJECTS_STORAGE_DESTROYED, + project_folder_mode: deletion_result.result.project_folder_mode.to_sym + ) + end end end diff --git a/modules/storages/app/services/storages/project_storages/helper.rb b/modules/storages/app/services/storages/project_storages/helper.rb index 5a7db62c2def..192544d9cd2b 100644 --- a/modules/storages/app/services/storages/project_storages/helper.rb +++ b/modules/storages/app/services/storages/project_storages/helper.rb @@ -40,8 +40,4 @@ def update_last_project_folder(user:, project_folder:, origin_folder_id:) .new(model: project_folder, user:) .call(origin_folder_id:) end - - def trigger_nextcloud_synchronization(project_folder_mode) - Storages::ManageNextcloudIntegrationEventsJob.perform_later if project_folder_mode.to_sym == :automatic - end end diff --git a/modules/storages/app/services/storages/project_storages/update_service.rb b/modules/storages/app/services/storages/project_storages/update_service.rb index 202f67e7fece..b75e4d38b3f2 100644 --- a/modules/storages/app/services/storages/project_storages/update_service.rb +++ b/modules/storages/app/services/storages/project_storages/update_service.rb @@ -36,8 +36,12 @@ def after_perform(service_call) super(service_call) project_storage = service_call.result - add_historical_data(service_call) if project_storage.project_folder_mode.to_sym != :inactive - Helper.trigger_nextcloud_synchronization(model.project_folder_mode) + project_folder_mode = project_storage.project_folder_mode.to_sym + add_historical_data(service_call) if project_folder_mode != :inactive + OpenProject::Notifications.send( + OpenProject::Events::PROJECTS_STORAGE_UPDATED, + project_folder_mode: + ) service_call end diff --git a/modules/storages/lib/open_project/storages/engine.rb b/modules/storages/lib/open_project/storages/engine.rb index 512ff777460a..f9445ef5b1d8 100644 --- a/modules/storages/lib/open_project/storages/engine.rb +++ b/modules/storages/lib/open_project/storages/engine.rb @@ -32,6 +32,13 @@ # gets loaded. module OpenProject::Storages class Engine < ::Rails::Engine + PERMISSIONS = %i[ + read_files + write_files + create_files + delete_files + share_files + ].freeze # engine name is used as a default prefix for module tables when generating # tables with the rails command. # It may also be used in other places, please investigate. @@ -49,7 +56,7 @@ class Engine < ::Rails::Engine initializer 'openproject_storages.event_subscriptions' do Rails.application.config.after_initialize do - if OpenProject::FeatureDecisions.managed_project_folders_active? + if OpenProject::FeatureDecisions.managed_project_folders_active? || Rails.env.test? [ OpenProject::Events::MEMBER_CREATED, OpenProject::Events::MEMBER_UPDATED, @@ -62,6 +69,47 @@ class Engine < ::Rails::Engine ::Storages::ManageNextcloudIntegrationEventsJob.debounce end end + + OpenProject::Notifications.subscribe( + OpenProject::Events::OAUTH_CLIENT_TOKEN_CREATED + ) do |payload| + if payload[:integration_type] == 'Storages::Storage' + ::Storages::ManageNextcloudIntegrationEventsJob.debounce + end + end + OpenProject::Notifications.subscribe( + OpenProject::Events::ROLE_CREATED + ) do |payload| + if payload[:permissions].intersect?(PERMISSIONS) + ::Storages::ManageNextcloudIntegrationEventsJob.debounce + end + end + OpenProject::Notifications.subscribe( + OpenProject::Events::ROLE_UPDATED + ) do |payload| + if payload[:permissions_diff].intersect?(PERMISSIONS) + ::Storages::ManageNextcloudIntegrationEventsJob.debounce + end + end + OpenProject::Notifications.subscribe( + OpenProject::Events::ROLE_DESTROYED + ) do |payload| + if payload[:permissions].intersect?(PERMISSIONS) + ::Storages::ManageNextcloudIntegrationEventsJob.debounce + end + end + + [ + OpenProject::Events::PROJECTS_STORAGE_CREATED, + OpenProject::Events::PROJECTS_STORAGE_UPDATED, + OpenProject::Events::PROJECTS_STORAGE_DESTROYED + ].each do |event| + OpenProject::Notifications.subscribe(event) do |payload| + if payload[:project_folder_mode] == :automatic + ::Storages::ManageNextcloudIntegrationEventsJob.debounce + end + end + end end end end @@ -93,21 +141,9 @@ class Engine < ::Rails::Engine # explicit check for test env is needed, because `with_flag: { managed_project_folders: true }` set for a test case # handled later and at this moment feature is disabled. if OpenProject::FeatureDecisions.managed_project_folders_active? || Rails.env.test? - permission :read_files, - {}, - dependencies: %i[] - permission :write_files, - {}, - dependencies: %i[] - permission :create_files, - {}, - dependencies: %i[] - permission :delete_files, - {}, - dependencies: %i[] - permission :share_files, - {}, - dependencies: %i[] + PERMISSIONS.each do |p| + permission(p, {}, dependencies: %i[]) + end end end