From bbd9ce568e9e23f1a73e82a922a1288924e9f5f1 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 29 Jul 2026 10:38:31 +0800 Subject: [PATCH 1/2] Notify Blazor components when application configuration cache is reset --- ...verCurrentApplicationConfigurationCacheResetService.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs index 144d975cb5..e550d75858 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Volo.Abp.AspNetCore.Components.Web.Configuration; +using Volo.Abp.AspNetCore.Components.Web.Security; using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus.Local; @@ -13,15 +14,20 @@ public class BlazorServerCurrentApplicationConfigurationCacheResetService : ITransientDependency { private readonly ILocalEventBus _localEventBus; + private readonly ApplicationConfigurationChangedService _applicationConfigurationChangedService; public BlazorServerCurrentApplicationConfigurationCacheResetService( - ILocalEventBus localEventBus) + ILocalEventBus localEventBus, + ApplicationConfigurationChangedService applicationConfigurationChangedService) { _localEventBus = localEventBus; + _applicationConfigurationChangedService = applicationConfigurationChangedService; } public async Task ResetAsync(Guid? userId = null) { await _localEventBus.PublishAsync(new CurrentApplicationConfigurationCacheResetEventData(userId)); + + _applicationConfigurationChangedService.NotifyChanged(); } } From 25fecd927ba3ee1c9a99f20a7d0ee055004f4582 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 29 Jul 2026 15:02:20 +0800 Subject: [PATCH 2/2] Reload the page after a feature or permission change affecting the current user The MVC toolbars and menus are rendered on the server and cannot reflect the change otherwise --- .../feature-management-modal.js | 19 +++++++++ .../permission-management-modal.js | 40 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/feature-management-modal.js b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/feature-management-modal.js index 2f352b767a..329908ed10 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/feature-management-modal.js +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/feature-management-modal.js @@ -3,6 +3,16 @@ var abp = abp || {}; abp.modals = abp.modals || {}; let l = abp.localization.getResource("AbpFeatureManagement"); + + // The toolbars, menus and bundles are rendered on the server, so a full page + // load is the only way to reflect the new features on the current page. An empty + // provider key means the features of the current host or tenant were changed. + function reloadPageIfCurrentFeaturesChanged(providerKey) { + if (!providerKey) { + window.location.reload(); + } + } + abp.modals.FeatureManagement = function () { abp.ResourceLoader.loadScript('/client-proxies/featureManagement-proxy.js'); @@ -16,6 +26,7 @@ var abp = abp || {}; $("#FeatureManagementForm").get(0).reset(); abp.notify.success(l('SavedSuccessfully')); $('#featureManagmentModal').modal('hide'); + reloadPageIfCurrentFeaturesChanged(prodiverKey); }); } }); @@ -59,6 +70,14 @@ var abp = abp || {}; } this.initDom = function ($el) { + let initialValues = $el.serialize(); + + $el.on('abp-ajax-success', function () { + if ($el.serialize() !== initialValues) { + reloadPageIfCurrentFeaturesChanged($el.find('#ProviderKey').val()); + } + }); + $el.find('.tab-pane').each(function () { let $tab = $(this); $tab.find('input[type="checkbox"]') diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/permission-management-modal.js b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/permission-management-modal.js index f783e944b8..6c63633403 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/permission-management-modal.js +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/permission-management-modal.js @@ -2,6 +2,26 @@ var abp = abp || {}; (function ($) { abp.modals = abp.modals || {}; + // The toolbars, menus and bundles are rendered on the server, so a full page load + // is the only way to reflect the new permissions on the current page. Changing the + // permissions of another user or role does not affect what this page renders. + function affectsCurrentUser(providerName, providerKey) { + var currentUser = abp.currentUser; + if (!currentUser || !currentUser.isAuthenticated) { + return false; + } + + if (providerName === 'U') { + return currentUser.id === providerKey; + } + + if (providerName === 'R') { + return (currentUser.roles || []).indexOf(providerKey) > -1; + } + + return false; + } + abp.modals.PermissionManagement = function () { var l = abp.localization.getResource("AbpPermissionManagement"); @@ -190,7 +210,27 @@ var abp = abp || {}; } } + // "Select all" checkboxes are UI helpers, so only the granted states are compared. + function getGrantedStates($el) { + return $el.find('input[type="checkbox"]') + .not('[name="SelectAllInThisTab"], [name="SelectAllInAllTabs"]') + .map(function () { + return this.name + '=' + this.checked; + }) + .get() + .join('&'); + } + this.initDom = function ($el) { + var initialGrantedStates = getGrantedStates($el); + + $el.on('abp-ajax-success', function () { + if (getGrantedStates($el) !== initialGrantedStates && + affectsCurrentUser($el.find('#ProviderName').val(), $el.find('#ProviderKey').val())) { + window.location.reload(); + } + }); + $el.find('.tab-pane').each(function () { var $tab = $(this); handleTabCheckedCheckboxCount($tab);