For reproducing that issue we need to enable multitenancy. Could you please create one Tenant for this demo so we can test on that Tenant.
Thanks.
1)LoadStop.Web.MVC\Areas\App\Views\Shared\Components\AppDefaultBrand\Default.chtml
We have one "AppLogo" img tag with following src attribute on above page: src="@headerViewModel.GetLogoUrl(ApplicationPath, UiThemeCustomizer.IsTopMenuUsed || UiThemeCustomizer.IsTabMenuUsed ? UiThemeCustomizer.HeaderSkin: UiThemeCustomizer.LeftAsideAsideSkin)"
2)LoadStop.Web.MVC\Areas\App\Models\Layout\HeaderViewModel.cs
public string GetLogoUrl(string appPath, string menuSkin) { if (LoginInformations?.Tenant?.LogoId == null) { return appPath + $"Common/Images/app-logo-on-{menuSkin}.png"; }
//id parameter is used to prevent caching only.
return appPath + "TenantCustomization/GetLogo?id=" + LoginInformations.Tenant.LogoId;
}
3)LoadStop.Web.Core\Controllers\TenantCustomizationController.cs
[AllowAnonymous] public async Task<ActionResult> GetLogo() {
try
{
var tenant = await _tenantManager.GetByIdAsync(AbpSession.GetTenantId());
if (!tenant.HasLogo())
{
return StatusCode((int)HttpStatusCode.NotFound);
}
var logoObject = await _binaryObjectManager.GetOrNullAsync(tenant.LogoId.Value);
if (logoObject == null)
{
return StatusCode((int)HttpStatusCode.NotFound);
}
return File(logoObject.Bytes, tenant.LogoFileType);
}
catch (System.Exception ex)
{
Logger.Error("Catch-Exception in GetLogo: " + ex.Message);
throw;
}
}
==========================================================================================
For our system, 3rd method "GetLogo" gets called and but AbpSession.GetTenantId()=null so it throws catch exception as per below: https://forum.aspnetboilerplate.com/viewtopic.php?p=21747
Above issue is same as my issue.
You can reproduce this issue on any of multi-tenancy enabled asp.net zero demo project where you can upload custom logo by tenant login and see that logo is being displayed on top left corner of header or not.
We have enable multi-tenancy in our application. In our application we need to display logo(custom logo uploaded by Tenant) on header as well as on some other pages. We have checked that there is one method "GetLogo()" in TenantCustomizationController. This controller exists in Web.Core project.
In "GetLogo()" method, tenantId is retrieved from AbpSession.GetTenantId(). And this tenantId will be passed to database for getting Image bytes from AbpBinaryObjects table. But in our system, we are getting AbpSession.GetTenantId()= null so logo (uploaded by Tenant) is not being displayed.
We are calling HeaderViewModel method "GetLogoUrl" which calls "GetLogo" from TenantCustomizationController. We are getting all values in LoginInformations.Tenant but not in TenantCustomizationController.
Please let us know how should we resolve this.
Could you please show me how can i override INavigationManager and use isvisible property with session variable ?
public class AppNavigationProvider : NavigationProvider
{
public const string MenuName = "App";
public const string MenuTopName = "AppTop";
private readonly IAbpSession session;
public AppNavigationProvider(IAbpSession session)
{
session = session;
}
public override void SetNavigation(INavigationProviderContext context)
{
var menu = context.Manager.Menus[MenuName] = new MenuDefinition(MenuName, new FixedLocalizableString("Main Menu"));
menu
.AddItem(new MenuItemDefinition(
AppPageNames.Host.Dashboard,
L("Dashboard"),
url: "App/HostDashboard",
icon: "flaticon-line-graph",
requiredPermissionName: AppPermissions.PagesAdministrationHostDashboard,
featureDependency: new SimpleFeatureDependency(true, AppFeatures.EnableTracking),
isVisible: (session.MultiTenancySide != MultiTenancySides.Host)
)
}
}
public override void PreInitialize()
{
Configuration.Navigation.Providers.Add<AppNavigationProvider>();
}
We are working on creation of menu dynamically. in MenuItemDefinition there is property isVisible that show/hide menu in the web app we have use session based condition in that.
Our problem is that , when app is started this menu is register using PreInitialize() method. when it goes to AppNavigationProvider class , session value is (session.MultiTenancySide = "Host") because session is null when app is startup. on that bases whole menu item's visible property is set to false , and that menu will not shown to any user, either user is host user or tenant user
Surprisingly featureDependency works dynamically for every user.
could you please help me to solve this issue?
Thanks
@ismcagdas
If you are not able to reproduce issue then can you try by hosting your test application on Azure server? as we are facing issue only on live server.
/=====================AppNotificationNames.cs========================/
/=====================.xml file========================/
<!-- Notification Definition -->
<text name="NewDocumentCreatedNotificationDefinition" value="On Document creation"></text>
<!-- Notification Message -->
<text name="NewDocumentCreatedNotificationMessage" value="New Document - New Document {documentName} type of {documentCategory} - {documentType} is created for {entity} with Expiration {ExpirationDate} by {createdByUser}"></text>
/=====================AppNotificationProvider.cs========================/
/* Document create */ context.Manager.Add( new NotificationDefinition( AppNotificationNames.NewDocumentCreated, displayName: L("NewDocumentCreatedNotificationDefinition"), permissionDependency: new SimplePermissionDependency(AppPermissions.Pages_Documents_Create) ) );
/==============================NotificationAppService.cs==============================/
public async Task<GetNotificationSettingsOutput> GetNotificationSettings() { var output = new GetNotificationSettingsOutput();
output.ReceiveNotifications = await SettingManager.GetSettingValueAsync<bool>(NotificationSettingNames.ReceiveNotifications);
var notificationDefinitions = (await _notificationDefinitionManager.GetAllAvailableAsync(AbpSession.ToUserIdentifier()));
output.Notifications = ObjectMapper.Map<List<NotificationSubscriptionWithDisplayNameDto>>(notificationDefinitions);
var subscribedNotifications = (await _notificationSubscriptionManager
.GetSubscribedNotificationsAsync(AbpSession.ToUserIdentifier()))
.Select(ns => ns.NotificationName)
.ToList();
output.Notifications.ForEach(n => n.IsSubscribed = subscribedNotifications.Contains(n.Name));
return output;
}
public async Task UpdateNotificationSettings(UpdateNotificationSettingsInput input) { await SettingManager.ChangeSettingForUserAsync(AbpSession.ToUserIdentifier(), NotificationSettingNames.ReceiveNotifications, input.ReceiveNotifications.ToString());
foreach (var notification in input.Notifications)
{
if (notification.IsSubscribed)
{
await _notificationSubscriptionManager.SubscribeAsync(AbpSession.ToUserIdentifier(), notification.Name);
}
else
{
await _notificationSubscriptionManager.UnsubscribeAsync(AbpSession.ToUserIdentifier(), notification.Name);
}
}
}
/==============================documentAppservice.cs==============================/
[AbpAuthorize(AppPermissions.Pages_Documents_Create)] private async Task<CreateOrEditDocumentDto> Create(CreateOrEditDocumentDto input) { var document = ObjectMapper.Map<Document>(input); await _documentRepository.InsertAsync(document); await UnitOfWorkManager.Current.SaveChangesAsync();
var createOrEditDocumentDto = ObjectMapper.Map<CreateOrEditDocumentDto>(document);
/*Send Document Created Notification to all subscribed users*/
await _appNotifier.NewDocumentCreatedAsync(await GetCurrentUserAsync(), GetDtoForDocumentCreatedNotification(EntityTypeEnums.Document,createOrEditDocumentDto, documentTypedata.Name));
}
/==============================AppNotifier.cs==============================/
/Send Notification for Document Create event/ public async Task NewDocumentCreatedAsync(User createdByUser, NotificationForNewDocumentCreatedDto notificationForNewDocumentCreatedDto) { try { /Set Notification data for dynamic localization string and set parameters for Notification message text/ var notificationData = new LocalizableMessageNotificationData( new LocalizableString( "NewDocumentCreatedNotificationMessage", LoadStopConsts.LocalizationSourceName ) );
notificationData["documentName"] = notificationForNewDocumentCreatedDto.CreateOrEditDocumentDto.Name;
notificationData["createdByUser"] = createdByUser.FullName;
//Write logs
Logger.Info("New document Publish Notification Start: " + notificationForNewDocumentCreatedDto.CreateOrEditDocumentDto.Name);
await _notificationPublisher.PublishAsync(AppNotificationNames.NewDocumentCreated, notificationData, null,
NotificationSeverity.Info, null, excludedUserIds: new[] { createdByUser.ToUserIdentifier() });
Logger.Info("New document Publish Notification End: " + notificationForNewDocumentCreatedDto.CreateOrEditDocumentDto.Name);
}
catch (Exception ex)
{
var errorMessage = ex.Message;
}
}
@ismcagdas Please check this comment https://support.aspnetzero.com/QA/Questions/6390#answer-45564aa4-85ff-cb00-d268-39ebc8f5015e in same thread for code.
/*=======================documentAppService.cs==============================*/
[AbpAuthorize(AppPermissions.Pages_Documents_Edit)]
private async Task<CreateOrEditDocumentDto> Update(CreateOrEditDocumentDto input)
{
var user = await UserManager.FindByIdAsync(AbpSession.GetUserId().ToString());
var document = await _documentRepository.FirstOrDefaultAsync((int)input.Id);
var oldDocumentType = document.DocumentyTypeName;
var documentMappedEntity = ObjectMapper.Map(input, document);
await _documentRepository.UpdateAsync(documentMappedEntity);
await UnitOfWorkManager.Current.SaveChangesAsync();
if(oldDocumentType != documentMappedEntity.DocumentTypeName)
{
/*Send notification for Document type update to all subscribed users*/
_appNotifier.NotificationForDocumentTypeChangedAsync(user,notificationForDocumentDto);
}
return ObjectMapper.Map<CreateOrEditDocumentDto>(documentMappedEntity);
}
/*=================AppNotifier.cs===================*/
public async Task NotificationForDocumentTypeChangedAsync(User createdByUser, CreateOrEditDocumentDto notificationForDocumentDto)
{
try
{
/*Set Notification data for dynamic localization string and set parameters for Notification message text*/
var notificationData = new LocalizableMessageNotificationData(
new LocalizableString(
CommonMethodHelper.GetNotificationTextLocalizationString("DocumentTypeChangedNotificationMessage"),
LoadStopConsts.LocalizationSourceName
)
);
notificationData["documentName"] = notificationForNewDocumentCreatedDto.CreateOrEditDocumentDto.Name;
notificationData["documentType"] = notificationForNewDocumentCreatedDto.DocumentTypeName;
notificationData["user"] = createdByUser.FullName;
//Write logs
Logger.Info("document type changed notification Publish Notification Start: " + notificationForNewDocumentCreatedDto.CreateOrEditDocumentDto.Name);
await _notificationPublisher.PublishAsync(AppNotificationNames.DocumentTypeChanged, notificationData, null,
NotificationSeverity.Info, null, excludedUserIds: new[] { createdByUser.ToUserIdentifier() });
Logger.Info("document type changed notification Publish Notification End: " + notificationForNewDocumentCreatedDto.CreateOrEditDocumentDto.Name);
}
catch(Exception ex)
{
var errorMessage = ex.Message;
}
}
/*=====================.xml file========================*/
<text name="DocumentTypeChangedNotificationMessage" value="Document type changed to {documentType} for Document: {documentName} by {user}"></text>
/*=====================AppNotificationNames.cs========================*/
public const string DocumentTypeChanged = "App.DocumentTypeChanged";
/*=====================AppNotificationProvider.cs========================*/
/* Document create */
context.Manager.Add(
new NotificationDefinition(
AppNotificationNames.NewDocumentCreated,
displayName: L("NewDocumentCreatedNotificationDefinition"),
permissionDependency: new SimplePermissionDependency(AppPermissions.Pages_Documents_Create)
)
);
/* Document type update */
context.Manager.Add(
new NotificationDefinition(
AppNotificationNames.DocumentTypeChanged,
displayName: L("DocumentTypeChangedNotificationDefinition"),
permissionDependency: new SimplePermissionDependency(AppPermissions.Pages_Documents_Edit)
)
);
For eg. I have created one document "MyLicenseDoc" with "DocType1". Now in edit mode, I have changed it to "DocType2". So in this case, all subscribed users will get notification.