mirror of https://github.com/abpframework/abp.git
51 changed files with 388 additions and 463 deletions
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using AutoMapper; |
|||
using AutoMapper.Internal; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AutoMapper |
|||
{ |
|||
public class AbpAutoMapperConventionalRegistrar : ConventionalRegistrarBase |
|||
{ |
|||
protected readonly Type[] OpenTypes = { |
|||
typeof(IValueResolver<,,>), |
|||
typeof(IMemberValueResolver<,,,>), |
|||
typeof(ITypeConverter<,>), |
|||
typeof(IValueConverter<,>), |
|||
typeof(IMappingAction<,>) |
|||
}; |
|||
|
|||
public override void AddType(IServiceCollection services, Type type) |
|||
{ |
|||
if (IsConventionalRegistrationDisabled(type)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (type.IsClass && !type.IsAbstract && OpenTypes.Any(type.ImplementsGenericInterface)) |
|||
{ |
|||
services.TryAddTransient(type); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,122 +0,0 @@ |
|||
// This software is part of the Autofac IoC container
|
|||
// Copyright © 2015 Autofac Contributors
|
|||
// https://autofac.org
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
|||
using System; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
|
|||
namespace Autofac.Extensions.DependencyInjection |
|||
{ |
|||
/// <summary>
|
|||
/// Autofac implementation of the ASP.NET Core <see cref="IServiceProvider"/>.
|
|||
/// </summary>
|
|||
/// <seealso cref="System.IServiceProvider" />
|
|||
/// <seealso cref="Microsoft.Extensions.DependencyInjection.ISupportRequiredService" />
|
|||
public class AutofacServiceProvider : IServiceProvider, ISupportRequiredService, IDisposable |
|||
{ |
|||
private readonly ILifetimeScope _lifetimeScope; |
|||
|
|||
private bool _disposed = false; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AutofacServiceProvider"/> class.
|
|||
/// </summary>
|
|||
/// <param name="lifetimeScope">
|
|||
/// The lifetime scope from which services will be resolved.
|
|||
/// </param>
|
|||
public AutofacServiceProvider(ILifetimeScope lifetimeScope) |
|||
{ |
|||
this._lifetimeScope = lifetimeScope; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets service of type <paramref name="serviceType" /> from the
|
|||
/// <see cref="AutofacServiceProvider" /> and requires it be present.
|
|||
/// </summary>
|
|||
/// <param name="serviceType">
|
|||
/// An object that specifies the type of service object to get.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// A service object of type <paramref name="serviceType" />.
|
|||
/// </returns>
|
|||
/// <exception cref="Autofac.Core.Registration.ComponentNotRegisteredException">
|
|||
/// Thrown if the <paramref name="serviceType" /> isn't registered with the container.
|
|||
/// </exception>
|
|||
/// <exception cref="Autofac.Core.DependencyResolutionException">
|
|||
/// Thrown if the object can't be resolved from the container.
|
|||
/// </exception>
|
|||
public object GetRequiredService(Type serviceType) |
|||
{ |
|||
return this._lifetimeScope.Resolve(serviceType); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the service object of the specified type.
|
|||
/// </summary>
|
|||
/// <param name="serviceType">
|
|||
/// An object that specifies the type of service object to get.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// A service object of type <paramref name="serviceType" />; or <see langword="null" />
|
|||
/// if there is no service object of type <paramref name="serviceType" />.
|
|||
/// </returns>
|
|||
public object GetService(Type serviceType) |
|||
{ |
|||
return this._lifetimeScope.ResolveOptional(serviceType); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the underlying instance of <see cref="ILifetimeScope" />.
|
|||
/// </summary>
|
|||
public ILifetimeScope LifetimeScope => _lifetimeScope; |
|||
|
|||
/// <summary>
|
|||
/// Releases unmanaged and - optionally - managed resources.
|
|||
/// </summary>
|
|||
/// <param name="disposing">
|
|||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
|||
/// <see langword="false" /> to release only unmanaged resources.
|
|||
/// </param>
|
|||
protected virtual void Dispose(bool disposing) |
|||
{ |
|||
if (!this._disposed) |
|||
{ |
|||
this._disposed = true; |
|||
if (disposing) |
|||
{ |
|||
this._lifetimeScope.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
this.Dispose(true); |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,77 +0,0 @@ |
|||
// This software is part of the Autofac IoC container
|
|||
// Copyright © 2017 Autofac Contributors
|
|||
// http://autofac.org
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
|||
using System; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
|
|||
namespace Autofac.Extensions.DependencyInjection |
|||
{ |
|||
/// <summary>
|
|||
/// A factory for creating a <see cref="ContainerBuilder"/> and an <see cref="T:System.IServiceProvider" />.
|
|||
/// </summary>
|
|||
public class AutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder> |
|||
{ |
|||
private readonly Action<ContainerBuilder> _configurationAction; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AutofacServiceProviderFactory"/> class.
|
|||
/// </summary>
|
|||
/// <param name="configurationAction">Action on a <see cref="ContainerBuilder"/> that adds component registrations to the container.</param>
|
|||
public AutofacServiceProviderFactory(Action<ContainerBuilder> configurationAction = null) |
|||
{ |
|||
_configurationAction = configurationAction ?? (builder => { }); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a container builder from an <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection" />.
|
|||
/// </summary>
|
|||
/// <param name="services">The collection of services</param>
|
|||
/// <returns>A container builder that can be used to create an <see cref="T:System.IServiceProvider" />.</returns>
|
|||
public ContainerBuilder CreateBuilder(IServiceCollection services) |
|||
{ |
|||
var builder = new ContainerBuilder(); |
|||
|
|||
builder.Populate(services); |
|||
|
|||
_configurationAction(builder); |
|||
|
|||
return builder; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates an <see cref="T:System.IServiceProvider" /> from the container builder.
|
|||
/// </summary>
|
|||
/// <param name="containerBuilder">The container builder</param>
|
|||
/// <returns>An <see cref="T:System.IServiceProvider" /></returns>
|
|||
public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder) |
|||
{ |
|||
if (containerBuilder == null) throw new ArgumentNullException(nameof(containerBuilder)); |
|||
|
|||
var container = containerBuilder.Build(); |
|||
|
|||
return new AutofacServiceProvider(container); |
|||
} |
|||
} |
|||
} |
|||
@ -1,67 +0,0 @@ |
|||
// This software is part of the Autofac IoC container
|
|||
// Copyright © 2015 Autofac Contributors
|
|||
// http://autofac.org
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
|||
using System; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
|
|||
namespace Autofac.Extensions.DependencyInjection |
|||
{ |
|||
/// <summary>
|
|||
/// Autofac implementation of the ASP.NET Core <see cref="IServiceScope"/>.
|
|||
/// </summary>
|
|||
/// <seealso cref="Microsoft.Extensions.DependencyInjection.IServiceScope" />
|
|||
internal class AutofacServiceScope : IServiceScope |
|||
{ |
|||
private readonly ILifetimeScope _lifetimeScope; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AutofacServiceScope"/> class.
|
|||
/// </summary>
|
|||
/// <param name="lifetimeScope">
|
|||
/// The lifetime scope from which services should be resolved for this service scope.
|
|||
/// </param>
|
|||
public AutofacServiceScope(ILifetimeScope lifetimeScope) |
|||
{ |
|||
this._lifetimeScope = lifetimeScope; |
|||
this.ServiceProvider = this._lifetimeScope.Resolve<IServiceProvider>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets an <see cref="IServiceProvider" /> corresponding to this service scope.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// An <see cref="IServiceProvider" /> that can be used to resolve dependencies from the scope.
|
|||
/// </value>
|
|||
public IServiceProvider ServiceProvider { get; } |
|||
|
|||
/// <summary>
|
|||
/// Disposes of the lifetime scope and resolved disposable services.
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
this._lifetimeScope.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,65 +0,0 @@ |
|||
// This software is part of the Autofac IoC container
|
|||
// Copyright © 2015 Autofac Contributors
|
|||
// http://autofac.org
|
|||
//
|
|||
// Permission is hereby granted, free of charge, to any person
|
|||
// obtaining a copy of this software and associated documentation
|
|||
// files (the "Software"), to deal in the Software without
|
|||
// restriction, including without limitation the rights to use,
|
|||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
// copies of the Software, and to permit persons to whom the
|
|||
// Software is furnished to do so, subject to the following
|
|||
// conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|||
// OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
|||
using System.Diagnostics.CodeAnalysis; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
|
|||
namespace Autofac.Extensions.DependencyInjection |
|||
{ |
|||
/// <summary>
|
|||
/// Autofac implementation of the ASP.NET Core <see cref="IServiceScopeFactory"/>.
|
|||
/// </summary>
|
|||
/// <seealso cref="Microsoft.Extensions.DependencyInjection.IServiceScopeFactory" />
|
|||
[SuppressMessage("Microsoft.ApiDesignGuidelines", "CA2213", Justification = "The creator of the root service lifetime scope is responsible for disposal.")] |
|||
internal class AutofacServiceScopeFactory : IServiceScopeFactory |
|||
{ |
|||
private readonly ILifetimeScope _lifetimeScope; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AutofacServiceScopeFactory"/> class.
|
|||
/// </summary>
|
|||
/// <param name="lifetimeScope">The lifetime scope.</param>
|
|||
public AutofacServiceScopeFactory(ILifetimeScope lifetimeScope) |
|||
{ |
|||
this._lifetimeScope = lifetimeScope; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates an <see cref="IServiceScope" /> which contains an
|
|||
/// <see cref="System.IServiceProvider" /> used to resolve dependencies within
|
|||
/// the scope.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// An <see cref="IServiceScope" /> controlling the lifetime of the scope. Once
|
|||
/// this is disposed, any scoped services that have been resolved
|
|||
/// from the <see cref="IServiceScope.ServiceProvider" />
|
|||
/// will also be disposed.
|
|||
/// </returns>
|
|||
public IServiceScope CreateScope() |
|||
{ |
|||
return new AutofacServiceScope(this._lifetimeScope.BeginLifetimeScope()); |
|||
} |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
{ |
|||
"profiles": { |
|||
"Volo.Abp.Cli": { |
|||
"commandName": "Project", |
|||
"commandLineArgs": "bundle --skip-version-check", |
|||
"workingDirectory": "C:\\Github\\volo\\abp\\templates\\app-pro\\aspnet-core\\src\\MyCompanyName.MyProjectName.Blazor" |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
using AutoMapper; |
|||
using Shouldly; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Testing; |
|||
using Xunit; |
|||
using IObjectMapper = Volo.Abp.ObjectMapping.IObjectMapper; |
|||
|
|||
namespace Volo.Abp.AutoMapper |
|||
{ |
|||
public class AutoMapper_Dependency_Injection_Tests : AbpIntegratedTest<AutoMapperTestModule> |
|||
{ |
|||
private readonly IObjectMapper _objectMapper; |
|||
|
|||
public AutoMapper_Dependency_Injection_Tests() |
|||
{ |
|||
_objectMapper = GetRequiredService<IObjectMapper>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Registered_AutoMapper_Service() |
|||
{ |
|||
GetService<CustomMappingActionService>().ShouldNotBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Custom_MappingAction_Test() |
|||
{ |
|||
var sourceModel = new SourceModel |
|||
{ |
|||
Name = "Source" |
|||
}; |
|||
|
|||
_objectMapper.Map<SourceModel, DestModel>(sourceModel).Name.ShouldBe(nameof(CustomMappingActionService)); |
|||
} |
|||
|
|||
public class SourceModel |
|||
{ |
|||
public string Name { get; set; } |
|||
} |
|||
|
|||
public class DestModel |
|||
{ |
|||
public string Name { get; set; } |
|||
} |
|||
|
|||
public class MapperActionProfile : Profile |
|||
{ |
|||
public MapperActionProfile() |
|||
{ |
|||
CreateMap<SourceModel, DestModel>().AfterMap<CustomMappingActionService>(); |
|||
} |
|||
} |
|||
|
|||
public class CustomMappingActionService : IMappingAction<SourceModel, DestModel> |
|||
{ |
|||
public void Process(SourceModel source, DestModel destination, ResolutionContext context) |
|||
{ |
|||
destination.Name = nameof(CustomMappingActionService); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,5 +1,5 @@ |
|||
{ |
|||
"ConnectionStrings": { |
|||
"Default": "Server=localhost;Database=BackgroundJobsDemoApp;Trusted_Connection=True;MultipleActiveResultSets=true" |
|||
"Default": "Server=localhost;Database=BackgroundJobsDemoApp;Trusted_Connection=True" |
|||
} |
|||
} |
|||
@ -1,5 +1,5 @@ |
|||
{ |
|||
"ConnectionStrings": { |
|||
"Default": "Server=localhost;Database=BackgroundJobsDemoApp;Trusted_Connection=True;MultipleActiveResultSets=true" |
|||
"Default": "Server=localhost;Database=BackgroundJobsDemoApp;Trusted_Connection=True" |
|||
} |
|||
} |
|||
@ -1,5 +1,5 @@ |
|||
{ |
|||
"ConnectionStrings": { |
|||
"Default": "Server=localhost;Database=BlobStoring_Host;Trusted_Connection=True;MultipleActiveResultSets=true" |
|||
"Default": "Server=localhost;Database=BlobStoring_Host;Trusted_Connection=True" |
|||
} |
|||
} |
|||
|
|||
@ -1,6 +1,6 @@ |
|||
{ |
|||
"ConnectionStrings": { |
|||
"SqlServer": "Server=localhost;Database=BloggingTestApp;Trusted_Connection=True;MultipleActiveResultSets=true", |
|||
"SqlServer": "Server=localhost;Database=BloggingTestApp;Trusted_Connection=True", |
|||
"MongoDb": "mongodb://localhost:27017/BloggingTestApp" |
|||
} |
|||
} |
|||
@ -1,5 +1,5 @@ |
|||
{ |
|||
"ConnectionStrings": { |
|||
"Default": "Server=localhost;Database=CmsKit_Unified;Trusted_Connection=True;MultipleActiveResultSets=true" |
|||
"Default": "Server=localhost;Database=CmsKit_Unified;Trusted_Connection=True" |
|||
} |
|||
} |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
{ |
|||
"ConnectionString": "Server=localhost;Database=VoloDocs;Trusted_Connection=True;MultipleActiveResultSets=true" |
|||
"ConnectionString": "Server=localhost;Database=VoloDocs;Trusted_Connection=True" |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Authorization.Permissions; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Entities.Events; |
|||
using Volo.Abp.EventBus; |
|||
using Volo.Abp.Identity; |
|||
|
|||
namespace Volo.Abp.PermissionManagement.Identity |
|||
{ |
|||
public class RoleDeletedEventHandler : |
|||
ILocalEventHandler<EntityDeletedEventData<IdentityRole>>, |
|||
ITransientDependency |
|||
{ |
|||
protected IPermissionManager PermissionManager { get; } |
|||
|
|||
public RoleDeletedEventHandler(IPermissionManager permissionManager) |
|||
{ |
|||
PermissionManager = permissionManager; |
|||
} |
|||
|
|||
public virtual async Task HandleEventAsync(EntityDeletedEventData<IdentityRole> eventData) |
|||
{ |
|||
await PermissionManager.DeleteAsync(RolePermissionValueProvider.ProviderName, eventData.Entity.Name); |
|||
} |
|||
} |
|||
} |
|||
@ -1,5 +1,5 @@ |
|||
{ |
|||
"ConnectionStrings": { |
|||
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=MyProjectName_Unified;Trusted_Connection=True;MultipleActiveResultSets=true" |
|||
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=MyProjectName_Unified;Trusted_Connection=True" |
|||
} |
|||
} |
|||
Loading…
Reference in new issue