mirror of https://github.com/abpframework/abp.git
committed by
GitHub
35 changed files with 732 additions and 14 deletions
@ -0,0 +1,24 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
using Volo.Abp.Features; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Volo.Abp.SimpleStateChecking; |
||||
|
|
||||
|
namespace Volo.Abp.SettingManagement; |
||||
|
|
||||
|
public class AllowChangingTimeZoneSettingsFeatureSimpleStateChecker : ISimpleStateChecker<PermissionDefinition> |
||||
|
{ |
||||
|
public async Task<bool> IsEnabledAsync(SimpleStateCheckerContext<PermissionDefinition> context) |
||||
|
{ |
||||
|
var currentTenant = context.ServiceProvider.GetRequiredService<ICurrentTenant>(); |
||||
|
|
||||
|
if (!currentTenant.IsAvailable) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
var featureChecker = context.ServiceProvider.GetRequiredService<IFeatureChecker>(); |
||||
|
return await featureChecker.IsEnabledAsync(SettingManagementFeatures.EnableTimeZone); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace Volo.Abp.SettingManagement; |
||||
|
|
||||
|
public interface ITimeZoneSettingsAppService : IApplicationService |
||||
|
{ |
||||
|
Task<string> GetAsync(); |
||||
|
|
||||
|
Task<List<NameValue>> GetTimezonesAsync(); |
||||
|
|
||||
|
Task UpdateAsync(string timezone); |
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using TimeZoneConverter; |
||||
|
using Volo.Abp.Features; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Volo.Abp.Settings; |
||||
|
using Volo.Abp.Timing; |
||||
|
|
||||
|
namespace Volo.Abp.SettingManagement; |
||||
|
|
||||
|
[RequiresFeature(SettingManagementFeatures.EnableTimeZone)] |
||||
|
[Authorize(SettingManagementPermissions.TimeZone)] |
||||
|
public class TimeZoneSettingsAppService : SettingManagementAppServiceBase, ITimeZoneSettingsAppService |
||||
|
{ |
||||
|
protected ISettingManager SettingManager { get; } |
||||
|
protected ISettingProvider SettingProvider { get; } |
||||
|
protected ITimezoneProvider TimezoneProvider { get; } |
||||
|
|
||||
|
public TimeZoneSettingsAppService(ISettingManager settingManager, ISettingProvider settingProvider, ITimezoneProvider timezoneProvider) |
||||
|
{ |
||||
|
SettingManager = settingManager; |
||||
|
SettingProvider = settingProvider; |
||||
|
TimezoneProvider = timezoneProvider; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<string> GetAsync() |
||||
|
{ |
||||
|
var timezone = await SettingProvider.GetOrNullAsync(TimingSettingNames.TimeZone); |
||||
|
return timezone ?? "UTC"; |
||||
|
} |
||||
|
|
||||
|
public virtual Task<List<NameValue>> GetTimezonesAsync() |
||||
|
{ |
||||
|
var timezones = TimezoneProvider.GetWindowsTimezones() |
||||
|
.OrderBy(x => x.Name) |
||||
|
.Select(x => new NameValue( $"{x.Name} ({GetTimezoneOffset(TZConvert.GetTimeZoneInfo(x.Name))})", x.Name)) |
||||
|
.ToList(); |
||||
|
|
||||
|
return Task.FromResult(timezones); |
||||
|
} |
||||
|
|
||||
|
protected virtual string GetTimezoneOffset(TimeZoneInfo timeZoneInfo) |
||||
|
{ |
||||
|
if (timeZoneInfo.BaseUtcOffset < TimeSpan.Zero) |
||||
|
{ |
||||
|
return "-" + timeZoneInfo.BaseUtcOffset.ToString(@"hh\:mm"); |
||||
|
} |
||||
|
|
||||
|
return "+" + timeZoneInfo.BaseUtcOffset.ToString(@"hh\:mm"); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task UpdateAsync(string timezone) |
||||
|
{ |
||||
|
if (CurrentTenant.GetMultiTenancySide() == MultiTenancySides.Host) |
||||
|
{ |
||||
|
await SettingManager.SetGlobalAsync(TimingSettingNames.TimeZone, timezone); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
await SettingManager.SetForCurrentTenantAsync(TimingSettingNames.TimeZone, timezone); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
@using Volo.Abp.SettingManagement.Localization |
||||
|
@inherits Volo.Abp.AspNetCore.Components.AbpComponentBase |
||||
|
@inject AbpBlazorMessageLocalizerHelper<AbpSettingManagementResource> LH |
||||
|
|
||||
|
@if (TimezoneSettings != null) |
||||
|
{ |
||||
|
<Form> |
||||
|
<Row> |
||||
|
<Column> |
||||
|
<Field> |
||||
|
<FieldLabel>@L["DisplayName:Timezone"] *</FieldLabel> |
||||
|
<Select TValue="string" SelectedValue="TimezoneSettings.Timezone" SelectedValueChanged="OnSelectedValueChangedAsync"> |
||||
|
@foreach (var item in TimezoneSettings.TimeZoneItems) |
||||
|
{ |
||||
|
<SelectItem Value="item.Value">@item.Name</SelectItem> |
||||
|
} |
||||
|
</Select> |
||||
|
</Field> |
||||
|
</Column> |
||||
|
</Row> |
||||
|
<Row> |
||||
|
<Column> |
||||
|
<SubmitButton Clicked="@UpdateSettingsAsync"/> |
||||
|
</Column> |
||||
|
</Row> |
||||
|
</Form> |
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Components; |
||||
|
using Volo.Abp.AspNetCore.Components.Messages; |
||||
|
using Volo.Abp.AspNetCore.Components.Web.Configuration; |
||||
|
using Volo.Abp.SettingManagement.Localization; |
||||
|
|
||||
|
namespace Volo.Abp.SettingManagement.Blazor.Pages.SettingManagement.TimeZoneSettingGroup; |
||||
|
|
||||
|
public partial class TimeZoneSettingGroupViewComponent |
||||
|
{ |
||||
|
[Inject] |
||||
|
protected ITimeZoneSettingsAppService TimeZoneSettingsAppService { get; set; } |
||||
|
|
||||
|
[Inject] |
||||
|
private ICurrentApplicationConfigurationCacheResetService CurrentApplicationConfigurationCacheResetService { get; set; } |
||||
|
|
||||
|
[Inject] |
||||
|
protected IUiMessageService UiMessageService { get; set; } |
||||
|
|
||||
|
protected UpdateTimezoneSettingsViewModel TimezoneSettings; |
||||
|
|
||||
|
public TimeZoneSettingGroupViewComponent() |
||||
|
{ |
||||
|
ObjectMapperContext = typeof(AbpSettingManagementBlazorModule); |
||||
|
LocalizationResource = typeof(AbpSettingManagementResource); |
||||
|
} |
||||
|
|
||||
|
protected async override Task OnInitializedAsync() |
||||
|
{ |
||||
|
TimezoneSettings = new UpdateTimezoneSettingsViewModel() |
||||
|
{ |
||||
|
Timezone = await TimeZoneSettingsAppService.GetAsync(), |
||||
|
TimeZoneItems = await TimeZoneSettingsAppService.GetTimezonesAsync() |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task OnSelectedValueChangedAsync(string timezone) |
||||
|
{ |
||||
|
TimezoneSettings.Timezone = timezone; |
||||
|
await InvokeAsync(StateHasChanged); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task UpdateSettingsAsync() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
await TimeZoneSettingsAppService.UpdateAsync(TimezoneSettings.Timezone); |
||||
|
await CurrentApplicationConfigurationCacheResetService.ResetAsync(); |
||||
|
await UiMessageService.Success(L["SuccessfullySaved"]); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
await HandleErrorAsync(ex); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class UpdateTimezoneSettingsViewModel |
||||
|
{ |
||||
|
public string Timezone { get; set; } |
||||
|
|
||||
|
public List<NameValue> TimeZoneItems { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using Volo.Abp.Features; |
||||
|
using Volo.Abp.SettingManagement.Blazor.Pages.SettingManagement.TimeZoneSettingGroup; |
||||
|
using Volo.Abp.SettingManagement.Localization; |
||||
|
using Volo.Abp.Timing; |
||||
|
|
||||
|
namespace Volo.Abp.SettingManagement.Blazor.Settings; |
||||
|
|
||||
|
public class TimeZonePageContributor : ISettingComponentContributor |
||||
|
{ |
||||
|
public async Task ConfigureAsync(SettingComponentCreationContext context) |
||||
|
{ |
||||
|
await CheckFeatureAsync(context); |
||||
|
|
||||
|
var l = context.ServiceProvider.GetRequiredService<IStringLocalizer<AbpSettingManagementResource>>(); |
||||
|
if (context.ServiceProvider.GetRequiredService<IClock>().SupportsMultipleTimezone) |
||||
|
{ |
||||
|
context.Groups.Add( |
||||
|
new SettingComponentGroup( |
||||
|
"Volo.Abp.TimeZone", |
||||
|
l["Menu:TimeZone"], |
||||
|
typeof(TimeZoneSettingGroupViewComponent) |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public async Task<bool> CheckPermissionsAsync(SettingComponentCreationContext context) |
||||
|
{ |
||||
|
if (!await CheckFeatureAsync(context)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
var authorizationService = context.ServiceProvider.GetRequiredService<IAuthorizationService>(); |
||||
|
|
||||
|
return await authorizationService.IsGrantedAsync(SettingManagementPermissions.TimeZone); |
||||
|
} |
||||
|
|
||||
|
private async Task<bool> CheckFeatureAsync(SettingComponentCreationContext context) |
||||
|
{ |
||||
|
var featureCheck = context.ServiceProvider.GetRequiredService<IFeatureChecker>(); |
||||
|
|
||||
|
return await featureCheck.IsEnabledAsync(SettingManagementFeatures.EnableTimeZone); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Http.Client; |
||||
|
using Volo.Abp.Http.Client.ClientProxying; |
||||
|
using Volo.Abp.Http.Modeling; |
||||
|
using Volo.Abp.SettingManagement; |
||||
|
|
||||
|
// ReSharper disable once CheckNamespace
|
||||
|
namespace Volo.Abp.SettingManagement; |
||||
|
|
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
[ExposeServices(typeof(IEmailSettingsAppService), typeof(EmailSettingsClientProxy))] |
||||
|
public partial class EmailSettingsClientProxy : ClientProxyBase<IEmailSettingsAppService>, IEmailSettingsAppService |
||||
|
{ |
||||
|
public virtual async Task<EmailSettingsDto> GetAsync() |
||||
|
{ |
||||
|
return await RequestAsync<EmailSettingsDto>(nameof(GetAsync)); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task UpdateAsync(UpdateEmailSettingsDto input) |
||||
|
{ |
||||
|
await RequestAsync(nameof(UpdateAsync), new ClientProxyRequestTypeValue |
||||
|
{ |
||||
|
{ typeof(UpdateEmailSettingsDto), input } |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task SendTestEmailAsync(SendTestEmailInput input) |
||||
|
{ |
||||
|
await RequestAsync(nameof(SendTestEmailAsync), new ClientProxyRequestTypeValue |
||||
|
{ |
||||
|
{ typeof(SendTestEmailInput), input } |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
// This file is part of EmailSettingsClientProxy, you can customize it here
|
||||
|
// ReSharper disable once CheckNamespace
|
||||
|
namespace Volo.Abp.SettingManagement; |
||||
|
|
||||
|
public partial class EmailSettingsClientProxy |
||||
|
{ |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Http.Client; |
||||
|
using Volo.Abp.Http.Client.ClientProxying; |
||||
|
using Volo.Abp.Http.Modeling; |
||||
|
using Volo.Abp.SettingManagement; |
||||
|
|
||||
|
// ReSharper disable once CheckNamespace
|
||||
|
namespace Volo.Abp.SettingManagement; |
||||
|
|
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
[ExposeServices(typeof(ITimeZoneSettingsAppService), typeof(TimeZoneSettingsClientProxy))] |
||||
|
public partial class TimeZoneSettingsClientProxy : ClientProxyBase<ITimeZoneSettingsAppService>, ITimeZoneSettingsAppService |
||||
|
{ |
||||
|
public virtual async Task<string> GetAsync() |
||||
|
{ |
||||
|
return await RequestAsync<string>(nameof(GetAsync)); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<List<NameValue>> GetTimezonesAsync() |
||||
|
{ |
||||
|
return await RequestAsync<List<NameValue>>(nameof(GetTimezonesAsync)); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task UpdateAsync(string timezone) |
||||
|
{ |
||||
|
await RequestAsync(nameof(UpdateAsync), new ClientProxyRequestTypeValue |
||||
|
{ |
||||
|
{ typeof(string), timezone } |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
// This file is part of TimeZoneSettingsClientProxy, you can customize it here
|
||||
|
// ReSharper disable once CheckNamespace
|
||||
|
namespace Volo.Abp.SettingManagement; |
||||
|
|
||||
|
public partial class TimeZoneSettingsClientProxy |
||||
|
{ |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
|
||||
|
namespace Volo.Abp.SettingManagement; |
||||
|
|
||||
|
[RemoteService(Name = SettingManagementRemoteServiceConsts.RemoteServiceName)] |
||||
|
[Area(SettingManagementRemoteServiceConsts.ModuleName)] |
||||
|
[Route("api/setting-management/timezone")] |
||||
|
public class TimeZoneSettingsController : AbpControllerBase, ITimeZoneSettingsAppService |
||||
|
{ |
||||
|
private readonly ITimeZoneSettingsAppService _timeZoneSettingsAppService; |
||||
|
|
||||
|
public TimeZoneSettingsController(ITimeZoneSettingsAppService timeZoneSettingsAppService) |
||||
|
{ |
||||
|
_timeZoneSettingsAppService = timeZoneSettingsAppService; |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
public Task<string> GetAsync() |
||||
|
{ |
||||
|
return _timeZoneSettingsAppService.GetAsync(); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
public Task<List<NameValue>> GetTimezonesAsync() |
||||
|
{ |
||||
|
return _timeZoneSettingsAppService.GetTimezonesAsync(); |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
public Task UpdateAsync(string timezone) |
||||
|
{ |
||||
|
return _timeZoneSettingsAppService.UpdateAsync(timezone); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
@using Microsoft.AspNetCore.Mvc.Localization |
||||
|
@using Volo.Abp.SettingManagement.Localization |
||||
|
@inject IHtmlLocalizer<AbpSettingManagementResource> L |
||||
|
@model Volo.Abp.SettingManagement.Web.Pages.SettingManagement.Components.TimeZoneSettingGroup.TimeZoneSettingGroupViewComponent.UpdateTimezoneSettingsViewModel |
||||
|
|
||||
|
<abp-script src="/Pages/SettingManagement/Components/TimeZoneSettingGroup/Default.js" /> |
||||
|
|
||||
|
<form id="TimeZoneSettingsForm" method="post"> |
||||
|
<abp-row> |
||||
|
<abp-column size-md="_12"> |
||||
|
<div> |
||||
|
<abp-select asp-for="Timezone" asp-items="@Model.TimeZoneItems"/> |
||||
|
</div> |
||||
|
<div> |
||||
|
<abp-button button-type="Primary" type="submit"> |
||||
|
<i class="fa fa-save"></i> @L["Save"] |
||||
|
</abp-button> |
||||
|
</div> |
||||
|
</abp-column> |
||||
|
</abp-row> |
||||
|
</form> |
||||
@ -0,0 +1,15 @@ |
|||||
|
(function ($) { |
||||
|
$(function () { |
||||
|
|
||||
|
var l = abp.localization.getResource('AbpSettingManagement'); |
||||
|
|
||||
|
$("#TimeZoneSettingsForm").on('submit', function (event) { |
||||
|
event.preventDefault(); |
||||
|
|
||||
|
volo.abp.settingManagement.timeZoneSettings.update($("#Timezone").val()).then(function (result) { |
||||
|
$(document).trigger("AbpSettingSaved"); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
|
})(jQuery); |
||||
@ -0,0 +1,42 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Rendering; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.Timing.Localization.Resources.AbpTiming; |
||||
|
|
||||
|
namespace Volo.Abp.SettingManagement.Web.Pages.SettingManagement.Components.TimeZoneSettingGroup; |
||||
|
|
||||
|
public class TimeZoneSettingGroupViewComponent : AbpViewComponent |
||||
|
{ |
||||
|
protected ITimeZoneSettingsAppService TimeZoneSettingsAppService { get; } |
||||
|
|
||||
|
public TimeZoneSettingGroupViewComponent(ITimeZoneSettingsAppService timeZoneSettingsAppService) |
||||
|
{ |
||||
|
ObjectMapperContext = typeof(AbpSettingManagementWebModule); |
||||
|
TimeZoneSettingsAppService = timeZoneSettingsAppService; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<IViewComponentResult> InvokeAsync() |
||||
|
{ |
||||
|
var timezone = await TimeZoneSettingsAppService.GetAsync(); |
||||
|
var timezones = await TimeZoneSettingsAppService.GetTimezonesAsync(); |
||||
|
var model = new UpdateTimezoneSettingsViewModel() |
||||
|
{ |
||||
|
Timezone = timezone, |
||||
|
TimeZoneItems = new List<SelectListItem>() |
||||
|
}; |
||||
|
model.TimeZoneItems.AddRange(timezones.Select(x => new SelectListItem(x.Name, x.Value)).ToList()); |
||||
|
return View("~/Pages/SettingManagement/Components/TimeZoneSettingGroup/Default.cshtml", model); |
||||
|
} |
||||
|
|
||||
|
public class UpdateTimezoneSettingsViewModel |
||||
|
{ |
||||
|
public string Timezone { get; set; } |
||||
|
|
||||
|
public List<SelectListItem> TimeZoneItems { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using Volo.Abp.SettingManagement.Localization; |
||||
|
using Volo.Abp.SettingManagement.Web.Pages.SettingManagement; |
||||
|
using Volo.Abp.SettingManagement.Web.Pages.SettingManagement.Components.EmailSettingGroup; |
||||
|
using Volo.Abp.SettingManagement.Web.Pages.SettingManagement.Components.TimeZoneSettingGroup; |
||||
|
using Volo.Abp.Timing; |
||||
|
|
||||
|
namespace Volo.Abp.SettingManagement.Web.Settings; |
||||
|
|
||||
|
public class TimeZonePageContributor : SettingPageContributorBase |
||||
|
{ |
||||
|
public TimeZonePageContributor() |
||||
|
{ |
||||
|
RequiredFeatures(SettingManagementFeatures.EnableTimeZone); |
||||
|
} |
||||
|
|
||||
|
public override Task ConfigureAsync(SettingPageCreationContext context) |
||||
|
{ |
||||
|
var l = context.ServiceProvider.GetRequiredService<IStringLocalizer<AbpSettingManagementResource>>(); |
||||
|
|
||||
|
if (context.ServiceProvider.GetRequiredService<IClock>().SupportsMultipleTimezone) |
||||
|
{ |
||||
|
context.Groups.Add( |
||||
|
new SettingPageGroup( |
||||
|
"Volo.Abp.TimeZone", |
||||
|
l["Menu:TimeZone"], |
||||
|
typeof(TimeZoneSettingGroupViewComponent) |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue