From f8a251afb9a9549be185279bc5ecad4c100b2467 Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Mon, 29 Jun 2020 16:20:24 +0800 Subject: [PATCH 01/77] Storage cache in the unit of work. Resolve #4040 --- .../Volo/Abp/Caching/AbpCachingModule.cs | 2 + .../Volo/Abp/Caching/DistributedCache.cs | 546 ++++++++++++++---- .../Volo/Abp/Caching/IDistributedCache.cs | 34 +- .../Volo/Abp/Caching/UnitOfWorkCacheItem.cs | 43 ++ .../Caching/UnitOfWorkCacheItemExtensions.cs | 11 + .../Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs | 4 +- .../Volo/Abp/Uow/UnitOfWorkExtensions.cs | 45 +- .../Abp/Caching/DistributedCache_Tests.cs | 244 +++++++- .../FeatureManagementStore.cs | 29 +- .../FeatureManagementStore_Tests.cs | 51 ++ .../SettingManagementStore.cs | 31 +- .../SettingManagementStore_Tests.cs | 34 ++ 12 files changed, 924 insertions(+), 150 deletions(-) create mode 100644 framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItem.cs create mode 100644 framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItemExtensions.cs diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs index ba59bf039e..d133ac110b 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs @@ -5,12 +5,14 @@ using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; using Volo.Abp.Serialization; using Volo.Abp.Threading; +using Volo.Abp.Uow; namespace Volo.Abp.Caching { [DependsOn( typeof(AbpThreadingModule), typeof(AbpSerializationModule), + typeof(AbpUnitOfWorkModule), typeof(AbpMultiTenancyModule), typeof(AbpJsonModule))] public class AbpCachingModule : AbpModule diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs index 5bb1dd2481..28082832b0 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs @@ -14,6 +14,7 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.ExceptionHandling; using Volo.Abp.MultiTenancy; using Volo.Abp.Threading; +using Volo.Abp.Uow; namespace Volo.Abp.Caching { @@ -30,13 +31,15 @@ namespace Volo.Abp.Caching ICancellationTokenProvider cancellationTokenProvider, IDistributedCacheSerializer serializer, IDistributedCacheKeyNormalizer keyNormalizer, - IHybridServiceScopeFactory serviceScopeFactory) : base( - distributedCacheOption: distributedCacheOption, - cache: cache, - cancellationTokenProvider: cancellationTokenProvider, - serializer: serializer, - keyNormalizer: keyNormalizer, - serviceScopeFactory: serviceScopeFactory) + IHybridServiceScopeFactory serviceScopeFactory, + IUnitOfWorkManager unitOfWorkManager) : base( + distributedCacheOption: distributedCacheOption, + cache: cache, + cancellationTokenProvider: cancellationTokenProvider, + serializer: serializer, + keyNormalizer: keyNormalizer, + serviceScopeFactory: serviceScopeFactory, + unitOfWorkManager:unitOfWorkManager) { } } @@ -50,6 +53,8 @@ namespace Volo.Abp.Caching public class DistributedCache : IDistributedCache where TCacheItem : class { + public const string DistributedCacheName = "AbpDistributedCache"; + public ILogger> Logger { get; set; } protected string CacheName { get; set; } @@ -66,6 +71,8 @@ namespace Volo.Abp.Caching protected IHybridServiceScopeFactory ServiceScopeFactory { get; } + protected IUnitOfWorkManager UnitOfWorkManager { get; } + protected SemaphoreSlim SyncSemaphore { get; } protected DistributedCacheEntryOptions DefaultCacheOptions; @@ -78,7 +85,8 @@ namespace Volo.Abp.Caching ICancellationTokenProvider cancellationTokenProvider, IDistributedCacheSerializer serializer, IDistributedCacheKeyNormalizer keyNormalizer, - IHybridServiceScopeFactory serviceScopeFactory) + IHybridServiceScopeFactory serviceScopeFactory, + IUnitOfWorkManager unitOfWorkManager) { _distributedCacheOption = distributedCacheOption.Value; Cache = cache; @@ -87,6 +95,7 @@ namespace Volo.Abp.Caching Serializer = serializer; KeyNormalizer = keyNormalizer; ServiceScopeFactory = serviceScopeFactory; + UnitOfWorkManager = unitOfWorkManager; SyncSemaphore = new SemaphoreSlim(1, 1); @@ -133,14 +142,25 @@ namespace Volo.Abp.Caching /// Gets a cache item with the given key. If no cache item is found for the given key then returns null. /// /// The key of cached item to be retrieved from the cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. /// The cache item, or null. public virtual TCacheItem Get( TCacheKey key, + bool considerUow = false, bool? hideErrors = null) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + if (ShouldConsiderUow(considerUow)) + { + var value = GetUnitOfWorkCache().GetOrDefault(key)?.GetUnRemovedValueOrNull(); + if (value != null) + { + return value; + } + } + byte[] cachedBytes; try @@ -163,6 +183,7 @@ namespace Volo.Abp.Caching public virtual KeyValuePair[] GetMany( IEnumerable keys, + bool considerUow = false, bool? hideErrors = null) { var keyArray = keys.ToArray(); @@ -172,33 +193,57 @@ namespace Volo.Abp.Caching { return GetManyFallback( keyArray, + considerUow, hideErrors ); } + var cachedValues = new List>(); + var notCachedKeys = new List(); + if (ShouldConsiderUow(considerUow)) + { + var cache = GetUnitOfWorkCache(); + foreach (var key in keyArray) + { + var value = cache.GetOrDefault(key)?.GetUnRemovedValueOrNull(); + if (value != null) + { + cachedValues.Add(new KeyValuePair(key, value)); + } + } + + notCachedKeys = keyArray.Except(cachedValues.Select(x => x.Key)).ToList(); + if (!notCachedKeys.Any()) + { + return cachedValues.ToArray(); + } + } + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; byte[][] cachedBytes; + var readKeys = notCachedKeys.Any() ? notCachedKeys.ToArray() : keyArray; try { - cachedBytes = cacheSupportsMultipleItems.GetMany(keyArray.Select(NormalizeKey)); + cachedBytes = cacheSupportsMultipleItems.GetMany(readKeys.Select(NormalizeKey)); } catch (Exception ex) { if (hideErrors == true) { HandleException(ex); - return ToCacheItemsWithDefaultValues(keyArray); + return ToCacheItemsWithDefaultValues(readKeys); } throw; } - - return ToCacheItems(cachedBytes, keyArray); + + return cachedValues.Concat(ToCacheItems(cachedBytes, readKeys)).ToArray(); } protected virtual KeyValuePair[] GetManyFallback( TCacheKey[] keys, + bool considerUow = false, bool? hideErrors = null) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -208,7 +253,7 @@ namespace Volo.Abp.Caching return keys .Select(key => new KeyValuePair( key, - Get(key, hideErrors: false) + Get(key, considerUow, hideErrors: false) ) ).ToArray(); } @@ -226,6 +271,7 @@ namespace Volo.Abp.Caching public virtual async Task[]> GetManyAsync( IEnumerable keys, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default) { @@ -236,18 +282,42 @@ namespace Volo.Abp.Caching { return await GetManyFallbackAsync( keyArray, + considerUow, hideErrors, token ); } + var cachedValues = new List>(); + var notCachedKeys = new List(); + if (ShouldConsiderUow(considerUow)) + { + var cache = GetUnitOfWorkCache(); + foreach (var key in keyArray) + { + var value = cache.GetOrDefault(key)?.GetUnRemovedValueOrNull(); + if (value != null) + { + cachedValues.Add(new KeyValuePair(key, value)); + } + } + + notCachedKeys = keyArray.Except(cachedValues.Select(x => x.Key)).ToList(); + if (!notCachedKeys.Any()) + { + return cachedValues.ToArray(); + } + } + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; byte[][] cachedBytes; + var readKeys = notCachedKeys.Any() ? notCachedKeys.ToArray() : keyArray; + try { cachedBytes = await cacheSupportsMultipleItems.GetManyAsync( - keyArray.Select(NormalizeKey), + readKeys.Select(NormalizeKey), CancellationTokenProvider.FallbackToProvider(token) ); } @@ -256,17 +326,18 @@ namespace Volo.Abp.Caching if (hideErrors == true) { await HandleExceptionAsync(ex); - return ToCacheItemsWithDefaultValues(keyArray); + return ToCacheItemsWithDefaultValues(readKeys); } throw; } - - return ToCacheItems(cachedBytes, keyArray); + + return cachedValues.Concat(ToCacheItems(cachedBytes, readKeys)).ToArray(); } protected virtual async Task[]> GetManyFallbackAsync( TCacheKey[] keys, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default) { @@ -280,7 +351,7 @@ namespace Volo.Abp.Caching { result.Add(new KeyValuePair( key, - await GetAsync(key, false, token)) + await GetAsync(key, considerUow, hideErrors: false, token: token)) ); } @@ -302,16 +373,27 @@ namespace Volo.Abp.Caching /// Gets a cache item with the given key. If no cache item is found for the given key then returns null. /// /// The key of cached item to be retrieved from the cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. /// The for the task. /// The cache item, or null. public virtual async Task GetAsync( TCacheKey key, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + if (ShouldConsiderUow(considerUow)) + { + var value = GetUnitOfWorkCache().GetOrDefault(key)?.GetUnRemovedValueOrNull(); + if (value != null) + { + return value; + } + } + byte[] cachedBytes; try @@ -347,15 +429,17 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The factory delegate is used to provide the cache item when no cache item is found for the given . /// The cache options for the factory delegate. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. /// The cache item. public virtual TCacheItem GetOrAdd( TCacheKey key, Func factory, Func optionsFactory = null, + bool considerUow = false, bool? hideErrors = null) { - var value = Get(key, hideErrors); + var value = Get(key, considerUow, hideErrors); if (value != null) { return value; @@ -363,14 +447,28 @@ namespace Volo.Abp.Caching using (SyncSemaphore.Lock()) { - value = Get(key, hideErrors); + value = Get(key, considerUow, hideErrors); if (value != null) { return value; } value = factory(); - Set(key, value, optionsFactory?.Invoke(), hideErrors); + + if (ShouldConsiderUow(considerUow)) + { + var uowCache = GetUnitOfWorkCache(); + if (uowCache.TryGetValue(key, out var item)) + { + item.SetValue(value); + } + else + { + uowCache.Add(key, new UnitOfWorkCacheItem(value)); + } + } + + Set(key, value, optionsFactory?.Invoke(), considerUow, hideErrors); } return value; @@ -384,17 +482,19 @@ namespace Volo.Abp.Caching /// The factory delegate is used to provide the cache item when no cache item is found for the given . /// The cache options for the factory delegate. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The for the task. /// The cache item. public virtual async Task GetOrAddAsync( TCacheKey key, Func> factory, Func optionsFactory = null, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default) { token = CancellationTokenProvider.FallbackToProvider(token); - var value = await GetAsync(key, hideErrors, token); + var value = await GetAsync(key, considerUow, hideErrors, token); if (value != null) { return value; @@ -402,14 +502,28 @@ namespace Volo.Abp.Caching using (await SyncSemaphore.LockAsync(token)) { - value = await GetAsync(key, hideErrors, token); + value = await GetAsync(key, considerUow, hideErrors, token); if (value != null) { return value; } value = await factory(); - await SetAsync(key, value, optionsFactory?.Invoke(), hideErrors, token); + + if (ShouldConsiderUow(considerUow)) + { + var uowCache = GetUnitOfWorkCache(); + if (uowCache.TryGetValue(key, out var item)) + { + item.SetValue(value); + } + else + { + uowCache.Add(key, new UnitOfWorkCacheItem(value)); + } + } + + await SetAsync(key, value, optionsFactory?.Invoke(), considerUow, hideErrors, token); } return value; @@ -421,41 +535,70 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The cache item value to set in the cache. /// The cache options for the value. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. public virtual void Set( TCacheKey key, TCacheItem value, DistributedCacheEntryOptions options = null, + bool considerUow = false, bool? hideErrors = null) { - hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; - - try + void SetRealCache() { - Cache.Set( - NormalizeKey(key), - Serializer.Serialize(value), - options ?? DefaultCacheOptions - ); + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + Cache.Set( + NormalizeKey(key), + Serializer.Serialize(value), + options ?? DefaultCacheOptions + ); + } + catch (Exception ex) + { + if (hideErrors == true) + { + HandleException(ex); + return; + } + + throw; + } } - catch (Exception ex) + + if (ShouldConsiderUow(considerUow)) { - if (hideErrors == true) + var cache = GetUnitOfWorkCache(); + if (cache.TryGetValue(key, out _)) { - HandleException(ex); - return; + cache[key].SetValue(value); + } + else + { + cache.Add(key, new UnitOfWorkCacheItem(value)); } - throw; + // ReSharper disable once PossibleNullReferenceException + UnitOfWorkManager.Current.OnCompleted(() => + { + SetRealCache(); + return Task.CompletedTask; + }); + } + else + { + SetRealCache(); } } - /// /// Sets the cache item value for the provided key. /// /// The key of cached item to be retrieved from the cache. /// The cache item value to set in the cache. /// The cache options for the value. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. /// The for the task. /// The indicating that the operation is asynchronous. @@ -463,35 +606,60 @@ namespace Volo.Abp.Caching TCacheKey key, TCacheItem value, DistributedCacheEntryOptions options = null, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default) { - hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; - - try + async Task SetRealCache() { - await Cache.SetAsync( - NormalizeKey(key), - Serializer.Serialize(value), - options ?? DefaultCacheOptions, - CancellationTokenProvider.FallbackToProvider(token) - ); - } - catch (Exception ex) - { - if (hideErrors == true) + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try { - await HandleExceptionAsync(ex); - return; + await Cache.SetAsync( + NormalizeKey(key), + Serializer.Serialize(value), + options ?? DefaultCacheOptions, + CancellationTokenProvider.FallbackToProvider(token) + ); } + catch (Exception ex) + { + if (hideErrors == true) + { + await HandleExceptionAsync(ex); + return; + } - throw; + throw; + } } + + if (ShouldConsiderUow(considerUow)) + { + var cache = GetUnitOfWorkCache(); + if (cache.TryGetValue(key, out _)) + { + cache[key].SetValue(value); + } + else + { + cache.Add(key, new UnitOfWorkCacheItem(value)); + } + + // ReSharper disable once PossibleNullReferenceException + UnitOfWorkManager.Current.OnCompleted(SetRealCache); + } + else + { + await SetRealCache(); + } } public void SetMany( IEnumerable> items, DistributedCacheEntryOptions options = null, + bool considerUow = false, bool? hideErrors = null) { var itemsArray = items.ToArray(); @@ -502,40 +670,73 @@ namespace Volo.Abp.Caching SetManyFallback( itemsArray, options, + considerUow, hideErrors ); - + return; } - - hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; - try + void SetRealCache() { - cacheSupportsMultipleItems.SetMany( - ToRawCacheItems(itemsArray), - options ?? DefaultCacheOptions - ); + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + cacheSupportsMultipleItems.SetMany( + ToRawCacheItems(itemsArray), + options ?? DefaultCacheOptions + ); + } + catch (Exception ex) + { + if (hideErrors == true) + { + HandleException(ex); + return; + } + + throw; + } } - catch (Exception ex) + + if (ShouldConsiderUow(considerUow)) { - if (hideErrors == true) + var cache = GetUnitOfWorkCache(); + + foreach (var pair in itemsArray) { - HandleException(ex); - return; + if (cache.TryGetValue(pair.Key, out _)) + { + cache[pair.Key].SetValue(pair.Value); + } + else + { + cache.Add(pair.Key, new UnitOfWorkCacheItem(pair.Value)); + } } - throw; + // ReSharper disable once PossibleNullReferenceException + UnitOfWorkManager.Current.OnCompleted(() => + { + SetRealCache(); + return Task.CompletedTask; + }); + } + else + { + SetRealCache(); } } - + protected virtual void SetManyFallback( KeyValuePair[] items, DistributedCacheEntryOptions options = null, + bool considerUow = false, bool? hideErrors = null) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; - + try { foreach (var item in items) @@ -543,7 +744,8 @@ namespace Volo.Abp.Caching Set( item.Key, item.Value, - options: options, + options, + considerUow, hideErrors: false ); } @@ -563,6 +765,7 @@ namespace Volo.Abp.Caching public virtual async Task SetManyAsync( IEnumerable> items, DistributedCacheEntryOptions options = null, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default) { @@ -574,38 +777,67 @@ namespace Volo.Abp.Caching await SetManyFallbackAsync( itemsArray, options, + considerUow, hideErrors, token ); - + return; } - - hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; - try + async Task SetRealCache() { - await cacheSupportsMultipleItems.SetManyAsync( - ToRawCacheItems(itemsArray), - options ?? DefaultCacheOptions, - CancellationTokenProvider.FallbackToProvider(token) - ); + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + await cacheSupportsMultipleItems.SetManyAsync( + ToRawCacheItems(itemsArray), + options ?? DefaultCacheOptions, + CancellationTokenProvider.FallbackToProvider(token) + ); + } + catch (Exception ex) + { + if (hideErrors == true) + { + await HandleExceptionAsync(ex); + return; + } + + throw; + } } - catch (Exception ex) + + if (ShouldConsiderUow(considerUow)) { - if (hideErrors == true) + var cache = GetUnitOfWorkCache(); + + foreach (var pair in itemsArray) { - await HandleExceptionAsync(ex); - return; + if (cache.TryGetValue(pair.Key, out _)) + { + cache[pair.Key].SetValue(pair.Value); + } + else + { + cache.Add(pair.Key, new UnitOfWorkCacheItem(pair.Value)); + } } - throw; + // ReSharper disable once PossibleNullReferenceException + UnitOfWorkManager.Current.OnCompleted(SetRealCache); + } + else + { + await SetRealCache(); } } - + protected virtual async Task SetManyFallbackAsync( KeyValuePair[] items, DistributedCacheEntryOptions options = null, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default) { @@ -618,7 +850,8 @@ namespace Volo.Abp.Caching await SetAsync( item.Key, item.Value, - options: options, + options, + considerUow, hideErrors: false, token: token ); @@ -636,9 +869,14 @@ namespace Volo.Abp.Caching } } + /// + /// Refreshes the cache value of the given key, and resets its sliding expiration timeout. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. public virtual void Refresh( - TCacheKey key, bool? - hideErrors = null) + TCacheKey key, + bool? hideErrors = null) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -658,6 +896,13 @@ namespace Volo.Abp.Caching } } + /// + /// Refreshes the cache value of the given key, and resets its sliding expiration timeout. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The indicating that the operation is asynchronous. public virtual async Task RefreshAsync( TCacheKey key, bool? hideErrors = null, @@ -681,48 +926,106 @@ namespace Volo.Abp.Caching } } + /// + /// Removes the cache item for given key from cache. + /// + /// The key of cached item to be retrieved from the cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. public virtual void Remove( TCacheKey key, + bool considerUow = false, bool? hideErrors = null) { - hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; - - try + void RemoveRealCache() { - Cache.Remove(NormalizeKey(key)); + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + Cache.Remove(NormalizeKey(key)); + } + catch (Exception ex) + { + if (hideErrors == true) + { + HandleException(ex); + return; + } + + throw; + } } - catch (Exception ex) + + if (ShouldConsiderUow(considerUow)) { - if (hideErrors == true) + var cache = GetUnitOfWorkCache(); + if (cache.TryGetValue(key, out _)) { - HandleException(ex); - return; + cache[key].RemoveValue(); } - throw; + // ReSharper disable once PossibleNullReferenceException + UnitOfWorkManager.Current.OnCompleted(() => + { + RemoveRealCache(); + return Task.CompletedTask; + }); + } + else + { + RemoveRealCache(); } } + /// + /// Removes the cache item for given key from cache. + /// + /// The key of cached item to be retrieved from the cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The indicating that the operation is asynchronous. public virtual async Task RemoveAsync( TCacheKey key, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default) { - hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; - - try + async Task RemoveRealCache() { - await Cache.RemoveAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token)); + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + await Cache.RemoveAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token)); + } + catch (Exception ex) + { + if (hideErrors == true) + { + await HandleExceptionAsync(ex); + return; + } + + throw; + } } - catch (Exception ex) + + if (ShouldConsiderUow(considerUow)) { - if (hideErrors == true) + var cache = GetUnitOfWorkCache(); + if (cache.TryGetValue(key, out _)) { - await HandleExceptionAsync(ex); - return; + cache[key].RemoveValue(); } - throw; + // ReSharper disable once PossibleNullReferenceException + UnitOfWorkManager.Current.OnCompleted(RemoveRealCache); + } + else + { + await RemoveRealCache(); } } @@ -730,7 +1033,7 @@ namespace Volo.Abp.Caching { AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); } - + protected virtual async Task HandleExceptionAsync(Exception ex) { Logger.LogException(ex, LogLevel.Warning); @@ -742,7 +1045,7 @@ namespace Volo.Abp.Caching .NotifyAsync(new ExceptionNotificationContext(ex, LogLevel.Warning)); } } - + protected virtual KeyValuePair[] ToCacheItems(byte[][] itemBytes, TCacheKey[] itemKeys) { if (itemBytes.Length != itemKeys.Length) @@ -764,7 +1067,7 @@ namespace Volo.Abp.Caching return result.ToArray(); } - + [CanBeNull] protected virtual TCacheItem ToCacheItem([CanBeNull] byte[] bytes) { @@ -786,12 +1089,33 @@ namespace Volo.Abp.Caching ) ).ToArray(); } - + private static KeyValuePair[] ToCacheItemsWithDefaultValues(TCacheKey[] keys) { return keys .Select(key => new KeyValuePair(key, default)) .ToArray(); } + + protected virtual bool ShouldConsiderUow(bool considerUow) + { + return considerUow && UnitOfWorkManager.Current != null; + } + + protected virtual string GetUnitOfWorkCacheKey() + { + return DistributedCacheName + CacheName; + } + + protected virtual Dictionary> GetUnitOfWorkCache() + { + if (UnitOfWorkManager.Current == null) + { + throw new AbpException($"There is no unit of work in the current context, The {GetType().Name} can only be used in a unit of work."); + } + + return UnitOfWorkManager.Current.GetOrAddItem(GetUnitOfWorkCacheKey(), + key => new Dictionary>()); + } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs index 80fe124785..1b0e58815e 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs @@ -29,42 +29,48 @@ namespace Volo.Abp.Caching /// Gets a cache item with the given key. If no cache item is found for the given key then returns null. /// /// The key of cached item to be retrieved from the cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. /// The cache item, or null. TCacheItem Get( TCacheKey key, + bool considerUow = false, bool? hideErrors = null ); - + /// /// Gets multiple cache items with the given keys. /// /// The returned list contains exactly the same count of items specified in the given keys. /// An item in the return list can not be null, but an item in the list has null value - /// if the related key not found in the cache. + /// if the related key not found in the cache. /// /// The keys of cached items to be retrieved from the cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. /// List of cache items. KeyValuePair[] GetMany( IEnumerable keys, + bool considerUow = false, bool? hideErrors = null ); - + /// /// Gets multiple cache items with the given keys. /// /// The returned list contains exactly the same count of items specified in the given keys. /// An item in the return list can not be null, but an item in the list has null value /// if the related key not found in the cache. - /// + /// /// /// The keys of cached items to be retrieved from the cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. /// /// The for the task. /// List of cache items. Task[]> GetManyAsync( IEnumerable keys, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default ); @@ -74,10 +80,12 @@ namespace Volo.Abp.Caching /// /// The key of cached item to be retrieved from the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The for the task. /// The cache item, or null. Task GetAsync( [NotNull] TCacheKey key, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default ); @@ -89,12 +97,14 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The factory delegate is used to provide the cache item when no cache item is found for the given . /// The cache options for the factory delegate. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. /// The cache item. TCacheItem GetOrAdd( TCacheKey key, Func factory, Func optionsFactory = null, + bool considerUow = false, bool? hideErrors = null ); @@ -105,6 +115,7 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The factory delegate is used to provide the cache item when no cache item is found for the given . /// The cache options for the factory delegate. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. /// The for the task. /// The cache item. @@ -112,6 +123,7 @@ namespace Volo.Abp.Caching [NotNull] TCacheKey key, Func> factory, Func optionsFactory = null, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default ); @@ -122,11 +134,13 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The cache item value to set in the cache. /// The cache options for the value. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. void Set( TCacheKey key, TCacheItem value, DistributedCacheEntryOptions options = null, + bool considerUow = false, bool? hideErrors = null ); @@ -136,6 +150,7 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The cache item value to set in the cache. /// The cache options for the value. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. /// The for the task. /// The indicating that the operation is asynchronous. @@ -143,6 +158,7 @@ namespace Volo.Abp.Caching [NotNull] TCacheKey key, [NotNull] TCacheItem value, [CanBeNull] DistributedCacheEntryOptions options = null, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default ); @@ -153,25 +169,29 @@ namespace Volo.Abp.Caching /// /// Items to set on the cache /// The cache options for the value. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. void SetMany( IEnumerable> items, DistributedCacheEntryOptions options = null, + bool considerUow = false, bool? hideErrors = null ); - + /// /// Sets multiple cache items. /// Based on the implementation, this can be more efficient than setting multiple items individually. /// /// Items to set on the cache /// The cache options for the value. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. /// The for the task. /// The indicating that the operation is asynchronous. Task SetManyAsync( IEnumerable> items, DistributedCacheEntryOptions options = null, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default ); @@ -203,9 +223,11 @@ namespace Volo.Abp.Caching /// Removes the cache item for given key from cache. /// /// The key of cached item to be retrieved from the cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. void Remove( TCacheKey key, + bool considerUow = false, bool? hideErrors = null ); @@ -213,11 +235,13 @@ namespace Volo.Abp.Caching /// Removes the cache item for given key from cache. /// /// The key of cached item to be retrieved from the cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. /// The for the task. /// The indicating that the operation is asynchronous. Task RemoveAsync( TCacheKey key, + bool considerUow = false, bool? hideErrors = null, CancellationToken token = default ); diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItem.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItem.cs new file mode 100644 index 0000000000..c37475d022 --- /dev/null +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItem.cs @@ -0,0 +1,43 @@ +using System; + +namespace Volo.Abp.Caching +{ + [Serializable] + public class UnitOfWorkCacheItem + where TValue : class + { + public bool IsRemoved { get; set; } + + public TValue Value { get; set; } + + public UnitOfWorkCacheItem() + { + + } + + public UnitOfWorkCacheItem(TValue value) + { + Value = value; + } + + public UnitOfWorkCacheItem(TValue value, bool isRemoved) + { + Value = value; + IsRemoved = isRemoved; + } + + public UnitOfWorkCacheItem SetValue(TValue value) + { + Value = value; + IsRemoved = false; + return this; + } + + public UnitOfWorkCacheItem RemoveValue() + { + Value = null; + IsRemoved = true; + return this; + } + } +} diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItemExtensions.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItemExtensions.cs new file mode 100644 index 0000000000..46757f80cf --- /dev/null +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItemExtensions.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.Caching +{ + public static class UnitOfWorkCacheItemExtensions + { + public static TValue GetUnRemovedValueOrNull(this UnitOfWorkCacheItem item) + where TValue : class + { + return item != null && !item.IsRemoved ? item.Value : null; + } + } +} diff --git a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs index b42b745fb4..b4158229fe 100644 --- a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs +++ b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; @@ -316,4 +316,4 @@ namespace Volo.Abp.Uow return $"[UnitOfWork {Id}]"; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkExtensions.cs b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkExtensions.cs index 5c01f54a99..c183a70390 100644 --- a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkExtensions.cs +++ b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkExtensions.cs @@ -1,4 +1,7 @@ -using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; namespace Volo.Abp.Uow { @@ -10,5 +13,43 @@ namespace Volo.Abp.Uow return unitOfWork.IsReserved && unitOfWork.ReservationName == reservationName; } + + public static void AddItem([NotNull] this IUnitOfWork unitOfWork, string key, TValue value) + where TValue : class + { + Check.NotNull(unitOfWork, nameof(unitOfWork)); + + if (!unitOfWork.Items.ContainsKey(key)) + { + unitOfWork.Items[key] = value; + } + else + { + unitOfWork.Items.Add(key, value); + } + } + + public static TValue GetItemOrDefault([NotNull] this IUnitOfWork unitOfWork, string key) + where TValue : class + { + Check.NotNull(unitOfWork, nameof(unitOfWork)); + + return unitOfWork.Items.FirstOrDefault(x => x.Key == key).As(); + } + + public static TValue GetOrAddItem([NotNull] this IUnitOfWork unitOfWork, string key, Func factory) + where TValue : class + { + Check.NotNull(unitOfWork, nameof(unitOfWork)); + + return unitOfWork.Items.GetOrAdd(key, factory).As(); + } + + public static void RemoveItem([NotNull] this IUnitOfWork unitOfWork, string key) + { + Check.NotNull(unitOfWork, nameof(unitOfWork)); + + unitOfWork.Items.RemoveAll(x => x.Key == key); + } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs index 533362c851..61a61ac2cd 100644 --- a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs +++ b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Testing; +using Volo.Abp.Uow; using Xunit; namespace Volo.Abp.Caching @@ -30,7 +31,7 @@ namespace Volo.Abp.Caching cacheItem.ShouldNotBeNull(); cacheItem.Name.ShouldBe(personName); - //Remove + //Remove await personCache.RemoveAsync(cacheKey); //Get (not exists since removed) @@ -111,7 +112,7 @@ namespace Volo.Abp.Caching cacheItem1.ShouldNotBeNull(); cacheItem1.Name.ShouldBe(personName); - //Remove + //Remove await personCache.RemoveAsync(cacheKey); @@ -145,7 +146,7 @@ namespace Volo.Abp.Caching cacheItem.ShouldNotBeNull(); cacheItem.Name.ShouldBe(personName); - //Remove + //Remove await personCache.RemoveAsync(cacheKey); //Get (not exists since removed) @@ -227,7 +228,7 @@ namespace Volo.Abp.Caching cacheItem1.ShouldNotBeNull(); cacheItem1.Name.ShouldBe(personName); - //Remove + //Remove await personCache.RemoveAsync(cacheKey); @@ -261,7 +262,7 @@ namespace Volo.Abp.Caching cacheItem.ShouldNotBeNull(); cacheItem.Name.ShouldBe(personName); - //Remove + //Remove await personCache.RemoveAsync(cacheKey); //Get (not exists since removed) @@ -299,7 +300,7 @@ namespace Volo.Abp.Caching cacheItem2.ShouldNotBeNull(); cacheItem2.Name.ShouldBe(personName); - //Remove + //Remove await personCache.RemoveAsync(cache1Key); await personCache.RemoveAsync(cache2Key); @@ -310,6 +311,235 @@ namespace Volo.Abp.Caching cacheItem2.ShouldBeNull(); } + [Fact] + public async Task Cache_Should_Only_Available_In_Uow_For_GetAsync() + { + const string key = "testkey"; + + using (var uow = GetRequiredService().Begin()) + { + var personCache = GetRequiredService>(); + + var cacheValue = await personCache.GetAsync(key, considerUow: true); + cacheValue.ShouldBeNull(); + + await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true); + + cacheValue = await personCache.GetAsync(key, considerUow: true); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + + uow.OnCompleted(async () => + { + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + }); + + await uow.CompleteAsync(); + } + } + + [Fact] + public async Task Cache_Should_Rollback_With_Uow_For_GetAsync() + { + const string key = "testkey"; + var personCache = GetRequiredService>(); + + var cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + + using (var uow = GetRequiredService().Begin()) + { + cacheValue = await personCache.GetAsync(key, considerUow: true); + cacheValue.ShouldBeNull(); + + await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true); + + cacheValue = await personCache.GetAsync(key, considerUow: true); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + } + + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + } + + [Fact] + public async Task Cache_Should_Only_Available_In_Uow_For_GetOrAddAsync() + { + const string key = "testkey"; + + using (var uow = GetRequiredService().Begin()) + { + var personCache = GetRequiredService>(); + + var cacheValue = await personCache.GetOrAddAsync(key, () => Task.FromResult(new PersonCacheItem("john")), considerUow: true); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + + cacheValue = await personCache.GetAsync(key, considerUow: true); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + + uow.OnCompleted(async () => + { + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + }); + + await uow.CompleteAsync(); + } + } + + [Fact] + public async Task Cache_Should_Rollback_With_Uow_For_GetOrAddAsync() + { + const string key = "testkey"; + var personCache = GetRequiredService>(); + + var cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + + using (var uow = GetRequiredService().Begin()) + { + cacheValue = await personCache.GetOrAddAsync(key, () => Task.FromResult(new PersonCacheItem("john")), considerUow: true); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + + cacheValue = await personCache.GetAsync(key, considerUow: true); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + } + + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + } + + [Fact] + public async Task Cache_Should_Only_Available_In_Uow_For_SetAsync() + { + const string key = "testkey"; + + using (var uow = GetRequiredService().Begin()) + { + var personCache = GetRequiredService>(); + + await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true); + + var cacheValue = await personCache.GetAsync(key, considerUow: true); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + + uow.OnCompleted(async () => + { + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + }); + + await uow.CompleteAsync(); + } + } + + [Fact] + public async Task Cache_Should_Rollback_With_Uow_For_SetAsync() + { + const string key = "testkey"; + var personCache = GetRequiredService>(); + + var cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + + using (var uow = GetRequiredService().Begin()) + { + await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true); + + cacheValue = await personCache.GetAsync(key, considerUow: true); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + } + + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + } + + [Fact] + public async Task Cache_Should_Only_Available_In_Uow_For_RemoveAsync() + { + const string key = "testkey"; + + using (var uow = GetRequiredService().Begin()) + { + var personCache = GetRequiredService>(); + + await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true); + + var cacheValue = await personCache.GetAsync(key, considerUow: true); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + + await personCache.RemoveAsync(key, considerUow: true); + + cacheValue = await personCache.GetAsync(key, considerUow: true); + cacheValue.ShouldBeNull(); + + uow.OnCompleted(async () => + { + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + }); + + await uow.CompleteAsync(); + } + } + + [Fact] + public async Task Cache_Should_Rollback_With_Uow_For_RemoveAsync() + { + const string key = "testkey"; + var personCache = GetRequiredService>(); + + var cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + + using (var uow = GetRequiredService().Begin()) + { + await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true); + + cacheValue = await personCache.GetAsync(key, considerUow: true); + cacheValue.ShouldNotBeNull(); + cacheValue.Name.ShouldBe("john"); + + await personCache.RemoveAsync(key, considerUow: true); + + cacheValue = await personCache.GetAsync(key, considerUow: true); + cacheValue.ShouldBeNull(); + } + + cacheValue = await personCache.GetAsync(key, considerUow: false); + cacheValue.ShouldBeNull(); + } + [Fact] public async Task Should_Set_And_Get_Multiple_Items_Async() { @@ -340,4 +570,4 @@ namespace Volo.Abp.Caching (await personCache.GetAsync("baris")).ShouldBeNull(); } } -} \ No newline at end of file +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs index 26388fdaf9..5b892a91bb 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs @@ -5,6 +5,7 @@ using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; using Volo.Abp.Features; using Volo.Abp.Guids; +using Volo.Abp.Uow; namespace Volo.Abp.FeatureManagement { @@ -27,12 +28,14 @@ namespace Volo.Abp.FeatureManagement FeatureDefinitionManager = featureDefinitionManager; } + [UnitOfWork] public virtual async Task GetOrNullAsync(string name, string providerName, string providerKey) { var cacheItem = await GetCacheItemAsync(name, providerName, providerKey); return cacheItem.Value; } + [UnitOfWork] public virtual async Task SetAsync(string name, string value, string providerName, string providerKey) { var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey); @@ -46,21 +49,25 @@ namespace Volo.Abp.FeatureManagement featureValue.Value = value; await FeatureValueRepository.UpdateAsync(featureValue); } + + await Cache.SetAsync(CalculateCacheKey(name, providerName, providerKey), new FeatureValueCacheItem(featureValue?.Value), considerUow: true); } + [UnitOfWork] public virtual async Task DeleteAsync(string name, string providerName, string providerKey) { var featureValues = await FeatureValueRepository.FindAllAsync(name, providerName, providerKey); foreach (var featureValue in featureValues) { await FeatureValueRepository.DeleteAsync(featureValue); + await Cache.RemoveAsync(CalculateCacheKey(name, providerName, providerKey), considerUow: true); } } protected virtual async Task GetCacheItemAsync(string name, string providerName, string providerKey) { var cacheKey = CalculateCacheKey(name, providerName, providerKey); - var cacheItem = await Cache.GetAsync(cacheKey); + var cacheItem = await Cache.GetAsync(cacheKey, considerUow: true); if (cacheItem != null) { @@ -68,28 +75,28 @@ namespace Volo.Abp.FeatureManagement } cacheItem = new FeatureValueCacheItem(null); - + await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); return cacheItem; } - + private async Task SetCacheItemsAsync( - string providerName, - string providerKey, - string currentName, + string providerName, + string providerKey, + string currentName, FeatureValueCacheItem currentCacheItem) { var featureDefinitions = FeatureDefinitionManager.GetAll(); var featuresDictionary = (await FeatureValueRepository.GetListAsync(providerName, providerKey)) .ToDictionary(s => s.Name, s => s.Value); - - var cacheItems = new List>(); - + + var cacheItems = new List>(); + foreach (var featureDefinition in featureDefinitions) { var featureValue = featuresDictionary.GetOrDefault(featureDefinition.Name); - + cacheItems.Add( new KeyValuePair( CalculateCacheKey(featureDefinition.Name, providerName, providerKey), @@ -103,7 +110,7 @@ namespace Volo.Abp.FeatureManagement } } - await Cache.SetManyAsync(cacheItems); + await Cache.SetManyAsync(cacheItems, considerUow: true); } protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs index a6f040dde3..a5250f1804 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Shouldly; using Volo.Abp.Features; using Volo.Abp.Modularity; +using Volo.Abp.Uow; using Xunit; namespace Volo.Abp.FeatureManagement @@ -14,11 +15,13 @@ namespace Volo.Abp.FeatureManagement { private IFeatureManagementStore FeatureManagementStore { get; set; } private IFeatureValueRepository FeatureValueRepository { get; set; } + private IUnitOfWorkManager UnitOfWorkManager { get; set; } protected FeatureManagementStore_Tests() { FeatureManagementStore = GetRequiredService(); FeatureValueRepository = GetRequiredService(); + UnitOfWorkManager = GetRequiredService(); } [Fact] @@ -73,6 +76,30 @@ namespace Volo.Abp.FeatureManagement TestEditionIds.Regular.ToString())).Value.ShouldBe(false.ToString().ToUpperInvariant()); } + [Fact] + public async Task Set_In_UnitOfWork_Should_Be_Consistent() + { + using (UnitOfWorkManager.Begin()) + { + // Arrange + (await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString())).ShouldNotBeNull(); + + + // Act + await FeatureManagementStore.SetAsync(TestFeatureDefinitionProvider.SocialLogins, + false.ToString().ToUpperInvariant(), + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString()); + + // Assert + (await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString())).ShouldBe(false.ToString().ToUpperInvariant()); + } + } + [Fact] public async Task DeleteAsync() { @@ -94,5 +121,29 @@ namespace Volo.Abp.FeatureManagement } + + [Fact] + public async Task Delete_In_UnitOfWork_Should_Be_Consistent() + { + using (var uow = UnitOfWorkManager.Begin()) + { + // Arrange + (await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString())).ShouldNotBeNull(); + + // Act + await FeatureManagementStore.DeleteAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString()); + + await uow.SaveChangesAsync(); + + // Assert + (await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString())).ShouldBeNull(); + } + } } } diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs index 617d360898..a568184f0e 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs @@ -5,6 +5,7 @@ using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; using Volo.Abp.Guids; using Volo.Abp.Settings; +using Volo.Abp.Uow; namespace Volo.Abp.SettingManagement { @@ -16,8 +17,8 @@ namespace Volo.Abp.SettingManagement protected IGuidGenerator GuidGenerator { get; } public SettingManagementStore( - ISettingRepository settingRepository, - IGuidGenerator guidGenerator, + ISettingRepository settingRepository, + IGuidGenerator guidGenerator, IDistributedCache cache, ISettingDefinitionManager settingDefinitionManager) { @@ -27,11 +28,13 @@ namespace Volo.Abp.SettingManagement SettingDefinitionManager = settingDefinitionManager; } + [UnitOfWork] public virtual async Task GetOrNullAsync(string name, string providerName, string providerKey) { return (await GetCacheItemAsync(name, providerName, providerKey)).Value; } + [UnitOfWork] public virtual async Task SetAsync(string name, string value, string providerName, string providerKey) { var setting = await SettingRepository.FindAsync(name, providerName, providerKey); @@ -45,6 +48,8 @@ namespace Volo.Abp.SettingManagement setting.Value = value; await SettingRepository.UpdateAsync(setting); } + + await Cache.SetAsync(CalculateCacheKey(name, providerName, providerKey), new SettingCacheItem(setting?.Value), considerUow: true); } public virtual async Task> GetListAsync(string providerName, string providerKey) @@ -53,19 +58,21 @@ namespace Volo.Abp.SettingManagement return settings.Select(s => new SettingValue(s.Name, s.Value)).ToList(); } + [UnitOfWork] public virtual async Task DeleteAsync(string name, string providerName, string providerKey) { var setting = await SettingRepository.FindAsync(name, providerName, providerKey); if (setting != null) { await SettingRepository.DeleteAsync(setting); + await Cache.RemoveAsync(CalculateCacheKey(name, providerName, providerKey), considerUow: true); } } protected virtual async Task GetCacheItemAsync(string name, string providerName, string providerKey) { var cacheKey = CalculateCacheKey(name, providerName, providerKey); - var cacheItem = await Cache.GetAsync(cacheKey); + var cacheItem = await Cache.GetAsync(cacheKey, considerUow: true); if (cacheItem != null) { @@ -75,26 +82,26 @@ namespace Volo.Abp.SettingManagement cacheItem = new SettingCacheItem(null); await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); - + return cacheItem; } private async Task SetCacheItemsAsync( - string providerName, - string providerKey, - string currentName, + string providerName, + string providerKey, + string currentName, SettingCacheItem currentCacheItem) { var settingDefinitions = SettingDefinitionManager.GetAll(); var settingsDictionary = (await SettingRepository.GetListAsync(providerName, providerKey)) .ToDictionary(s => s.Name, s => s.Value); - - var cacheItems = new List>(); - + + var cacheItems = new List>(); + foreach (var settingDefinition in settingDefinitions) { var settingValue = settingsDictionary.GetOrDefault(settingDefinition.Name); - + cacheItems.Add( new KeyValuePair( CalculateCacheKey(settingDefinition.Name, providerName, providerKey), @@ -108,7 +115,7 @@ namespace Volo.Abp.SettingManagement } } - await Cache.SetManyAsync(cacheItems); + await Cache.SetManyAsync(cacheItems, considerUow: true); } protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManagementStore_Tests.cs b/modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManagementStore_Tests.cs index 753ba15054..7a405faf2d 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManagementStore_Tests.cs +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManagementStore_Tests.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Settings; +using Volo.Abp.Uow; using Xunit; namespace Volo.Abp.SettingManagement @@ -13,12 +14,14 @@ namespace Volo.Abp.SettingManagement private readonly ISettingManagementStore _settingManagementStore; private readonly ISettingRepository _settingRepository; private readonly SettingTestData _testData; + private readonly IUnitOfWorkManager _unitOfWorkManager; public SettingManagementStore_Tests() { _settingManagementStore = GetRequiredService(); _settingRepository = GetRequiredService(); _testData = GetRequiredService(); + _unitOfWorkManager= GetRequiredService(); } [Fact] @@ -50,6 +53,21 @@ namespace Volo.Abp.SettingManagement (await _settingRepository.FindAsync(_testData.SettingId)).Value.ShouldBe("43"); } + [Fact] + public async Task Set_In_UnitOfWork_Should_Be_Consistent() + { + using (_unitOfWorkManager.Begin()) + { + var value = await _settingManagementStore.GetOrNullAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null); + value.ShouldBe("42"); + + await _settingManagementStore.SetAsync("MySetting1", "43", GlobalSettingValueProvider.ProviderName, null); + + var valueAfterSet = await _settingManagementStore.GetOrNullAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null); + valueAfterSet.ShouldBe("43"); + } + } + [Fact] public async Task DeleteAsync() { @@ -60,5 +78,21 @@ namespace Volo.Abp.SettingManagement (await _settingRepository.FindAsync(_testData.SettingId)).ShouldBeNull(); } + [Fact] + public async Task Delete_In_UnitOfWork_Should_Be_Consistent() + { + using (var uow = _unitOfWorkManager.Begin()) + { + (await _settingManagementStore.GetOrNullAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null)).ShouldNotBeNull(); + + await _settingManagementStore.DeleteAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null); + + await uow.SaveChangesAsync(); + + var value = await _settingManagementStore.GetOrNullAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null); + value.ShouldBeNull(); + } + } + } } From 6f1514da88931591a149a3b4b222f8f0ca7e6ab0 Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Wed, 1 Jul 2020 11:53:04 +0800 Subject: [PATCH 02/77] Switch to the tenant before calling the handleevent method. Resolve #4508 --- .../RabbitMq/RabbitMqDistributedEventBus.cs | 14 ++-- .../Volo.Abp.EventBus.csproj | 1 + .../Volo/Abp/EventBus/AbpEventBusModule.cs | 2 + .../Volo/Abp/EventBus/EventBusBase.cs | 80 +++++++++++++------ .../Volo/Abp/EventBus/Local/LocalEventBus.cs | 8 +- .../LocalDistributedEventBus_Test.cs | 29 ++++++- ...leDistributedSingleInstanceEventHandler.cs | 32 ++++++++ .../Local/EventBus_MultiTenancy_Test.cs | 64 +++++++++++++++ .../Volo/Abp/EventBus/MySimpleEventData.cs | 12 ++- 9 files changed, 203 insertions(+), 39 deletions(-) create mode 100644 framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/MySimpleDistributedSingleInstanceEventHandler.cs create mode 100644 framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_MultiTenancy_Test.cs diff --git a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs index ed7d37db20..e8c7fc6c5b 100644 --- a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs +++ b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs @@ -9,6 +9,7 @@ using RabbitMQ.Client; using RabbitMQ.Client.Events; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus.Distributed; +using Volo.Abp.MultiTenancy; using Volo.Abp.RabbitMQ; using Volo.Abp.Threading; @@ -26,7 +27,7 @@ namespace Volo.Abp.EventBus.RabbitMq protected AbpDistributedEventBusOptions AbpDistributedEventBusOptions { get; } protected IConnectionPool ConnectionPool { get; } protected IRabbitMqSerializer Serializer { get; } - + //TODO: Accessing to the List may not be thread-safe! protected ConcurrentDictionary> HandlerFactories { get; } protected ConcurrentDictionary EventTypes { get; } @@ -37,17 +38,18 @@ namespace Volo.Abp.EventBus.RabbitMq IOptions options, IConnectionPool connectionPool, IRabbitMqSerializer serializer, - IServiceScopeFactory serviceScopeFactory, + IServiceScopeFactory serviceScopeFactory, IOptions distributedEventBusOptions, - IRabbitMqMessageConsumerFactory messageConsumerFactory) - : base(serviceScopeFactory) + IRabbitMqMessageConsumerFactory messageConsumerFactory, + ICurrentTenant currentTenant) + : base(serviceScopeFactory, currentTenant) { ConnectionPool = connectionPool; Serializer = serializer; MessageConsumerFactory = messageConsumerFactory; AbpDistributedEventBusOptions = distributedEventBusOptions.Value; AbpRabbitMqEventBusOptions = options.Value; - + HandlerFactories = new ConcurrentDictionary>(); EventTypes = new ConcurrentDictionary(); } @@ -178,7 +180,7 @@ namespace Volo.Abp.EventBus.RabbitMq "direct", durable: true ); - + var properties = channel.CreateBasicProperties(); properties.DeliveryMode = RabbitMqConsts.DeliveryModes.Persistent; diff --git a/framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj b/framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj index 566767e111..a1799c6673 100644 --- a/framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj +++ b/framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj @@ -16,6 +16,7 @@ + diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs index c5a5022f77..81c3393a50 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs @@ -4,10 +4,12 @@ using System.Collections.Generic; using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Local; using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; using Volo.Abp.Reflection; namespace Volo.Abp.EventBus { + [DependsOn(typeof(AbpMultiTenancyModule))] public class AbpEventBusModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs index a7883950d1..6c3f93814a 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Collections; using Volo.Abp.EventBus.Distributed; +using Volo.Abp.MultiTenancy; using Volo.Abp.Reflection; namespace Volo.Abp.EventBus @@ -16,9 +17,12 @@ namespace Volo.Abp.EventBus { protected IServiceScopeFactory ServiceScopeFactory { get; } - protected EventBusBase(IServiceScopeFactory serviceScopeFactory) + protected ICurrentTenant CurrentTenant { get; } + + protected EventBusBase(IServiceScopeFactory serviceScopeFactory, ICurrentTenant currentTenant) { ServiceScopeFactory = serviceScopeFactory; + CurrentTenant = currentTenant; } /// @@ -162,31 +166,34 @@ namespace Volo.Abp.EventBus { var handlerType = eventHandlerWrapper.EventHandler.GetType(); - if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(ILocalEventHandler<>))) - { - var method = typeof(ILocalEventHandler<>) - .MakeGenericType(eventType) - .GetMethod( - nameof(ILocalEventHandler.HandleEventAsync), - new[] { eventType } - ); - - await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData })); - } - else if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(IDistributedEventHandler<>))) - { - var method = typeof(IDistributedEventHandler<>) - .MakeGenericType(eventType) - .GetMethod( - nameof(IDistributedEventHandler.HandleEventAsync), - new[] { eventType } - ); - - await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData })); - } - else + using (CurrentTenant.Change(GetEventDataTenantId(eventData))) { - throw new AbpException("The object instance is not an event handler. Object type: " + handlerType.AssemblyQualifiedName); + if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(ILocalEventHandler<>))) + { + var method = typeof(ILocalEventHandler<>) + .MakeGenericType(eventType) + .GetMethod( + nameof(ILocalEventHandler.HandleEventAsync), + new[] { eventType } + ); + + await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData })); + } + else if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(IDistributedEventHandler<>))) + { + var method = typeof(IDistributedEventHandler<>) + .MakeGenericType(eventType) + .GetMethod( + nameof(IDistributedEventHandler.HandleEventAsync), + new[] { eventType } + ); + + await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData })); + } + else + { + throw new AbpException("The object instance is not an event handler. Object type: " + handlerType.AssemblyQualifiedName); + } } } catch (TargetInvocationException ex) @@ -200,6 +207,27 @@ namespace Volo.Abp.EventBus } } + protected virtual Guid? GetEventDataTenantId(object eventData) + { + if (eventData is IMultiTenant multiTenantEventData) + { + return multiTenantEventData.TenantId; + } + + //TODO: Cache propertyInfo & Use interface or class to get Entity property. + var propertyInfo = eventData.GetType().GetProperty("Entity"); + if (propertyInfo != null && propertyInfo.GetGetMethod(true) != null) + { + var entity = propertyInfo.GetValue(eventData); + if (entity != null && entity is IMultiTenant multiTenantEntity) + { + return multiTenantEntity.TenantId; + } + } + + return CurrentTenant.Id; + } + protected class EventTypeWithEventHandlerFactories { public Type EventType { get; } @@ -246,4 +274,4 @@ namespace Volo.Abp.EventBus } } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs index 7db5c18f01..8c7bef6f2d 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; using Volo.Abp.Threading; namespace Volo.Abp.EventBus.Local @@ -29,8 +30,9 @@ namespace Volo.Abp.EventBus.Local public LocalEventBus( IOptions options, - IServiceScopeFactory serviceScopeFactory) - : base(serviceScopeFactory) + IServiceScopeFactory serviceScopeFactory, + ICurrentTenant currentTenant) + : base(serviceScopeFactory, currentTenant) { Options = options.Value; Logger = NullLogger.Instance; @@ -166,4 +168,4 @@ namespace Volo.Abp.EventBus.Local return false; } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus_Test.cs b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus_Test.cs index 47649ad3c3..d3371f383a 100644 --- a/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus_Test.cs +++ b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus_Test.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.MultiTenancy; using Xunit; namespace Volo.Abp.EventBus.Distributed @@ -17,5 +20,29 @@ namespace Volo.Abp.EventBus.Distributed Assert.Equal(3, MySimpleDistributedTransientEventHandler.HandleCount); Assert.Equal(3, MySimpleDistributedTransientEventHandler.DisposeCount); } + + [Fact] + public async Task Should_Change_TenantId_If_EventData_Is_MultiTenant() + { + var tenantId = Guid.NewGuid(); + + DistributedEventBus.Subscribe(GetRequiredService()); + + await DistributedEventBus.PublishAsync(new MySimpleEventData(3, tenantId)); + + Assert.Equal(tenantId, MySimpleDistributedSingleInstanceEventHandler.TenantId); + } + + [Fact] + public async Task Should_Change_TenantId_If_Generic_EventData_Is_MultiTenant() + { + var tenantId = Guid.NewGuid(); + + DistributedEventBus.Subscribe>(GetRequiredService()); + + await DistributedEventBus.PublishAsync(new MySimpleEventData(3, tenantId)); + + Assert.Equal(tenantId, MySimpleDistributedSingleInstanceEventHandler.TenantId); + } } } diff --git a/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/MySimpleDistributedSingleInstanceEventHandler.cs b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/MySimpleDistributedSingleInstanceEventHandler.cs new file mode 100644 index 0000000000..53fe8e347b --- /dev/null +++ b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/MySimpleDistributedSingleInstanceEventHandler.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.EventBus.Distributed +{ + public class MySimpleDistributedSingleInstanceEventHandler : IDistributedEventHandler, IDistributedEventHandler>, ITransientDependency + { + private readonly ICurrentTenant _currentTenant; + + public MySimpleDistributedSingleInstanceEventHandler(ICurrentTenant currentTenant) + { + _currentTenant = currentTenant; + } + + public static Guid? TenantId { get; set; } + + public Task HandleEventAsync(MySimpleEventData eventData) + { + TenantId = _currentTenant.Id; + return Task.CompletedTask; + } + + public Task HandleEventAsync(EntityCreatedEto eventData) + { + TenantId = _currentTenant.Id; + return Task.CompletedTask; + } + } +} diff --git a/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_MultiTenancy_Test.cs b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_MultiTenancy_Test.cs new file mode 100644 index 0000000000..62dcb33b42 --- /dev/null +++ b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_MultiTenancy_Test.cs @@ -0,0 +1,64 @@ +using System; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.MultiTenancy; +using Xunit; + +namespace Volo.Abp.EventBus.Local +{ + public class EventBus_MultiTenancy_Test : EventBusTestBase + { + [Fact] + public async Task Should_Change_TenantId_If_EventData_Is_MultiTenant() + { + var tenantId = Guid.NewGuid(); + var handler = new MyEventHandler(GetRequiredService()); + + LocalEventBus.Subscribe>(handler); + + await LocalEventBus.PublishAsync(new EntityCreatedEventData(new MyEntity(tenantId))); + + handler.TenantId.ShouldBe(tenantId); + } + + public class MyEntity : Entity, IMultiTenant + { + public override object[] GetKeys() + { + return new object[0]; + } + + public MyEntity() + { + + } + + public MyEntity(Guid? tenantId) + { + TenantId = tenantId; + } + + public Guid? TenantId { get; } + } + + public class MyEventHandler : ILocalEventHandler> + { + private readonly ICurrentTenant _currentTenant; + + public MyEventHandler(ICurrentTenant currentTenant) + { + _currentTenant = currentTenant; + } + + public Guid? TenantId { get; set; } + + public Task HandleEventAsync(EntityChangedEventData eventData) + { + TenantId = _currentTenant.Id; + return Task.CompletedTask; + } + } + } +} diff --git a/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleEventData.cs b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleEventData.cs index 08142b3355..45a3e7f237 100644 --- a/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleEventData.cs +++ b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleEventData.cs @@ -1,12 +1,18 @@ +using System; +using Volo.Abp.MultiTenancy; + namespace Volo.Abp.EventBus { - public class MySimpleEventData + public class MySimpleEventData : IMultiTenant { public int Value { get; set; } - public MySimpleEventData(int value) + public Guid? TenantId { get; } + + public MySimpleEventData(int value, Guid? tenantId = null) { Value = value; + TenantId = tenantId; } } -} \ No newline at end of file +} From a874455eda5bf70326ab02654f4971e50f094ae3 Mon Sep 17 00:00:00 2001 From: wakuflair Date: Thu, 2 Jul 2020 18:19:06 +0800 Subject: [PATCH 03/77] TagHelper: Suppress label generation if "nolabel" specified --- .../TagHelpers/Form/AbpInputTagHelperService.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index 4ddf44d2d6..b5c4b7ec37 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -247,7 +247,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual async Task GetLabelAsHtmlAsync(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag, bool isCheckbox) { - if (IsOutputHidden(inputTag)) + if (IsOutputHidden(inputTag) || SuppressLabel(inputTag)) { return ""; } @@ -433,6 +433,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return idAttr != null ? "for=\"" + idAttr.Value + "\"" : ""; } + protected virtual bool SuppressLabel(TagHelperOutput inputTag) + { + return inputTag.AllAttributes.ContainsName("nolabel"); + } + protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool suppress) { var list = context.GetValue>(FormGroupContents) ?? new List(); From 667e52c142861cb1b5b2b479d80a98470a4282be Mon Sep 17 00:00:00 2001 From: wakuflair Date: Thu, 2 Jul 2020 18:27:47 +0800 Subject: [PATCH 04/77] Fix wrong property usage --- .../TagHelpers/Form/AbpInputTagHelperService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index b5c4b7ec37..f6abd15d6b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -435,7 +435,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual bool SuppressLabel(TagHelperOutput inputTag) { - return inputTag.AllAttributes.ContainsName("nolabel"); + return inputTag.Attributes.ContainsName("nolabel"); } protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool suppress) From f219d9bf9346d8650c2d5b38b3c72a18ba59e148 Mon Sep 17 00:00:00 2001 From: wakuflair Date: Thu, 2 Jul 2020 18:33:33 +0800 Subject: [PATCH 05/77] Rename `SuppressLabel` to `ShouldSuppressLabel` --- .../TagHelpers/Form/AbpInputTagHelperService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index f6abd15d6b..55aeafc4ea 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -247,7 +247,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual async Task GetLabelAsHtmlAsync(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag, bool isCheckbox) { - if (IsOutputHidden(inputTag) || SuppressLabel(inputTag)) + if (IsOutputHidden(inputTag) || ShouldSuppressLabel(inputTag)) { return ""; } @@ -433,7 +433,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return idAttr != null ? "for=\"" + idAttr.Value + "\"" : ""; } - protected virtual bool SuppressLabel(TagHelperOutput inputTag) + protected virtual bool ShouldSuppressLabel(TagHelperOutput inputTag) { return inputTag.Attributes.ContainsName("nolabel"); } From 300bbb39badbd18a6818b1af172a3cbea0c6f4be Mon Sep 17 00:00:00 2001 From: wakuflair Date: Mon, 6 Jul 2020 10:34:37 +0800 Subject: [PATCH 06/77] Introduced `SuppressLabel` property instead of `nolabel` attribute --- .../TagHelpers/Form/AbpInputTagHelper.cs | 2 ++ .../TagHelpers/Form/AbpInputTagHelperService.cs | 7 +------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs index efd3af2d2c..711e8dfb6c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs @@ -36,6 +36,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form public string Value { get; set; } + public bool SuppressLabel { get; set; } + public AbpInputTagHelper(AbpInputTagHelperService tagHelperService) : base(tagHelperService) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index 55aeafc4ea..58a88c676f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -247,7 +247,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual async Task GetLabelAsHtmlAsync(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag, bool isCheckbox) { - if (IsOutputHidden(inputTag) || ShouldSuppressLabel(inputTag)) + if (IsOutputHidden(inputTag) || TagHelper.SuppressLabel) { return ""; } @@ -433,11 +433,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return idAttr != null ? "for=\"" + idAttr.Value + "\"" : ""; } - protected virtual bool ShouldSuppressLabel(TagHelperOutput inputTag) - { - return inputTag.Attributes.ContainsName("nolabel"); - } - protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool suppress) { var list = context.GetValue>(FormGroupContents) ?? new List(); From e7e1f3fe73c51f382a67fca72f27e2f2a2ba76de Mon Sep 17 00:00:00 2001 From: wakuflair Date: Mon, 6 Jul 2020 10:35:02 +0800 Subject: [PATCH 07/77] Added "Suppress Label Generation" demo --- .../Pages/Components/FormElements.cshtml | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/FormElements.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/FormElements.cshtml index 889e798c8a..6fa9cabb37 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/FormElements.cshtml +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/FormElements.cshtml @@ -108,7 +108,7 @@ public class FormElementsModel : PageModel { public SampleModel MyModel { get; set; } - + public List<SelectListItem> CityList { get; set; } = new List<SelectListItem> { new SelectListItem { Value = "NY", Text = "New York"}, @@ -260,7 +260,7 @@ public class SampleModel { public string SampleInput0 { get; set; } - + public string SampleInput1 { get; set; } public string SampleInput2 { get; set; } @@ -354,7 +354,7 @@ public class FormElementsModel : PageModel { public SampleModel MyModel { get; set; } - + public List"SelectListItem" CityList { get; set; } = new List"SelectListItem" { new SelectListItem { Value = "NY", Text = "New York"}, @@ -458,6 +458,51 @@ <option value="3">Coupe</option> </select> </div> + + + + + + +

Suppress Label Generation

+ +
+
+ +
+
+ + +

+ public class FormElementsModel : PageModel
+ {
+     public SampleModel MyModel { get; set; }
+
+     public void OnGet()
+     {
+         MyModel = new SampleModel();
+     }
+
+     public class SampleModel
+     {
+         [Required]
+         public string Name { get; set; }
+     }
+ }
+
+
+
+ +

+<abp-input asp-for="@@Model.MyModel.Name" suppress-label="true"/>
+                
+
+ +

+<div class="form-group">
+    <input type="text" id="MyModel_Name" name="MyModel.Name" value="" class="form-control ">
+    <span class="text-danger field-validation-valid" data-valmsg-for="MyModel.Name" data-valmsg-replace="true"></span>
+</div>
 
From c6cdc8d628a2ea87a4473e86cd5bdd85888d5f8d Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Fri, 10 Jul 2020 16:02:54 +0800 Subject: [PATCH 08/77] Add TestMemoryDistributedCache for better test. --- .../Volo/Abp/Caching/AbpCachingTestModule.cs | 6 +- .../Abp/Caching/TestMemoryDistributedCache.cs | 61 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/TestMemoryDistributedCache.cs diff --git a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/AbpCachingTestModule.cs b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/AbpCachingTestModule.cs index be003431f2..9da6725fa5 100644 --- a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/AbpCachingTestModule.cs +++ b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/AbpCachingTestModule.cs @@ -1,5 +1,7 @@ using Microsoft.Extensions.Caching.Distributed; using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Modularity; namespace Volo.Abp.Caching @@ -26,6 +28,8 @@ namespace Volo.Abp.Caching option.GlobalCacheEntryOptions.SetSlidingExpiration(TimeSpan.FromMinutes(20)); }); + + context.Services.Replace(ServiceDescriptor.Singleton()); } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/TestMemoryDistributedCache.cs b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/TestMemoryDistributedCache.cs new file mode 100644 index 0000000000..69589d97cd --- /dev/null +++ b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/TestMemoryDistributedCache.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Caching +{ + [DisableConventionalRegistration] + public class TestMemoryDistributedCache : MemoryDistributedCache, ICacheSupportsMultipleItems + { + public TestMemoryDistributedCache(IOptions optionsAccessor) + : base(optionsAccessor) + { + } + + public TestMemoryDistributedCache(IOptions optionsAccessor, ILoggerFactory loggerFactory) + : base(optionsAccessor, loggerFactory) + { + } + + public byte[][] GetMany(IEnumerable keys) + { + var values = new List(); + foreach (var key in keys) + { + values.Add(Get(key)); + } + return values.ToArray(); + } + + public async Task GetManyAsync(IEnumerable keys, CancellationToken token = default) + { + var values = new List(); + foreach (var key in keys) + { + values.Add(await GetAsync(key, token)); + } + return values.ToArray(); + } + + public void SetMany(IEnumerable> items, DistributedCacheEntryOptions options) + { + foreach (var item in items) + { + Set(item.Key, item.Value, options); + } + } + + public async Task SetManyAsync(IEnumerable> items, DistributedCacheEntryOptions options, CancellationToken token = default) + { + foreach (var item in items) + { + await SetAsync(item.Key, item.Value, options, token); + } + } + } +} From 3f975b8b3e3b1770e02b9adf37a3d3b190cfc520 Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Mon, 20 Jul 2020 09:32:59 +0800 Subject: [PATCH 09/77] Get event data tenant id from IEventDataMayHaveTenantId. --- .../Volo/Abp/EventBus/EventBusBase.cs | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs index 6c3f93814a..80d4134db2 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs @@ -209,23 +209,12 @@ namespace Volo.Abp.EventBus protected virtual Guid? GetEventDataTenantId(object eventData) { - if (eventData is IMultiTenant multiTenantEventData) + return eventData switch { - return multiTenantEventData.TenantId; - } - - //TODO: Cache propertyInfo & Use interface or class to get Entity property. - var propertyInfo = eventData.GetType().GetProperty("Entity"); - if (propertyInfo != null && propertyInfo.GetGetMethod(true) != null) - { - var entity = propertyInfo.GetValue(eventData); - if (entity != null && entity is IMultiTenant multiTenantEntity) - { - return multiTenantEntity.TenantId; - } - } - - return CurrentTenant.Id; + IMultiTenant multiTenantEventData => multiTenantEventData.TenantId, + IEventDataMayHaveTenantId eventDataMayHaveTenantId when eventDataMayHaveTenantId.IsMultiTenant(out var tenantId) => tenantId, + _ => CurrentTenant.Id + }; } protected class EventTypeWithEventHandlerFactories From a391e7a5280507314e7d22ea7fe1e9b7a1569060 Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Mon, 20 Jul 2020 11:07:19 +0800 Subject: [PATCH 10/77] Refactor. --- .../Volo/Abp/Caching/DistributedCache.cs | 160 +++++++++--------- .../Volo/Abp/Caching/IDistributedCache.cs | 58 +++---- .../Abp/Caching/DistributedCache_Tests.cs | 12 +- 3 files changed, 115 insertions(+), 115 deletions(-) diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs index 28082832b0..b26feec136 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs @@ -53,7 +53,7 @@ namespace Volo.Abp.Caching public class DistributedCache : IDistributedCache where TCacheItem : class { - public const string DistributedCacheName = "AbpDistributedCache"; + public const string UowCacheName = "AbpDistributedCache"; public ILogger> Logger { get; set; } @@ -142,13 +142,13 @@ namespace Volo.Abp.Caching /// Gets a cache item with the given key. If no cache item is found for the given key then returns null. /// /// The key of cached item to be retrieved from the cache. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The cache item, or null. public virtual TCacheItem Get( TCacheKey key, - bool considerUow = false, - bool? hideErrors = null) + bool? hideErrors = null, + bool considerUow = false) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -183,8 +183,8 @@ namespace Volo.Abp.Caching public virtual KeyValuePair[] GetMany( IEnumerable keys, - bool considerUow = false, - bool? hideErrors = null) + bool? hideErrors = null, + bool considerUow = false) { var keyArray = keys.ToArray(); @@ -193,19 +193,19 @@ namespace Volo.Abp.Caching { return GetManyFallback( keyArray, - considerUow, - hideErrors + hideErrors, + considerUow ); } - var cachedValues = new List>(); var notCachedKeys = new List(); + var cachedValues = new List>(); if (ShouldConsiderUow(considerUow)) { - var cache = GetUnitOfWorkCache(); + var uowCache = GetUnitOfWorkCache(); foreach (var key in keyArray) { - var value = cache.GetOrDefault(key)?.GetUnRemovedValueOrNull(); + var value = uowCache.GetOrDefault(key)?.GetUnRemovedValueOrNull(); if (value != null) { cachedValues.Add(new KeyValuePair(key, value)); @@ -232,7 +232,7 @@ namespace Volo.Abp.Caching if (hideErrors == true) { HandleException(ex); - return ToCacheItemsWithDefaultValues(readKeys); + return ToCacheItemsWithDefaultValues(keyArray); } throw; @@ -243,8 +243,8 @@ namespace Volo.Abp.Caching protected virtual KeyValuePair[] GetManyFallback( TCacheKey[] keys, - bool considerUow = false, - bool? hideErrors = null) + bool? hideErrors = null, + bool considerUow = false) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -253,7 +253,7 @@ namespace Volo.Abp.Caching return keys .Select(key => new KeyValuePair( key, - Get(key, considerUow, hideErrors: false) + Get(key, false, considerUow) ) ).ToArray(); } @@ -271,8 +271,8 @@ namespace Volo.Abp.Caching public virtual async Task[]> GetManyAsync( IEnumerable keys, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default) { var keyArray = keys.ToArray(); @@ -282,20 +282,20 @@ namespace Volo.Abp.Caching { return await GetManyFallbackAsync( keyArray, - considerUow, hideErrors, + considerUow, token ); } - var cachedValues = new List>(); var notCachedKeys = new List(); + var cachedValues = new List>(); if (ShouldConsiderUow(considerUow)) { - var cache = GetUnitOfWorkCache(); + var uowCache = GetUnitOfWorkCache(); foreach (var key in keyArray) { - var value = cache.GetOrDefault(key)?.GetUnRemovedValueOrNull(); + var value = uowCache.GetOrDefault(key)?.GetUnRemovedValueOrNull(); if (value != null) { cachedValues.Add(new KeyValuePair(key, value)); @@ -326,7 +326,7 @@ namespace Volo.Abp.Caching if (hideErrors == true) { await HandleExceptionAsync(ex); - return ToCacheItemsWithDefaultValues(readKeys); + return ToCacheItemsWithDefaultValues(keyArray); } throw; @@ -337,8 +337,8 @@ namespace Volo.Abp.Caching protected virtual async Task[]> GetManyFallbackAsync( TCacheKey[] keys, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -351,7 +351,7 @@ namespace Volo.Abp.Caching { result.Add(new KeyValuePair( key, - await GetAsync(key, considerUow, hideErrors: false, token: token)) + await GetAsync(key, false, considerUow, token: token)) ); } @@ -373,14 +373,14 @@ namespace Volo.Abp.Caching /// Gets a cache item with the given key. If no cache item is found for the given key then returns null. /// /// The key of cached item to be retrieved from the cache. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The for the task. /// The cache item, or null. public virtual async Task GetAsync( TCacheKey key, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -429,17 +429,17 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The factory delegate is used to provide the cache item when no cache item is found for the given . /// The cache options for the factory delegate. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The cache item. public virtual TCacheItem GetOrAdd( TCacheKey key, Func factory, Func optionsFactory = null, - bool considerUow = false, - bool? hideErrors = null) + bool? hideErrors = null, + bool considerUow = false) { - var value = Get(key, considerUow, hideErrors); + var value = Get(key, hideErrors, considerUow); if (value != null) { return value; @@ -447,7 +447,7 @@ namespace Volo.Abp.Caching using (SyncSemaphore.Lock()) { - value = Get(key, considerUow, hideErrors); + value = Get(key, hideErrors, considerUow); if (value != null) { return value; @@ -468,7 +468,7 @@ namespace Volo.Abp.Caching } } - Set(key, value, optionsFactory?.Invoke(), considerUow, hideErrors); + Set(key, value, optionsFactory?.Invoke(), hideErrors, considerUow); } return value; @@ -489,12 +489,12 @@ namespace Volo.Abp.Caching TCacheKey key, Func> factory, Func optionsFactory = null, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default) { token = CancellationTokenProvider.FallbackToProvider(token); - var value = await GetAsync(key, considerUow, hideErrors, token); + var value = await GetAsync(key, hideErrors, considerUow, token); if (value != null) { return value; @@ -502,7 +502,7 @@ namespace Volo.Abp.Caching using (await SyncSemaphore.LockAsync(token)) { - value = await GetAsync(key, considerUow, hideErrors, token); + value = await GetAsync(key, hideErrors, considerUow, token); if (value != null) { return value; @@ -523,7 +523,7 @@ namespace Volo.Abp.Caching } } - await SetAsync(key, value, optionsFactory?.Invoke(), considerUow, hideErrors, token); + await SetAsync(key, value, optionsFactory?.Invoke(), hideErrors, considerUow, token); } return value; @@ -535,14 +535,14 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The cache item value to set in the cache. /// The cache options for the value. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. public virtual void Set( TCacheKey key, TCacheItem value, DistributedCacheEntryOptions options = null, - bool considerUow = false, - bool? hideErrors = null) + bool? hideErrors = null, + bool considerUow = false) { void SetRealCache() { @@ -570,14 +570,14 @@ namespace Volo.Abp.Caching if (ShouldConsiderUow(considerUow)) { - var cache = GetUnitOfWorkCache(); - if (cache.TryGetValue(key, out _)) + var uowCache = GetUnitOfWorkCache(); + if (uowCache.TryGetValue(key, out _)) { - cache[key].SetValue(value); + uowCache[key].SetValue(value); } else { - cache.Add(key, new UnitOfWorkCacheItem(value)); + uowCache.Add(key, new UnitOfWorkCacheItem(value)); } // ReSharper disable once PossibleNullReferenceException @@ -598,16 +598,16 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The cache item value to set in the cache. /// The cache options for the value. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The for the task. /// The indicating that the operation is asynchronous. public virtual async Task SetAsync( TCacheKey key, TCacheItem value, DistributedCacheEntryOptions options = null, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default) { async Task SetRealCache() @@ -637,14 +637,14 @@ namespace Volo.Abp.Caching if (ShouldConsiderUow(considerUow)) { - var cache = GetUnitOfWorkCache(); - if (cache.TryGetValue(key, out _)) + var uowCache = GetUnitOfWorkCache(); + if (uowCache.TryGetValue(key, out _)) { - cache[key].SetValue(value); + uowCache[key].SetValue(value); } else { - cache.Add(key, new UnitOfWorkCacheItem(value)); + uowCache.Add(key, new UnitOfWorkCacheItem(value)); } // ReSharper disable once PossibleNullReferenceException @@ -659,8 +659,8 @@ namespace Volo.Abp.Caching public void SetMany( IEnumerable> items, DistributedCacheEntryOptions options = null, - bool considerUow = false, - bool? hideErrors = null) + bool? hideErrors = null, + bool considerUow = false) { var itemsArray = items.ToArray(); @@ -670,8 +670,8 @@ namespace Volo.Abp.Caching SetManyFallback( itemsArray, options, - considerUow, - hideErrors + hideErrors, + considerUow ); return; @@ -702,17 +702,17 @@ namespace Volo.Abp.Caching if (ShouldConsiderUow(considerUow)) { - var cache = GetUnitOfWorkCache(); + var uowCache = GetUnitOfWorkCache(); foreach (var pair in itemsArray) { - if (cache.TryGetValue(pair.Key, out _)) + if (uowCache.TryGetValue(pair.Key, out _)) { - cache[pair.Key].SetValue(pair.Value); + uowCache[pair.Key].SetValue(pair.Value); } else { - cache.Add(pair.Key, new UnitOfWorkCacheItem(pair.Value)); + uowCache.Add(pair.Key, new UnitOfWorkCacheItem(pair.Value)); } } @@ -732,8 +732,8 @@ namespace Volo.Abp.Caching protected virtual void SetManyFallback( KeyValuePair[] items, DistributedCacheEntryOptions options = null, - bool considerUow = false, - bool? hideErrors = null) + bool? hideErrors = null, + bool considerUow = false) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -745,8 +745,8 @@ namespace Volo.Abp.Caching item.Key, item.Value, options, - considerUow, - hideErrors: false + false, + considerUow ); } } @@ -765,8 +765,8 @@ namespace Volo.Abp.Caching public virtual async Task SetManyAsync( IEnumerable> items, DistributedCacheEntryOptions options = null, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default) { var itemsArray = items.ToArray(); @@ -777,8 +777,8 @@ namespace Volo.Abp.Caching await SetManyFallbackAsync( itemsArray, options, - considerUow, hideErrors, + considerUow, token ); @@ -811,17 +811,17 @@ namespace Volo.Abp.Caching if (ShouldConsiderUow(considerUow)) { - var cache = GetUnitOfWorkCache(); + var uowCache = GetUnitOfWorkCache(); foreach (var pair in itemsArray) { - if (cache.TryGetValue(pair.Key, out _)) + if (uowCache.TryGetValue(pair.Key, out _)) { - cache[pair.Key].SetValue(pair.Value); + uowCache[pair.Key].SetValue(pair.Value); } else { - cache.Add(pair.Key, new UnitOfWorkCacheItem(pair.Value)); + uowCache.Add(pair.Key, new UnitOfWorkCacheItem(pair.Value)); } } @@ -837,8 +837,8 @@ namespace Volo.Abp.Caching protected virtual async Task SetManyFallbackAsync( KeyValuePair[] items, DistributedCacheEntryOptions options = null, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -851,8 +851,8 @@ namespace Volo.Abp.Caching item.Key, item.Value, options, + false, considerUow, - hideErrors: false, token: token ); } @@ -934,8 +934,8 @@ namespace Volo.Abp.Caching /// Indicates to throw or hide the exceptions for the distributed cache. public virtual void Remove( TCacheKey key, - bool considerUow = false, - bool? hideErrors = null) + bool? hideErrors = null, + bool considerUow = false) { void RemoveRealCache() { @@ -959,10 +959,10 @@ namespace Volo.Abp.Caching if (ShouldConsiderUow(considerUow)) { - var cache = GetUnitOfWorkCache(); - if (cache.TryGetValue(key, out _)) + var uowCache = GetUnitOfWorkCache(); + if (uowCache.TryGetValue(key, out _)) { - cache[key].RemoveValue(); + uowCache[key].RemoveValue(); } // ReSharper disable once PossibleNullReferenceException @@ -982,14 +982,14 @@ namespace Volo.Abp.Caching /// Removes the cache item for given key from cache. /// /// The key of cached item to be retrieved from the cache. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The for the task. /// The indicating that the operation is asynchronous. public virtual async Task RemoveAsync( TCacheKey key, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default) { async Task RemoveRealCache() @@ -1014,10 +1014,10 @@ namespace Volo.Abp.Caching if (ShouldConsiderUow(considerUow)) { - var cache = GetUnitOfWorkCache(); - if (cache.TryGetValue(key, out _)) + var uowCache = GetUnitOfWorkCache(); + if (uowCache.TryGetValue(key, out _)) { - cache[key].RemoveValue(); + uowCache[key].RemoveValue(); } // ReSharper disable once PossibleNullReferenceException @@ -1104,14 +1104,14 @@ namespace Volo.Abp.Caching protected virtual string GetUnitOfWorkCacheKey() { - return DistributedCacheName + CacheName; + return UowCacheName + CacheName; } protected virtual Dictionary> GetUnitOfWorkCache() { if (UnitOfWorkManager.Current == null) { - throw new AbpException($"There is no unit of work in the current context, The {GetType().Name} can only be used in a unit of work."); + throw new AbpException($"There is no active UOW."); } return UnitOfWorkManager.Current.GetOrAddItem(GetUnitOfWorkCacheKey(), diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs index 1b0e58815e..2f74fd678b 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs @@ -29,13 +29,13 @@ namespace Volo.Abp.Caching /// Gets a cache item with the given key. If no cache item is found for the given key then returns null. /// /// The key of cached item to be retrieved from the cache. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The cache item, or null. TCacheItem Get( TCacheKey key, - bool considerUow = false, - bool? hideErrors = null + bool? hideErrors = null, + bool considerUow = false ); /// @@ -46,13 +46,13 @@ namespace Volo.Abp.Caching /// if the related key not found in the cache. /// /// The keys of cached items to be retrieved from the cache. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// List of cache items. KeyValuePair[] GetMany( IEnumerable keys, - bool considerUow = false, - bool? hideErrors = null + bool? hideErrors = null, + bool considerUow = false ); /// @@ -64,14 +64,14 @@ namespace Volo.Abp.Caching /// /// /// The keys of cached items to be retrieved from the cache. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// /// The for the task. /// List of cache items. Task[]> GetManyAsync( IEnumerable keys, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default ); @@ -85,8 +85,8 @@ namespace Volo.Abp.Caching /// The cache item, or null. Task GetAsync( [NotNull] TCacheKey key, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default ); @@ -97,15 +97,15 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The factory delegate is used to provide the cache item when no cache item is found for the given . /// The cache options for the factory delegate. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The cache item. TCacheItem GetOrAdd( TCacheKey key, Func factory, Func optionsFactory = null, - bool considerUow = false, - bool? hideErrors = null + bool? hideErrors = null, + bool considerUow = false ); /// @@ -115,16 +115,16 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The factory delegate is used to provide the cache item when no cache item is found for the given . /// The cache options for the factory delegate. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The for the task. /// The cache item. Task GetOrAddAsync( [NotNull] TCacheKey key, Func> factory, Func optionsFactory = null, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default ); @@ -134,14 +134,14 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The cache item value to set in the cache. /// The cache options for the value. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. void Set( TCacheKey key, TCacheItem value, DistributedCacheEntryOptions options = null, - bool considerUow = false, - bool? hideErrors = null + bool? hideErrors = null, + bool considerUow = false ); /// @@ -150,16 +150,16 @@ namespace Volo.Abp.Caching /// The key of cached item to be retrieved from the cache. /// The cache item value to set in the cache. /// The cache options for the value. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The for the task. /// The indicating that the operation is asynchronous. Task SetAsync( [NotNull] TCacheKey key, [NotNull] TCacheItem value, [CanBeNull] DistributedCacheEntryOptions options = null, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default ); @@ -169,13 +169,13 @@ namespace Volo.Abp.Caching /// /// Items to set on the cache /// The cache options for the value. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. void SetMany( IEnumerable> items, DistributedCacheEntryOptions options = null, - bool considerUow = false, - bool? hideErrors = null + bool? hideErrors = null, + bool considerUow = false ); /// @@ -184,15 +184,15 @@ namespace Volo.Abp.Caching /// /// Items to set on the cache /// The cache options for the value. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The for the task. /// The indicating that the operation is asynchronous. Task SetManyAsync( IEnumerable> items, DistributedCacheEntryOptions options = null, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default ); @@ -223,26 +223,26 @@ namespace Volo.Abp.Caching /// Removes the cache item for given key from cache. /// /// The key of cached item to be retrieved from the cache. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. void Remove( TCacheKey key, - bool considerUow = false, - bool? hideErrors = null + bool? hideErrors = null, + bool considerUow = false ); /// /// Removes the cache item for given key from cache. /// /// The key of cached item to be retrieved from the cache. - /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// Indicates to throw or hide the exceptions for the distributed cache. + /// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache. /// The for the task. /// The indicating that the operation is asynchronous. Task RemoveAsync( TCacheKey key, - bool considerUow = false, bool? hideErrors = null, + bool considerUow = false, CancellationToken token = default ); } diff --git a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs index 61a61ac2cd..35a72362ac 100644 --- a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs +++ b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs @@ -181,10 +181,10 @@ namespace Volo.Abp.Caching factoryExecuted = false; cacheItem = await personCache.GetOrAddAsync(cacheKey, - async () => + () => { factoryExecuted = true; - return new PersonCacheItem(personName); + return Task.FromResult(new PersonCacheItem(personName)); }); factoryExecuted.ShouldBeFalse(); @@ -539,7 +539,7 @@ namespace Volo.Abp.Caching cacheValue = await personCache.GetAsync(key, considerUow: false); cacheValue.ShouldBeNull(); } - + [Fact] public async Task Should_Set_And_Get_Multiple_Items_Async() { @@ -547,7 +547,7 @@ namespace Volo.Abp.Caching await personCache.SetManyAsync(new[] { - new KeyValuePair("john", new PersonCacheItem("John Nash")), + new KeyValuePair("john", new PersonCacheItem("John Nash")), new KeyValuePair("thomas", new PersonCacheItem("Thomas Moore")) }); @@ -557,7 +557,7 @@ namespace Volo.Abp.Caching "thomas", "baris" //doesn't exist }); - + cacheItems.Length.ShouldBe(3); cacheItems[0].Key.ShouldBe("john"); cacheItems[0].Value.Name.ShouldBe("John Nash"); @@ -565,7 +565,7 @@ namespace Volo.Abp.Caching cacheItems[1].Value.Name.ShouldBe("Thomas Moore"); cacheItems[2].Key.ShouldBe("baris"); cacheItems[2].Value.ShouldBeNull(); - + (await personCache.GetAsync("john")).Name.ShouldBe("John Nash"); (await personCache.GetAsync("baris")).ShouldBeNull(); } From 57fdecc462a44fea4e64717956ebb8b93b87bb3a Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Mon, 20 Jul 2020 13:00:06 +0800 Subject: [PATCH 11/77] Add GetMany & SetMany unit tests. --- .../Abp/Caching/DistributedCache_Tests.cs | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs index 35a72362ac..32876c08fe 100644 --- a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs +++ b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Testing; @@ -540,6 +541,148 @@ namespace Volo.Abp.Caching cacheValue.ShouldBeNull(); } + [Fact] + public async Task Cache_Should_Only_Available_In_Uow_For_GetManyAsync() + { + var testkey = "testkey"; + var testkey2 = "testkey2"; + var testKeys = new[] {testkey, testkey2}; + + using (var uow = GetRequiredService().Begin()) + { + var personCache = GetRequiredService>(); + + var cacheValue = await personCache.GetManyAsync(testKeys, considerUow: true); + cacheValue.Where(x => x.Value != null).ShouldBeEmpty(); + + await personCache.SetManyAsync(new List> + { + new KeyValuePair(testkey, new PersonCacheItem("john")), + new KeyValuePair(testkey2, new PersonCacheItem("jack")) + }, considerUow: true); + + cacheValue = await personCache.GetManyAsync(testKeys, considerUow: true); + cacheValue.Where(x => x.Value != null).ShouldNotBeEmpty(); + cacheValue.ShouldContain(x => x.Value.Name == "john" || x.Value.Name == "jack"); + + cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false); + cacheValue.Where(x => x.Value != null).ShouldBeEmpty(); + + uow.OnCompleted(async () => + { + cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false); + cacheValue.ShouldNotBeEmpty(); + cacheValue.ShouldContain(x => x.Value.Name == "john" || x.Value.Name == "jack"); + }); + + await uow.CompleteAsync(); + } + } + + [Fact] + public async Task Cache_Should_Rollback_With_Uow_For_GetManyAsync() + { + var testkey = "testkey"; + var testkey2 = "testkey2"; + var testKeys = new[] {testkey, testkey2}; + + var personCache = GetRequiredService>(); + + var cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false); + cacheValue.Where(x => x.Value != null).ShouldBeEmpty(); + + using (var uow = GetRequiredService().Begin()) + { + cacheValue = await personCache.GetManyAsync(testKeys, considerUow: true); + cacheValue.Where(x => x.Value != null).ShouldBeEmpty(); + + await personCache.SetManyAsync(new List> + { + new KeyValuePair(testkey, new PersonCacheItem("john")), + new KeyValuePair(testkey2, new PersonCacheItem("jack")) + }, considerUow: true); + + cacheValue = await personCache.GetManyAsync(testKeys, considerUow: true); + cacheValue.Where(x => x.Value != null).ShouldNotBeEmpty(); + cacheValue.ShouldContain(x => x.Value.Name == "john" || x.Value.Name == "jack"); + + cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false); + cacheValue.Where(x => x.Value != null).ShouldBeEmpty(); + } + + cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false); + cacheValue.Where(x => x.Value != null).ShouldBeEmpty(); + } + + + [Fact] + public async Task Cache_Should_Only_Available_In_Uow_For_SetManyAsync() + { + var testkey = "testkey"; + var testkey2 = "testkey2"; + var testKeys = new[] {testkey, testkey2}; + + using (var uow = GetRequiredService().Begin()) + { + var personCache = GetRequiredService>(); + + await personCache.SetManyAsync(new List> + { + new KeyValuePair(testkey, new PersonCacheItem("john")), + new KeyValuePair(testkey, new PersonCacheItem("jack")) + }, considerUow: true); + + var cacheValue = await personCache.GetManyAsync(testKeys, considerUow: true); + cacheValue.Where(x => x.Value != null).ShouldNotBeEmpty(); + cacheValue.ShouldContain(x => x.Value.Name == "john" || x.Value.Name == "jack"); + + cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false); + cacheValue.Where(x => x.Value != null).ShouldBeEmpty(); + + uow.OnCompleted(async () => + { + var cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false); + cacheValue.Where(x => x.Value != null).ShouldNotBeEmpty(); + cacheValue.ShouldContain(x => x.Value.Name == "john" || x.Value.Name == "jack"); + }); + + await uow.CompleteAsync(); + } + } + + [Fact] + public async Task Cache_Should_Rollback_With_Uow_For_SetManyAsync() + { + var testkey = "testkey"; + var testkey2 = "testkey2"; + var testKeys = new[] {testkey, testkey2}; + + var personCache = GetRequiredService>(); + + var cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false); + cacheValue.Where(x => x.Value != null).ShouldBeEmpty(); + + using (var uow = GetRequiredService().Begin()) + { + await personCache.SetManyAsync(new List> + { + new KeyValuePair(testkey, new PersonCacheItem("john")), + new KeyValuePair(testkey, new PersonCacheItem("jack")) + }, considerUow: true); + + cacheValue = await personCache.GetManyAsync(testKeys, considerUow: true); + cacheValue.Where(x => x.Value != null).ShouldNotBeEmpty(); + cacheValue.ShouldContain(x => x.Value.Name == "john" || x.Value.Name == "jack"); + + cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false); + cacheValue.Where(x => x.Value != null).ShouldBeEmpty(); + } + + cacheValue = await personCache.GetManyAsync(testKeys, considerUow: false); + cacheValue.Where(x => x.Value != null).ShouldBeEmpty(); + } + + [Fact] public async Task Should_Set_And_Get_Multiple_Items_Async() { From 404deb0389da9343629b78071bd6ca5be0249e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 20 Jul 2020 09:23:48 +0300 Subject: [PATCH 12/77] Create The Author Entity --- docs/en/Tutorials/Part-1.md | 2 +- docs/en/Tutorials/Part-3.md | 2 +- docs/en/Tutorials/Part-4.md | 2 +- docs/en/Tutorials/Part-5.md | 2 +- docs/en/Tutorials/Part-6.md | 81 +++++++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 docs/en/Tutorials/Part-6.md diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index 2f21b99e4b..6401cb7a35 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -27,7 +27,7 @@ end In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies: -* **{{DB_Text}}** as the ORM provider. +* **{{DB_Text}}** as the ORM provider. * **{{UI_Value}}** as the UI Framework. This tutorial is organized as the following parts; diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index 3b31ec1c88..15cbf2229b 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -32,7 +32,7 @@ In this tutorial series, you will build an ABP based web application named `Acme This tutorial is organized as the following parts; -- [Part 1: Creating the project and book list page](Part-1.md) +- [Part 1: Creating the server side](Part-1.md) - [Part 2: The book list page](Part-2.md) - **Part 3: Creating, updating and deleting books (this part)** - [Part 4: Integration tests](Part-4.md) diff --git a/docs/en/Tutorials/Part-4.md b/docs/en/Tutorials/Part-4.md index 5e87d18653..45505cc42f 100644 --- a/docs/en/Tutorials/Part-4.md +++ b/docs/en/Tutorials/Part-4.md @@ -32,7 +32,7 @@ In this tutorial series, you will build an ABP based web application named `Acme This tutorial is organized as the following parts; -- [Part 1: Creating the project and book list page](Part-1.md) +- [Part 1: Creating the server side](Part-1.md) - [Part 2: The book list page](Part-2.md) - [Part 3: Creating, updating and deleting books](Part-3.md) - **Part 4: Integration tests (this part)** diff --git a/docs/en/Tutorials/Part-5.md b/docs/en/Tutorials/Part-5.md index 537f79ca79..c99dc109a2 100644 --- a/docs/en/Tutorials/Part-5.md +++ b/docs/en/Tutorials/Part-5.md @@ -32,7 +32,7 @@ In this tutorial series, you will build an ABP based web application named `Acme This tutorial is organized as the following parts; -- [Part 1: Creating the project and book list page](Part-1.md) +- [Part 1: Creating the server side](Part-1.md) - [Part 2: The book list page](Part-2.md) - [Part 3: Creating, updating and deleting books](Part-3.md) - [Part 4: Integration tests](Part-4.md) diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md new file mode 100644 index 0000000000..9fe9196c58 --- /dev/null +++ b/docs/en/Tutorials/Part-6.md @@ -0,0 +1,81 @@ +# Web Application Development Tutorial - Part 6: Authors +````json +//[doc-params] +{ + "UI": ["MVC","NG"], + "DB": ["EF","Mongo"] +} +```` +{{ +if UI == "MVC" + UI_Text="mvc" +else if UI == "NG" + UI_Text="angular" +else + UI_Text="?" +end +if DB == "EF" + DB_Text="Entity Framework Core" +else if DB == "Mongo" + DB_Text="MongoDB" +else + DB_Text="?" +end +}} + +## About This Tutorial + +In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies: + +* **{{DB_Text}}** as the ORM provider. +* **{{UI_Value}}** as the UI Framework. + +This tutorial is organized as the following parts; + +- [Part 1: Creating the server side](Part-1.md) +- [Part 2: The book list page](Part-2.md) +- [Part 3: Creating, updating and deleting books](Part-3.md) +- [Part 4: Integration tests](Part-4.md) +- [Part 5: Authorization](Part-5.md) +- **Part 6: The author entity (this part)** + +### Download the Source Code + +This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded: + +* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +## Introduction + +In the previous parts, we've used the ABP infrastructure to easily build some services; + +* Used the [CrudAppService](../Application-Services.md) base class instead of manually developing an application service for standard create, read, update and delete operations. +* Used [generic repositories](../Repositories.md) to completely automate the database layer. +* Used [conventional API controllers](../API/Auto-API-Controllers.md) instead of manually writing API controllers. + +For the "Authors" part, we will do most of the things manually to show how you can do it in case of need. + +## The Author Entity + +Create an `Authors` folder (namespace) in the `Acme.BookStore.Domain` project and add an `Author` class inside it: + +````csharp +using System; +using Volo.Abp.Domain.Entities.Auditing; + +namespace Acme.BookStore.Authors +{ + public class Author : FullAuditedAggregateRoot + { + public string Name { get; internal set; } + + public DateTime BirthDate { get; set; } + + public string ShortBio { get; set; } + } +} +```` + +* Inherited from `FullAuditedAggregateRoot` which makes the entity [soft delete](../Data-Filtering.md) (that means when you delete it, it is not deleted in the database, but just marked as deleted) with all the [auditing](../Entities.md) properties. +* `internal set` for the `Name` property restricts to set this property from out of the domain layer. Because we want to change it in a controlled way in the domain layer to prevent to create two authors with the same name, to just demonstrate a simple business rule. \ No newline at end of file From 83aeea2230dcac31a3c8b8c571dc6ac803cdbe8d Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Mon, 20 Jul 2020 15:34:22 +0800 Subject: [PATCH 13/77] Use IdentitySecurityLogManager instead EventBus to save security logs. https://github.com/abpframework/abp/pull/4675#issuecomment-660089083 --- .../IdentityServerSupportedLoginModel.cs | 2 +- .../IdentityServerSupportedLogoutModel.cs | 2 +- .../Account/Controllers/AccountController.cs | 16 +++----- .../Pages/Account/AccountPageModel.cs | 3 +- .../Pages/Account/Login.cshtml.cs | 6 +-- .../Pages/Account/Logout.cshtml.cs | 2 +- ...Event.cs => IdentitySecurityLogContext.cs} | 7 ++-- ...ndler.cs => IdentitySecurityLogManager.cs} | 26 ++++++------- .../IdentityServerSecurityLogActionConsts.cs | 19 ++++++++++ ...IdentityServerSecurityLogIdentityConsts.cs | 7 ++++ .../AbpResourceOwnerPasswordValidator.cs | 25 ++++++++++++- .../AspNetIdentity/SignInResultExtensions.cs | 37 +++++++++++++++++++ 12 files changed, 115 insertions(+), 37 deletions(-) rename modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/{IdentitySecurityLogEvent.cs => IdentitySecurityLogContext.cs} (72%) rename modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/{IdentitySecurityLogHandler.cs => IdentitySecurityLogManager.cs} (74%) create mode 100644 modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityServerSecurityLogActionConsts.cs create mode 100644 modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityServerSecurityLogIdentityConsts.cs create mode 100644 modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/SignInResultExtensions.cs diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs index 5afe312a6e..297e54cd69 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs @@ -132,7 +132,7 @@ namespace Volo.Abp.Account.Web.Pages.Account true ); - await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = result.ToIdentitySecurityLogAction(), diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs index b25b34799d..bec2ede369 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs @@ -20,7 +20,7 @@ namespace Volo.Abp.Account.Web.Pages.Account public override async Task OnGetAsync() { - await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = IdentitySecurityLogActionConsts.Logout diff --git a/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs b/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs index aa22882701..575853f410 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs @@ -6,10 +6,8 @@ using Volo.Abp.Account.Localization; using Volo.Abp.Account.Settings; using Volo.Abp.Account.Web.Areas.Account.Controllers.Models; using Volo.Abp.AspNetCore.Mvc; -using Volo.Abp.EventBus.Local; using Volo.Abp.Identity; using Volo.Abp.Identity.AspNetCore; -using Volo.Abp.SecurityLog; using Volo.Abp.Settings; using Volo.Abp.Validation; using SignInResult = Microsoft.AspNetCore.Identity.SignInResult; @@ -28,22 +26,20 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers protected SignInManager SignInManager { get; } protected IdentityUserManager UserManager { get; } protected ISettingProvider SettingProvider { get; } - - protected ILocalEventBus LocalEventBus { get; } + protected IdentitySecurityLogManager IdentitySecurityLogManager { get; } public AccountController( SignInManager signInManager, IdentityUserManager userManager, ISettingProvider settingProvider, - ISecurityLogManager securityLogManager, - ILocalEventBus localEventBus) + IdentitySecurityLogManager identitySecurityLogManager) { LocalizationResource = typeof(AccountResource); SignInManager = signInManager; UserManager = userManager; SettingProvider = settingProvider; - LocalEventBus = localEventBus; + IdentitySecurityLogManager = identitySecurityLogManager; } [HttpPost] @@ -62,7 +58,7 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers true ); - await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = signInResult.ToIdentitySecurityLogAction(), @@ -76,13 +72,13 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers [Route("logout")] public virtual async Task Logout() { - await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = IdentitySecurityLogActionConsts.Logout }); - await SignInManager.SignOutAsync(); + await SignInManager.SignOutAsync(); } [HttpPost] diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs index e645c9c9cf..7e79442bd7 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs @@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Account.Localization; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; -using Volo.Abp.EventBus.Local; using Volo.Abp.Identity; using IdentityUser = Volo.Abp.Identity.IdentityUser; @@ -15,7 +14,7 @@ namespace Volo.Abp.Account.Web.Pages.Account { public SignInManager SignInManager { get; set; } public IdentityUserManager UserManager { get; set; } - public ILocalEventBus LocalEventBus { get; set; } + public IdentitySecurityLogManager IdentitySecurityLogManager { get; set; } protected AccountPageModel() { diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs index 05946182db..9d6c540ce5 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs @@ -98,7 +98,7 @@ namespace Volo.Abp.Account.Web.Pages.Account true ); - await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = result.ToIdentitySecurityLogAction(), @@ -194,7 +194,7 @@ namespace Volo.Abp.Account.Web.Pages.Account if (!result.Succeeded) { - await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { Identity = IdentitySecurityLogIdentityConsts.IdentityExternal, Action = "Login" + result @@ -224,7 +224,7 @@ namespace Volo.Abp.Account.Web.Pages.Account await SignInManager.SignInAsync(user, false); - await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { Identity = IdentitySecurityLogIdentityConsts.IdentityExternal, Action = result.ToIdentitySecurityLogAction(), diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs index fe7361e4ce..5923827a24 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs @@ -16,7 +16,7 @@ namespace Volo.Abp.Account.Web.Pages.Account public virtual async Task OnGetAsync() { - await LocalEventBus.PublishAsync(new IdentitySecurityLogEvent + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = IdentitySecurityLogActionConsts.Logout diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogEvent.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogContext.cs similarity index 72% rename from modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogEvent.cs rename to modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogContext.cs index faac2d5033..42ec5e6fe5 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogEvent.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogContext.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; -using Volo.Abp.MultiTenancy; namespace Volo.Abp.Identity { - public class IdentitySecurityLogEvent : IMultiTenant + public class IdentitySecurityLogContext { public Guid? TenantId { get; set; } @@ -18,12 +17,12 @@ namespace Volo.Abp.Identity public Dictionary ExtraProperties { get; } - public IdentitySecurityLogEvent() + public IdentitySecurityLogContext() { ExtraProperties = new Dictionary(); } - public virtual IdentitySecurityLogEvent WithProperty(string key, object value) + public virtual IdentitySecurityLogContext WithProperty(string key, object value) { ExtraProperties[key] = value; return this; diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogHandler.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogManager.cs similarity index 74% rename from modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogHandler.cs rename to modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogManager.cs index 5000d4d288..bdad21df13 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogHandler.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogManager.cs @@ -2,15 +2,13 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Volo.Abp.DependencyInjection; -using Volo.Abp.EventBus; using Volo.Abp.Security.Claims; using Volo.Abp.SecurityLog; -using Volo.Abp.Uow; using Volo.Abp.Users; namespace Volo.Abp.Identity { - public class IdentitySecurityLogHandler : ILocalEventHandler, ITransientDependency + public class IdentitySecurityLogManager : ITransientDependency { protected ISecurityLogManager SecurityLogManager { get; } protected IdentityUserManager UserManager { get; } @@ -18,7 +16,7 @@ namespace Volo.Abp.Identity protected IUserClaimsPrincipalFactory UserClaimsPrincipalFactory { get; } protected ICurrentUser CurrentUser { get; } - public IdentitySecurityLogHandler( + public IdentitySecurityLogManager( ISecurityLogManager securityLogManager, IdentityUserManager userManager, ICurrentPrincipalAccessor currentPrincipalAccessor, @@ -32,24 +30,24 @@ namespace Volo.Abp.Identity CurrentUser = currentUser; } - public async Task HandleEventAsync(IdentitySecurityLogEvent eventData) + public async Task SaveAsync(IdentitySecurityLogContext context) { Action securityLogAction = securityLog => { - securityLog.Identity = eventData.Identity; - securityLog.Action = eventData.Action; + securityLog.Identity = context.Identity; + securityLog.Action = context.Action; - if (securityLog.UserName.IsNullOrWhiteSpace()) + if (!context.UserName.IsNullOrWhiteSpace()) { - securityLog.UserName = eventData.UserName; + securityLog.UserName = context.UserName; } - if (securityLog.ClientId.IsNullOrWhiteSpace()) + if (!context.ClientId.IsNullOrWhiteSpace()) { - securityLog.ClientId = eventData.ClientId; + securityLog.ClientId = context.ClientId; } - foreach (var property in eventData.ExtraProperties) + foreach (var property in context.ExtraProperties) { securityLog.ExtraProperties[property.Key] = property.Value; } @@ -61,13 +59,13 @@ namespace Volo.Abp.Identity } else { - if (eventData.UserName.IsNullOrWhiteSpace()) + if (context.UserName.IsNullOrWhiteSpace()) { await SecurityLogManager.SaveAsync(securityLogAction); } else { - var user = await UserManager.FindByNameAsync(eventData.UserName); + var user = await UserManager.FindByNameAsync(context.UserName); if (user != null) { using (CurrentPrincipalAccessor.Change(await UserClaimsPrincipalFactory.CreateAsync(user))) diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityServerSecurityLogActionConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityServerSecurityLogActionConsts.cs new file mode 100644 index 0000000000..47ad6b21d5 --- /dev/null +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityServerSecurityLogActionConsts.cs @@ -0,0 +1,19 @@ +namespace Volo.Abp.IdentityServer +{ + public class IdentityServerSecurityLogActionConsts + { + public static string LoginSucceeded { get; set; } = "LoginSucceeded"; + + public static string LoginLockedout { get; set; } = "LoginLockedout"; + + public static string LoginNotAllowed { get; set; } = "LoginNotAllowed"; + + public static string LoginRequiresTwoFactor { get; set; } = "LoginRequiresTwoFactor"; + + public static string LoginFailed { get; set; } = "LoginFailed"; + + public static string LoginInvalidUserName { get; set; } = "LoginInvalidUserName"; + + public static string LoginInvalidUserNameOrPassword { get; set; } = "LoginInvalidUserNameOrPassword"; + } +} diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityServerSecurityLogIdentityConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityServerSecurityLogIdentityConsts.cs new file mode 100644 index 0000000000..4a5f96e0e2 --- /dev/null +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityServerSecurityLogIdentityConsts.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.IdentityServer +{ + public class IdentityServerSecurityLogIdentityConsts + { + public static string IdentityServer { get; set; } = "IdentityServer"; + } +} diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs index e45df9b3f7..90910e1894 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs @@ -10,6 +10,7 @@ using IdentityServer4.Validation; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; +using Volo.Abp.Identity; using Volo.Abp.IdentityServer.Localization; using Volo.Abp.Security.Claims; using Volo.Abp.Uow; @@ -23,18 +24,21 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity protected SignInManager SignInManager { get; } protected IEventService Events { get; } protected UserManager UserManager { get; } + protected IdentitySecurityLogManager IdentitySecurityLogManager { get; } protected ILogger> Logger { get; } protected IStringLocalizer Localizer { get; } public AbpResourceOwnerPasswordValidator( UserManager userManager, SignInManager signInManager, + IdentitySecurityLogManager identitySecurityLogManager, IEventService events, - ILogger> logger, + ILogger> logger, IStringLocalizer localizer) { UserManager = userManager; SignInManager = signInManager; + IdentitySecurityLogManager = identitySecurityLogManager; Events = events; Logger = logger; Localizer = localizer; @@ -71,6 +75,12 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity additionalClaims.ToArray() ); + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() + { + Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, + Action = result.ToIdentitySecurityLogAction(), + }); + return; } else if (result.IsLockedOut) @@ -91,12 +101,25 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity await Events.RaiseAsync(new UserLoginFailureEvent(context.UserName, "invalid credentials", interactive: false)); errorDescription = Localizer["InvalidUserNameOrPassword"]; } + + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() + { + Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, + Action = result.ToIdentitySecurityLogAction(), + UserName = context.UserName + }); } else { Logger.LogInformation("No user found matching username: {username}", context.UserName); await Events.RaiseAsync(new UserLoginFailureEvent(context.UserName, "invalid username", interactive: false)); errorDescription = Localizer["InvalidUsername"]; + + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() + { + Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, + Action = IdentityServerSecurityLogActionConsts.LoginInvalidUserName + }); } context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, errorDescription); diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/SignInResultExtensions.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/SignInResultExtensions.cs new file mode 100644 index 0000000000..6f167912c3 --- /dev/null +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/SignInResultExtensions.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Identity; + +namespace Volo.Abp.IdentityServer.AspNetIdentity +{ + public static class SignInResultExtensions + { + public static string ToIdentitySecurityLogAction(this SignInResult result) + { + if (result.Succeeded) + { + return IdentityServerSecurityLogActionConsts.LoginSucceeded; + } + + if (result.IsLockedOut) + { + return IdentityServerSecurityLogActionConsts.LoginLockedout; + } + + if (result.RequiresTwoFactor) + { + return IdentityServerSecurityLogActionConsts.LoginRequiresTwoFactor; + } + + if (result.IsNotAllowed) + { + return IdentityServerSecurityLogActionConsts.LoginNotAllowed; + } + + if (!result.Succeeded) + { + return IdentityServerSecurityLogActionConsts.LoginFailed; + } + + return IdentityServerSecurityLogActionConsts.LoginFailed; + } + } +} From 1cc65bc3ef726ba78ef3a90a1aec59fe645b843a Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Mon, 20 Jul 2020 16:03:01 +0800 Subject: [PATCH 14/77] Remove TenantId from IdentitySecurityLogContext. --- .../Volo/Abp/Identity/IdentitySecurityLogContext.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogContext.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogContext.cs index 42ec5e6fe5..740688ff84 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogContext.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentitySecurityLogContext.cs @@ -5,8 +5,6 @@ namespace Volo.Abp.Identity { public class IdentitySecurityLogContext { - public Guid? TenantId { get; set; } - public string Identity { get; set; } public string Action { get; set; } From 2154dd28532b2f62d20e2624791a7c1d74da4b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 20 Jul 2020 17:36:43 +0300 Subject: [PATCH 15/77] Implemented domain layer of the Authors. --- docs/en/Tutorials/Part-1.md | 1 + docs/en/Tutorials/Part-2.md | 1 + docs/en/Tutorials/Part-3.md | 1 + docs/en/Tutorials/Part-4.md | 1 + docs/en/Tutorials/Part-5.md | 4 + docs/en/Tutorials/Part-6.md | 207 ++++++++++++++++++++++++++++++++++-- 6 files changed, 209 insertions(+), 6 deletions(-) diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index 6401cb7a35..1b4c9ab814 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -37,6 +37,7 @@ This tutorial is organized as the following parts; - [Part 3: Creating, updating and deleting books](Part-3.md) - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) +- [Part 6: Author: Domain layer](Part-6.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 690394264c..5ceb2552d6 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -37,6 +37,7 @@ This tutorial is organized as the following parts; - [Part 3: Creating, updating and deleting books](Part-3.md) - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) +- [Part 6: Author: Domain layer](Part-6.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index 15cbf2229b..6a8bdca5e5 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -37,6 +37,7 @@ This tutorial is organized as the following parts; - **Part 3: Creating, updating and deleting books (this part)** - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) +- [Part 6: Author: Domain layer](Part-6.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-4.md b/docs/en/Tutorials/Part-4.md index 45505cc42f..5e4117ea6c 100644 --- a/docs/en/Tutorials/Part-4.md +++ b/docs/en/Tutorials/Part-4.md @@ -37,6 +37,7 @@ This tutorial is organized as the following parts; - [Part 3: Creating, updating and deleting books](Part-3.md) - **Part 4: Integration tests (this part)** - [Part 5: Authorization](Part-5.md) +- [Part 6: Author: Domain layer](Part-6.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-5.md b/docs/en/Tutorials/Part-5.md index c99dc109a2..916366ca98 100644 --- a/docs/en/Tutorials/Part-5.md +++ b/docs/en/Tutorials/Part-5.md @@ -37,6 +37,7 @@ This tutorial is organized as the following parts; - [Part 3: Creating, updating and deleting books](Part-3.md) - [Part 4: Integration tests](Part-4.md) - **Part 5: Authorization (this part)** +- [Part 6: Author: Domain layer](Part-6.md) ### Download the Source Code @@ -399,3 +400,6 @@ Open the `/src/app/book/book.component.html` file and replace the edit and delet {{end}} +## The Next Part + +See the [next part](part-6.md) of this tutorial. \ No newline at end of file diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index 9fe9196c58..ce58b5da8a 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -37,7 +37,7 @@ This tutorial is organized as the following parts; - [Part 3: Creating, updating and deleting books](Part-3.md) - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) -- **Part 6: The author entity (this part)** +- **Part 6: Author: Domain layer (this part)** ### Download the Source Code @@ -54,7 +54,12 @@ In the previous parts, we've used the ABP infrastructure to easily build some se * Used [generic repositories](../Repositories.md) to completely automate the database layer. * Used [conventional API controllers](../API/Auto-API-Controllers.md) instead of manually writing API controllers. -For the "Authors" part, we will do most of the things manually to show how you can do it in case of need. +For the "Authors" part; + +* We will **do most of the things manually** to show how you can do it in case of need. +* We will implement **Domain Driven Design (DDD) best practices**. + +> **The development will be done layer by layer to concentrate on an individual layer in one time. In a real project, you will develop your application feature by feature (vertical) as done in the previous parts. In this way, you will experience both approaches.** ## The Author Entity @@ -62,20 +67,210 @@ Create an `Authors` folder (namespace) in the `Acme.BookStore.Domain` project an ````csharp using System; +using JetBrains.Annotations; +using Volo.Abp; using Volo.Abp.Domain.Entities.Auditing; namespace Acme.BookStore.Authors { public class Author : FullAuditedAggregateRoot { - public string Name { get; internal set; } - + public string Name { get; private set; } public DateTime BirthDate { get; set; } - public string ShortBio { get; set; } + + private Author() + { + /* This constructor is for deserialization / ORM purpose */ + } + + internal Author( + Guid id, + [NotNull] string name, + DateTime birthDate, + [CanBeNull] string shortBio = null) + : base(id) + { + SetName(name); + BirthDate = birthDate; + ShortBio = shortBio; + } + + internal Author ChangeName([NotNull] string name) + { + SetName(name); + return this; + } + + private void SetName([NotNull] string name) + { + Name = Check.NotNullOrWhiteSpace( + name, + nameof(name), + maxLength: AuthorConsts.MaxNameLength + ); + } } } ```` * Inherited from `FullAuditedAggregateRoot` which makes the entity [soft delete](../Data-Filtering.md) (that means when you delete it, it is not deleted in the database, but just marked as deleted) with all the [auditing](../Entities.md) properties. -* `internal set` for the `Name` property restricts to set this property from out of the domain layer. Because we want to change it in a controlled way in the domain layer to prevent to create two authors with the same name, to just demonstrate a simple business rule. \ No newline at end of file +* `private set` for the `Name` property restricts to set this property from out of this class. There are two ways of setting the name (in both cases, we validate the name): + * In the constructor, while creating a new author. + * Using the `ChangeName` method to update the name later. +* The `constructor` and the `ChangeName` method is `internal` to force to use these methods only in the domain layer, using the `AuthorManager` that will be explained below. +* `Check` class is an ABP Framework utility class to help you while checking method arguments (it throws exception on an invalid case). + +`AuthorConsts` is a simple class that is located under the `Authors` namespace of the `Acme.BookStore.Domain.Shared` project: + +````csharp +namespace Acme.BookStore.Authors +{ + public static class AuthorConsts + { + public const int MaxNameLength = 64; + } +} +```` + +Created this class inside the `Acme.BookStore.Domain.Shared` project since we will re-use it on the Data Transfer Objects (DTOs) later. + +## AuthorManager: The Domain Service + +`Author` constructor and `ChangeName` method is `internal`, so they can be usable only in the domain layer. Create an `AuthorManager` class in the `Authors` folder (namespace) of the `Acme.BookStore.Domain` project: + +````csharp +using System; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp; +using Volo.Abp.Domain.Services; + +namespace Acme.BookStore.Authors +{ + public class AuthorManager : DomainService + { + private readonly IAuthorRepository _authorRepository; + + public AuthorManager(IAuthorRepository authorRepository) + { + _authorRepository = authorRepository; + } + + public async Task CreateAsync( + [NotNull] string name, + DateTime birthDate, + [CanBeNull] string shortBio = null) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + + var existingAuthor = _authorRepository.FindByNameAsync(name); + if (existingAuthor != null) + { + throw new AuthorAlreadyExistsException(name); + } + + return new Author( + GuidGenerator.Create(), + name, + birthDate, + shortBio + ); + } + + public async Task ChangeNameAsync( + [NotNull] Author author, + [NotNull] string newName) + { + Check.NotNull(author, nameof(author)); + Check.NotNullOrWhiteSpace(newName, nameof(newName)); + + var existingAuthor = await _authorRepository.FindByNameAsync(newName); + if (existingAuthor != null && existingAuthor.Id != author.Id) + { + throw new AuthorAlreadyExistsException(newName); + } + + author.ChangeName(newName); + } + } +} +```` + +* `AuthorManager` forces to create an author and change name of an author in a controlled way. The application layer (will be introduced later) will use these methods. + +> **DDD tip**: Do not introduce domain service methods unless they are needed and they perform core business rules. For this case, we needed to this service to be able to force the unique name constraint. + +Both methods checks if there is already an author with the given name and throws a special business exception, `AuthorAlreadyExistsException`, defined as shown below: + +````csharp +using Volo.Abp; + +namespace Acme.BookStore.Authors +{ + public class AuthorAlreadyExistsException : BusinessException + { + public AuthorAlreadyExistsException(string name) + : base(BookStoreDomainErrorCodes.AuthorAlreadyExists) + { + WithData("name", name); + } + } +} +```` + +`BusinessException` is a special exception type that. It is a good way to throw domain related exceptions. It is automatically handled by the ABP Framework and can be easily localized. `WithData` method is used to provide additional data to the exception object that will later be used on the localization message or for some other purpose. + +Open the `BookStoreDomainErrorCodes` in the `Acme.BookStore.Domain.Shared` project and change as shown below: + +````csharp +namespace Acme.BookStore +{ + public static class BookStoreDomainErrorCodes + { + public const string AuthorAlreadyExists = "BookStore:00001"; + } +} +```` + +This is a unique string represents the error code thrown by your application and can be handled by client applications. For users, you probably want to localize it. Open the `Localization/BookStore/en.json` inside the `Acme.BookStore.Domain.Shared` project and add the following entry: + +````json +"BookStore:00001": "There is already an author with the same name: {name}" +```` + +Whenever you throw an `AuthorAlreadyExistsException`, the end use will see a nice error message on the UI. + +## IAuthorRepository + +`AuthorManager` inject the `IAuthorRepository`, so we need to define it. Create this new interface in the `Authors` folder (namespace) of the `Acme.BookStore.Domain` project: + +````charp +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore.Authors +{ + public interface IAuthorRepository : IRepository + { + Task FindByNameAsync(string name); + + Task> GetListAsync( + int skipCount, + int maxResultCount, + string sorting, + string filter = "" + ); + } +} +```` + +* `IAuthorRepository` extends the standard `IRepository` interface, so all the standard [repository](../Repositories.md) methods will also be available for the `IAuthorRepository`. +* `FindByNameAsync` was used in the `AuthorManager` to query an author by name. +* `GetListAsync` will be used in the application layer to get a listed, sorted and filtered list of authors to show on the UI. + +We will implement this repository in the next parts. + +> Both of these methods might **seem unnecessary** since the standard repositories already `IQueryable` and you can directly use them instead of defining such custom methods. You're right and do it like in a real application. However, for this **"learning" tutorial**, it is useful to explain how to create custom repository methods. \ No newline at end of file From e940e604fdc262ee1ffa5ac3a342b487a157b09d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 20 Jul 2020 17:45:11 +0300 Subject: [PATCH 16/77] Added the 7th part: Author: Database integration. --- docs/en/Tutorials/Part-1.md | 1 + docs/en/Tutorials/Part-2.md | 1 + docs/en/Tutorials/Part-3.md | 1 + docs/en/Tutorials/Part-4.md | 1 + docs/en/Tutorials/Part-5.md | 1 + docs/en/Tutorials/Part-6.md | 3 ++- docs/en/Tutorials/Part-7.md | 52 +++++++++++++++++++++++++++++++++++++ 7 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 docs/en/Tutorials/Part-7.md diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index 1b4c9ab814..6d59481bec 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -38,6 +38,7 @@ This tutorial is organized as the following parts; - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) - [Part 6: Author: Domain layer](Part-6.md) +- [Part 7: Author: Database Integration](Part-7.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 5ceb2552d6..13725696c9 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -38,6 +38,7 @@ This tutorial is organized as the following parts; - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) - [Part 6: Author: Domain layer](Part-6.md) +- [Part 7: Author: Database Integration](Part-7.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index 6a8bdca5e5..09ebcba338 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -38,6 +38,7 @@ This tutorial is organized as the following parts; - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) - [Part 6: Author: Domain layer](Part-6.md) +- [Part 7: Author: Database Integration](Part-7.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-4.md b/docs/en/Tutorials/Part-4.md index 5e4117ea6c..0dd34621aa 100644 --- a/docs/en/Tutorials/Part-4.md +++ b/docs/en/Tutorials/Part-4.md @@ -38,6 +38,7 @@ This tutorial is organized as the following parts; - **Part 4: Integration tests (this part)** - [Part 5: Authorization](Part-5.md) - [Part 6: Author: Domain layer](Part-6.md) +- [Part 7: Author: Database Integration](Part-7.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-5.md b/docs/en/Tutorials/Part-5.md index 916366ca98..803bad1492 100644 --- a/docs/en/Tutorials/Part-5.md +++ b/docs/en/Tutorials/Part-5.md @@ -38,6 +38,7 @@ This tutorial is organized as the following parts; - [Part 4: Integration tests](Part-4.md) - **Part 5: Authorization (this part)** - [Part 6: Author: Domain layer](Part-6.md) +- [Part 7: Author: Database Integration](Part-7.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index ce58b5da8a..dcd36e3a4e 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -1,4 +1,4 @@ -# Web Application Development Tutorial - Part 6: Authors +# Web Application Development Tutorial - Part 6: Authors: Domain Layer ````json //[doc-params] { @@ -38,6 +38,7 @@ This tutorial is organized as the following parts; - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) - **Part 6: Author: Domain layer (this part)** +- [Part 7: Author: Database Integration](Part-7.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md new file mode 100644 index 0000000000..797a09fcf3 --- /dev/null +++ b/docs/en/Tutorials/Part-7.md @@ -0,0 +1,52 @@ +# Web Application Development Tutorial - Part 7: Authors: Database Integration +````json +//[doc-params] +{ + "UI": ["MVC","NG"], + "DB": ["EF","Mongo"] +} +```` +{{ +if UI == "MVC" + UI_Text="mvc" +else if UI == "NG" + UI_Text="angular" +else + UI_Text="?" +end +if DB == "EF" + DB_Text="Entity Framework Core" +else if DB == "Mongo" + DB_Text="MongoDB" +else + DB_Text="?" +end +}} + +## About This Tutorial + +In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies: + +* **{{DB_Text}}** as the ORM provider. +* **{{UI_Value}}** as the UI Framework. + +This tutorial is organized as the following parts; + +- [Part 1: Creating the server side](Part-1.md) +- [Part 2: The book list page](Part-2.md) +- [Part 3: Creating, updating and deleting books](Part-3.md) +- [Part 4: Integration tests](Part-4.md) +- [Part 5: Authorization](Part-5.md) +- [Part 6: Author: Domain layer](Part-6.md) +- **Part 7: Author: Database Integration (this part)** + +### Download the Source Code + +This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded: + +* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +## DB Context + +TODO \ No newline at end of file From 6d26c92d08df7de7ada4891b53a7af60ca7087a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 20 Jul 2020 17:52:57 +0300 Subject: [PATCH 17/77] Added link to the next part. --- docs/en/Tutorials/Part-6.md | 12 +++++++++++- .../images/bookstore-author-domain-layer.png | Bin 0 -> 76496 bytes 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 docs/en/Tutorials/images/bookstore-author-domain-layer.png diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index dcd36e3a4e..bedd006d6a 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -274,4 +274,14 @@ namespace Acme.BookStore.Authors We will implement this repository in the next parts. -> Both of these methods might **seem unnecessary** since the standard repositories already `IQueryable` and you can directly use them instead of defining such custom methods. You're right and do it like in a real application. However, for this **"learning" tutorial**, it is useful to explain how to create custom repository methods. \ No newline at end of file +> Both of these methods might **seem unnecessary** since the standard repositories already `IQueryable` and you can directly use them instead of defining such custom methods. You're right and do it like in a real application. However, for this **"learning" tutorial**, it is useful to explain how to create custom repository methods. + +## Conclusion + +This part covered the domain layer of the authors functionality of the book store application. The main files created/updated in this part was highlighted in the picture below: + +![bookstore-author-domain-layer](images/bookstore-author-domain-layer.png) + +## The Next Part + +See the [next part](part-7.md) of this tutorial. \ No newline at end of file diff --git a/docs/en/Tutorials/images/bookstore-author-domain-layer.png b/docs/en/Tutorials/images/bookstore-author-domain-layer.png new file mode 100644 index 0000000000000000000000000000000000000000..fde53834bbefd32bb060a39423215180bafc31dc GIT binary patch literal 76496 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¨ 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

%}!cF-hL^;;a52O$dISY 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 Vr(b}Jx?F+4V`Xk{_5^t<@qa1=d{O`a literal 0 HcmV?d00001 From 005504dc4d5180471a6676fa603a6345ac525796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 20 Jul 2020 18:17:27 +0300 Subject: [PATCH 18/77] Completed to part 7. --- docs/en/Tutorials/Part-6.md | 6 +-- docs/en/Tutorials/Part-7.md | 100 +++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index bedd006d6a..8b97ac63fc 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -262,7 +262,7 @@ namespace Acme.BookStore.Authors int skipCount, int maxResultCount, string sorting, - string filter = "" + string filter = null ); } } @@ -272,9 +272,9 @@ namespace Acme.BookStore.Authors * `FindByNameAsync` was used in the `AuthorManager` to query an author by name. * `GetListAsync` will be used in the application layer to get a listed, sorted and filtered list of authors to show on the UI. -We will implement this repository in the next parts. +We will implement this repository in the next part. -> Both of these methods might **seem unnecessary** since the standard repositories already `IQueryable` and you can directly use them instead of defining such custom methods. You're right and do it like in a real application. However, for this **"learning" tutorial**, it is useful to explain how to create custom repository methods. +> Both of these methods might **seem unnecessary** since the standard repositories already `IQueryable` and you can directly use them instead of defining such custom methods. You're right and do it like in a real application. However, for this **"learning" tutorial**, it is useful to explain how to create custom repository methods when you really need it. ## Conclusion diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md index 797a09fcf3..fb69894e97 100644 --- a/docs/en/Tutorials/Part-7.md +++ b/docs/en/Tutorials/Part-7.md @@ -47,6 +47,104 @@ This tutorials has multiple versions based on your **UI** and **Database** prefe * [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) * [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) +## Introduction + +This part explains to configure the database integration for the `Author` entity introduced in the previous part. + ## DB Context -TODO \ No newline at end of file +{{if DB=="EF"}} + +Open the `BookStoreDbContext` in the `Acme.BookStore.EntityFrameworkCore` project and add the following `DbSet` property: + +````csharp +public DbSet Authors { get; set; } +```` + +Then open the `BookStoreDbContextModelCreatingExtensions` class in the same project and add the following lines to the end of the `ConfigureBookStore` method: + +````csharp +builder.Entity(b => +{ + b.ToTable(BookStoreConsts.DbTablePrefix + "Books", + BookStoreConsts.DbSchema); + + b.ConfigureByConvention(); + + b.Property(x => x.Name) + .IsRequired() + .HasMaxLength(AuthorConsts.MaxNameLength); +}); +```` + +This is just like done for the `Book` entity before, so no need to explain again. + +{{else if DB=="Mongo"}} + +TODO, for MongoDB + +{{end}} + +## Implementing the IAuthorRepository + +{{if DB=="EF"}} + +Create a new class, named `EfCoreAuthorRepository` inside the `Acme.BookStore.EntityFrameworkCore` project (in the `Authors` folder) and paste the following code: + +````csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading.Tasks; +using Acme.BookStore.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace Acme.BookStore.Authors +{ + public class EfCoreAuthorRepository + : EfCoreRepository, + IAuthorRepository + { + public EfCoreAuthorRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async Task FindByNameAsync(string name) + { + return await DbSet.FirstOrDefaultAsync(author => author.Name == name); + } + + public async Task> GetListAsync( + int skipCount, + int maxResultCount, + string sorting, + string filter = null) + { + return await DbSet + .WhereIf( + !filter.IsNullOrWhiteSpace(), + author => author.Name.Contains(filter) + ) + .OrderBy(sorting) + .Skip(skipCount) + .Take(maxResultCount) + .ToListAsync(); + } + } +} +```` + +* Inherited from the `EfCoreAuthorRepository`, so it inherits the standard repository method implementations. +* `WhereIf` is a shortcut extension method of the ABP Framework. It adds the `Where` condition only if the first condition meets (it filters by name, only if the filter was provided). You could do the same yourself, but these type of shortcut methods makes our life easier. +* `sorting` can be a string like `Name`, `Name ASC` or `Name DESC`. It is possible by using the [System.Linq.Dynamic.Core](https://www.nuget.org/packages/System.Linq.Dynamic.Core) NuGet package. + +{{else if DB=="Mongo"}} + +TODO, for MongoDB + +{{end}} \ No newline at end of file From 6a2d020d5ff3288a60595ad6238e9c71f235de9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 20 Jul 2020 21:11:30 +0300 Subject: [PATCH 19/77] Added part-8 to the tutorial. --- docs/en/Tutorials/Part-1.md | 5 ++-- docs/en/Tutorials/Part-2.md | 5 ++-- docs/en/Tutorials/Part-3.md | 5 ++-- docs/en/Tutorials/Part-4.md | 5 ++-- docs/en/Tutorials/Part-5.md | 5 ++-- docs/en/Tutorials/Part-6.md | 5 ++-- docs/en/Tutorials/Part-7.md | 7 +++-- docs/en/Tutorials/Part-8.md | 57 +++++++++++++++++++++++++++++++++++++ 8 files changed, 79 insertions(+), 15 deletions(-) create mode 100644 docs/en/Tutorials/Part-8.md diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index 6d59481bec..7de5e54c16 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -37,8 +37,9 @@ This tutorial is organized as the following parts; - [Part 3: Creating, updating and deleting books](Part-3.md) - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) -- [Part 6: Author: Domain layer](Part-6.md) -- [Part 7: Author: Database Integration](Part-7.md) +- [Part 6: Authors: Domain layer](Part-6.md) +- [Part 7: Authors: Database Integration](Part-7.md) +- [Part 8: Authors: Application Layer](Part-8.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 13725696c9..ec86bd1ac3 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -37,8 +37,9 @@ This tutorial is organized as the following parts; - [Part 3: Creating, updating and deleting books](Part-3.md) - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) -- [Part 6: Author: Domain layer](Part-6.md) -- [Part 7: Author: Database Integration](Part-7.md) +- [Part 6: Authors: Domain layer](Part-6.md) +- [Part 7: Authors: Database Integration](Part-7.md) +- [Part 8: Authors: Application Layer](Part-8.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index 09ebcba338..16edf047e9 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -37,8 +37,9 @@ This tutorial is organized as the following parts; - **Part 3: Creating, updating and deleting books (this part)** - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) -- [Part 6: Author: Domain layer](Part-6.md) -- [Part 7: Author: Database Integration](Part-7.md) +- [Part 6: Authors: Domain layer](Part-6.md) +- [Part 7: Authors: Database Integration](Part-7.md) +- [Part 8: Authors: Application Layer](Part-8.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-4.md b/docs/en/Tutorials/Part-4.md index 0dd34621aa..4cf2628c58 100644 --- a/docs/en/Tutorials/Part-4.md +++ b/docs/en/Tutorials/Part-4.md @@ -37,8 +37,9 @@ This tutorial is organized as the following parts; - [Part 3: Creating, updating and deleting books](Part-3.md) - **Part 4: Integration tests (this part)** - [Part 5: Authorization](Part-5.md) -- [Part 6: Author: Domain layer](Part-6.md) -- [Part 7: Author: Database Integration](Part-7.md) +- [Part 6: Authors: Domain layer](Part-6.md) +- [Part 7: Authors: Database Integration](Part-7.md) +- [Part 8: Authors: Application Layer](Part-8.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-5.md b/docs/en/Tutorials/Part-5.md index 803bad1492..4094640cc2 100644 --- a/docs/en/Tutorials/Part-5.md +++ b/docs/en/Tutorials/Part-5.md @@ -37,8 +37,9 @@ This tutorial is organized as the following parts; - [Part 3: Creating, updating and deleting books](Part-3.md) - [Part 4: Integration tests](Part-4.md) - **Part 5: Authorization (this part)** -- [Part 6: Author: Domain layer](Part-6.md) -- [Part 7: Author: Database Integration](Part-7.md) +- [Part 6: Authors: Domain layer](Part-6.md) +- [Part 7: Authors: Database Integration](Part-7.md) +- [Part 8: Authors: Application Layer](Part-8.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index 8b97ac63fc..e62bf03391 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -37,8 +37,9 @@ This tutorial is organized as the following parts; - [Part 3: Creating, updating and deleting books](Part-3.md) - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) -- **Part 6: Author: Domain layer (this part)** -- [Part 7: Author: Database Integration](Part-7.md) +- **Part 6: Authors: Domain layer (this part)** +- [Part 7: Authors: Database Integration](Part-7.md) +- [Part 8: Authors: Application Layer](Part-8.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md index fb69894e97..d925eb5e17 100644 --- a/docs/en/Tutorials/Part-7.md +++ b/docs/en/Tutorials/Part-7.md @@ -37,8 +37,9 @@ This tutorial is organized as the following parts; - [Part 3: Creating, updating and deleting books](Part-3.md) - [Part 4: Integration tests](Part-4.md) - [Part 5: Authorization](Part-5.md) -- [Part 6: Author: Domain layer](Part-6.md) -- **Part 7: Author: Database Integration (this part)** +- [Part 6: Authors: Domain layer](Part-6.md) +- **Part 7: Authors: Database Integration (this part)** +- [Part 8: Authors: Application Layer](Part-8.md) ### Download the Source Code @@ -49,7 +50,7 @@ This tutorials has multiple versions based on your **UI** and **Database** prefe ## Introduction -This part explains to configure the database integration for the `Author` entity introduced in the previous part. +This part explains how to configure the database integration for the `Author` entity introduced in the previous part. ## DB Context diff --git a/docs/en/Tutorials/Part-8.md b/docs/en/Tutorials/Part-8.md new file mode 100644 index 0000000000..1845cefd7e --- /dev/null +++ b/docs/en/Tutorials/Part-8.md @@ -0,0 +1,57 @@ +# Web Application Development Tutorial - Part 8: Authors: Application Layer +````json +//[doc-params] +{ + "UI": ["MVC","NG"], + "DB": ["EF","Mongo"] +} +```` +{{ +if UI == "MVC" + UI_Text="mvc" +else if UI == "NG" + UI_Text="angular" +else + UI_Text="?" +end +if DB == "EF" + DB_Text="Entity Framework Core" +else if DB == "Mongo" + DB_Text="MongoDB" +else + DB_Text="?" +end +}} + +## About This Tutorial + +In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies: + +* **{{DB_Text}}** as the ORM provider. +* **{{UI_Value}}** as the UI Framework. + +This tutorial is organized as the following parts; + +- [Part 1: Creating the server side](Part-1.md) +- [Part 2: The book list page](Part-2.md) +- [Part 3: Creating, updating and deleting books](Part-3.md) +- [Part 4: Integration tests](Part-4.md) +- [Part 5: Authorization](Part-5.md) +- [Part 6: Authors: Domain layer](Part-6.md) +- [Part 7: Authors: Database Integration](Part-7.md) +- **Part 8: Author: Application Layer (this part)** + +### Download the Source Code + +This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded: + +* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +## Introduction + +This part explains to create an application layer for the `Author` entity created before. + +## Application Service + +TODO \ No newline at end of file From d858f3a2c570920a2d4ee95133d7e2e284a1983a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 20 Jul 2020 22:00:53 +0300 Subject: [PATCH 20/77] Update Part-7.md --- docs/en/Tutorials/Part-7.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md index d925eb5e17..85234be21f 100644 --- a/docs/en/Tutorials/Part-7.md +++ b/docs/en/Tutorials/Part-7.md @@ -67,7 +67,7 @@ Then open the `BookStoreDbContextModelCreatingExtensions` class in the same proj ````csharp builder.Entity(b => { - b.ToTable(BookStoreConsts.DbTablePrefix + "Books", + b.ToTable(BookStoreConsts.DbTablePrefix + "Authors", BookStoreConsts.DbSchema); b.ConfigureByConvention(); From 1938a2648eec9f72a59d0291aeb8a91d54a35fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 20 Jul 2020 22:00:58 +0300 Subject: [PATCH 21/77] Update Part-1.md --- docs/en/Tutorials/Part-1.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index 7de5e54c16..50d82e4df9 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -230,6 +230,11 @@ namespace Acme.BookStore } public async Task SeedAsync(DataSeedContext context) + { + await CreateBooksAsync(); + } + + private async Task CreateBooksAsync() { if (await _bookRepository.GetCountAsync() > 0) { From a63bdeebb1ab49742c97886c38bc2a5bdf64a459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 20 Jul 2020 22:01:06 +0300 Subject: [PATCH 22/77] Update Part-6.md --- docs/en/Tutorials/Part-6.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index e62bf03391..ec8815cc29 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -166,7 +166,7 @@ namespace Acme.BookStore.Authors { Check.NotNullOrWhiteSpace(name, nameof(name)); - var existingAuthor = _authorRepository.FindByNameAsync(name); + var existingAuthor = await _authorRepository.FindByNameAsync(name); if (existingAuthor != null) { throw new AuthorAlreadyExistsException(name); From 7d4c34a01e6c5d0efa02f17a4d22e10da481cc56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 20 Jul 2020 22:26:34 +0300 Subject: [PATCH 23/77] IAuthorAppService section completed. --- docs/en/Tutorials/Part-8.md | 123 +++++++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 2 deletions(-) diff --git a/docs/en/Tutorials/Part-8.md b/docs/en/Tutorials/Part-8.md index 1845cefd7e..d5dcdae579 100644 --- a/docs/en/Tutorials/Part-8.md +++ b/docs/en/Tutorials/Part-8.md @@ -52,6 +52,125 @@ This tutorials has multiple versions based on your **UI** and **Database** prefe This part explains to create an application layer for the `Author` entity created before. -## Application Service +## IAuthorAppService + +We will first create the [application service](../Application-Services.md) interface and the related [DTO](../Data-Transfer-Objects.md)s. Create a new interface, named `IAuthorAppService`, in the `Authors` namespace (folder) of the `Acme.BookStore.Application.Contracts` project: + +````csharp +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Acme.BookStore.Authors +{ + public interface IAuthorAppService : IApplicationService + { + Task GetAsync(Guid id); + + Task> GetListAsync(GetAuthorListDto input); + + Task CreateAsync(CreateAuthorDto input); + + Task UpdateAsync(Guid id, UpdateAuthorDto input); + + Task DeleteAsync(Guid id); + } +} +```` + +* `IApplicationService` is a conventional interface that is inherited by all the application services, so the ABP Framework can identify the service. +* Defined standard methods to perform CRUD operations on the `Author` entity. +* `PagedResultDto` is a pre-defined DTO class in the ABP Framework. It has an `Items` collection and a `TotalCount` property to return a paged result. +* Preferred to return an `AuthorDto` (for the newly created author) from the `CreateAsync` method, while it is not used by this application - just to show a different usage. + +This interface is using the DTOs defined below. + +### AuthorDto + +````csharp +using System; +using Volo.Abp.Application.Dtos; + +namespace Acme.BookStore.Authors +{ + public class AuthorDto : EntityDto + { + public string Name { get; set; } + + public DateTime BirthDate { get; set; } + + public string ShortBio { get; set; } + } +} +```` + +* `EntityDto` simply has an `Id` property with the given generic argument. You could create an `Id` property yourself instead of inheriting the `EntityDto`. + +### GetAuthorListDto + +````csharp +using Volo.Abp.Application.Dtos; + +namespace Acme.BookStore.Authors +{ + public class GetAuthorListDto : PagedAndSortedResultRequestDto + { + public string Filter { get; set; } + } +} +```` + +* `Filter` is used to search authors. It can be `null` (or empty string) to get all the authors. +* `PagedAndSortedResultRequestDto` has the standard paging and sorting properties: `int MaxResultCount`, `int SkipCount` and `string Sorting`. + +> ABP Framework has such base DTO classes to simplify and standardize your DTOs. See the [DTO documentation](../Data-Transfer-Objects.md) for all. + +### CreateAuthorDto + +````csharp +using System; +using System.ComponentModel.DataAnnotations; + +namespace Acme.BookStore.Authors +{ + public class CreateAuthorDto + { + [Required] + [StringLength(AuthorConsts.MaxNameLength)] + public string Name { get; set; } + + [Required] + public DateTime BirthDate { get; set; } + + public string ShortBio { get; set; } + } +} +```` + +Data annotation attributes can be used to validate the DTO. See the [validation document](../Validation.md) for details. + +### UpdateAuthorDto + +````csharp +using System; +using System.ComponentModel.DataAnnotations; + +namespace Acme.BookStore.Authors +{ + public class UpdateAuthorDto + { + [Required] + [StringLength(AuthorConsts.MaxNameLength)] + public string Name { get; set; } + + [Required] + public DateTime BirthDate { get; set; } + + public string ShortBio { get; set; } + } +} +```` + +We could share (re-use) the same DTO among the create and the update operations. While you can do it, we prefer to create different DTOs for these operations since we see they generally be different by the time. So, code duplication is reasonable here compared to a tightly coupled design. -TODO \ No newline at end of file From 720d574cd105647889a279f23308c3dbaac84fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 20 Jul 2020 23:33:50 +0300 Subject: [PATCH 24/77] Completed the part-8 of the tutorial. --- docs/en/Tutorials/Part-7.md | 6 +- docs/en/Tutorials/Part-8.md | 379 ++++++++++++++++++++++++++++++++++++ 2 files changed, 384 insertions(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md index 85234be21f..713399a206 100644 --- a/docs/en/Tutorials/Part-7.md +++ b/docs/en/Tutorials/Part-7.md @@ -148,4 +148,8 @@ namespace Acme.BookStore.Authors TODO, for MongoDB -{{end}} \ No newline at end of file +{{end}} + +## The Next Part + +See the [next part](part-8.md) of this tutorial. \ No newline at end of file diff --git a/docs/en/Tutorials/Part-8.md b/docs/en/Tutorials/Part-8.md index d5dcdae579..52505752d5 100644 --- a/docs/en/Tutorials/Part-8.md +++ b/docs/en/Tutorials/Part-8.md @@ -174,3 +174,382 @@ namespace Acme.BookStore.Authors We could share (re-use) the same DTO among the create and the update operations. While you can do it, we prefer to create different DTOs for these operations since we see they generally be different by the time. So, code duplication is reasonable here compared to a tightly coupled design. +## AuthorAppService + +It is time to implement the `IAuthorAppService` interface. Create a new class, named `AuthorAppService` in the `Authors` namespace (folder) of the `Acme.BookStore.Application` project: + +````csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Acme.BookStore.Permissions; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Application.Dtos; + +namespace Acme.BookStore.Authors +{ + [Authorize(BookStorePermissions.Authors.Default)] + public class AuthorAppService : BookStoreAppService, IAuthorAppService + { + private readonly IAuthorRepository _authorRepository; + private readonly AuthorManager _authorManager; + + public AuthorAppService( + IAuthorRepository authorRepository, + AuthorManager authorManager) + { + _authorRepository = authorRepository; + _authorManager = authorManager; + } + + //...SERVICE METHODS WILL COME HERE... + } +} +```` + +* `[Authorize(BookStorePermissions.Authors.Default)]` is a declarative way to check a permission (policy) to authorize the current user. See the [authorization document](../Authorization.md) for more. `BookStorePermissions` class will be updated below, don't worry for the compile error for now. +* Derived from the `BookStoreAppService`, which is a simple base class comes with the startup template. It is derived from the standard `ApplicationService` class. +* Implemented the `IAuthorAppService` which was defined above. +* Injected the `IAuthorRepository` and `AuthorManager` to use in the service methods. + +Now, we will introduce the service methods one by one. Copy the explained method into the `AuthorAppService` class. + +### GetAsync + +````csharp +public async Task GetAsync(Guid id) +{ + var author = await _authorRepository.GetAsync(id); + return ObjectMapper.Map(author); +} +```` + +This method simply gets the `Author` entity by its `Id`, converts to the `AuthorDto` using the [object to object mapper](../Object-To-Object-Mapping.md). This requires to configure the AutoMapper, which will be explained later. + +### GetListAsync + +````csharp +public async Task> GetListAsync(GetAuthorListDto input) +{ + if (input.Sorting.IsNullOrWhiteSpace()) + { + input.Sorting = nameof(Author.Name); + } + + var authors = await _authorRepository.GetListAsync( + input.SkipCount, + input.MaxResultCount, + input.Sorting, + input.Filter + ); + + var totalCount = await AsyncExecuter.CountAsync( + _authorRepository.WhereIf( + !input.Filter.IsNullOrWhiteSpace(), + author => author.Name.Contains(input.Filter) + ) + ); + + return new PagedResultDto( + totalCount, + ObjectMapper.Map, List>(authors) + ); +} +```` + +* Default sorting is "by author name" which is done in the beginning of the method in case of it wasn't sent by the client. +* Used the `IAuthorRepository.GetListAsync` to get a paged, sorted and filtered list of authors from the database. We had implemented it in the previous part of this tutorial. Again, it actually was not needed to create such a method since we could directly query over the repository, but wanted to demonstrate how to create custom repository methods. +* Directly queried from the `AuthorRepository` while getting the count of the authors. We preferred to use the `AsyncExecuter` service which allows us to perform async queries without depending on the EF Core. However, you could depend on the EF Core package and directly use the `_authorRepository.WhereIf(...).ToListAsync()` method. See the [repository document](../Repositories.md) to read the alternative approaches and the discussion. +* Finally, returning a paged result by mapping the list of `Author`s to a list of `AuthorDto`s. + +### CreateAsync + +````csharp +[Authorize(BookStorePermissions.Authors.Create)] +public async Task CreateAsync(CreateAuthorDto input) +{ + var author = await _authorManager.CreateAsync( + input.Name, + input.BirthDate, + input.ShortBio + ); + + await _authorRepository.InsertAsync(author); + + return ObjectMapper.Map(author); +} +```` + +* `CreateAsync` requires the `BookStorePermissions.Authors.Create` permission (in addition to the `BookStorePermissions.Authors.Default` declared for the `AuthorAppService` class). +* Used the `AuthorManeger` (domain service) to create a new author. +* Used the `IAuthorRepository.InsertAsync` to insert the new author to the database. +* Used the `ObjectMapper` to return an `AuthorDto` representing the newly created author. + +> **DDD tip**: Some developers may find useful to insert the new entity inside the `_authorManager.CreateAsync`. We think it is a better design to leave it to the application layer since it better knows when to insert it to the database (maybe it requires additional works on the entity before insert, which would require to an additional update if we perform the insert in the domain service). However, it is completely up to you. + +### UpdateAsync + +````csharp +[Authorize(BookStorePermissions.Authors.Edit)] +public async Task UpdateAsync(Guid id, UpdateAuthorDto input) +{ + var author = await _authorRepository.GetAsync(id); + + if (author.Name != input.Name) + { + await _authorManager.ChangeNameAsync(author, input.Name); + } + + author.BirthDate = input.BirthDate; + author.ShortBio = input.ShortBio; + + await _authorRepository.UpdateAsync(author); +} +```` + +* `UpdateAsync` requires the additional `BookStorePermissions.Authors.Edit` permission. +* Used the `IAuthorRepository.GetAsync` to get the author entity from the database. `GetAsync` throws `EntityNotFoundException` if there is no author with the given id, which results a `404` HTTP status code in a web application. It is a good practice to always bring the entity on an update operation. +* Used the `AuthorManager.ChangeNameAsync` (domain service method) to change the author name if it was requested to change by the client. +* Directly updated the `BirthDate` and `ShortBio` since there is not any business rule to change these properties, they accept any value. +* Finally, called the `IAuthorRepository.UpdateAsync` method to update the entity on the database. + +{{if DB == "EF"}} + +> **EF Core tip**: Entity Framework Core has a **change tracking** system and **automatically saves** any change to an entity at the end of the unit of work (You can simply think that the ABP Framework automatically calls `SaveChanges` at the end of the method). So, it will work as expected even if you don't call the `_authorRepository.UpdateAsync(...)` in the end of the method. If you don't consider to change the EF Core later, you can just remove this line. + +{{end}} + +### DeleteAsync + +````csharp +[Authorize(BookStorePermissions.Authors.Delete)] +public async Task DeleteAsync(Guid id) +{ + await _authorRepository.DeleteAsync(id); +} +```` + +* `DeleteAsync` requires the additional `BookStorePermissions.Authors.Delete` permission. +* It simply uses the `DeleteAsync` method of the repository. + +## Permission Definitions + +You can't compile the code since it is expecting some constants declared in the `BookStorePermissions` class. + +Open the `BookStorePermissions` class inside the `Acme.BookStore.Application.Contracts` project and change the content as shown below: + +````csharp +namespace Acme.BookStore.Permissions +{ + public static class BookStorePermissions + { + public const string GroupName = "BookStore"; + + public static class Books + { + public const string Default = GroupName + ".Books"; + public const string Create = Default + ".Create"; + public const string Edit = Default + ".Edit"; + public const string Delete = Default + ".Delete"; + } + + // *** ADDED a NEW NESTED CLASS *** + public static class Authors + { + public const string Default = GroupName + ".Authors"; + public const string Create = Default + ".Create"; + public const string Edit = Default + ".Edit"; + public const string Delete = Default + ".Delete"; + } + } +} +```` + +Then open the `BookStorePermissionDefinitionProvider` in the same project and add the following lines at the end of the `Define` method: + +````csharp +var authorsPermission = bookStoreGroup.AddPermission( + BookStorePermissions.Authors.Default, L("Permission:Authors")); + +authorsPermission.AddChild( + BookStorePermissions.Authors.Create, L("Permission:Authors.Create")); + +authorsPermission.AddChild( + BookStorePermissions.Authors.Edit, L("Permission:Authors.Edit")); + +authorsPermission.AddChild( + BookStorePermissions.Authors.Delete, L("Permission:Authors.Delete")); +```` + +Finally, add the following entries to the `Localization/BookStore/en.json` inside the `Acme.BookStore.Domain.Shared` project, to localize the permission names: + +````csharp +"Permission:Authors": "Author Management", +"Permission:Authors.Create": "Creating new authors", +"Permission:Authors.Edit": "Editing the authors", +"Permission:Authors.Delete": "Deleting the authors" +```` + +## Object to Object Mapping + +`AuthorAppService` is using the `ObjectMapper` to convert the `Author` objects to `AuthorDto` objects. So, we need to define this mapping in the AutoMapper configuration. + +Open the `BookStoreApplicationAutoMapperProfile` class inside the `Acme.BookStore.Application` project and add the following line to the constructor: + +````csharp +CreateMap(); +```` + +## Data Seeder + +As just done for the books before, it would be good to have some initial author entities in the database. This will be good while running the application first time, but also it is very useful for the automated tests. + +Open the `BookStoreDataSeederContributor` in the `Acme.BookStore.Domain` project and add a new `CreateAuthorsAsync` method as shown below: + +````csharp +using System; +using System.Threading.Tasks; +using Acme.BookStore.Authors; +using Acme.BookStore.Books; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore +{ + public class BookStoreDataSeederContributor + : IDataSeedContributor, ITransientDependency + { + private readonly IRepository _bookRepository; + private readonly IAuthorRepository _authorRepository; + private readonly AuthorManager _authorManager; + + public BookStoreDataSeederContributor( + IRepository bookRepository, + IAuthorRepository authorRepository, + AuthorManager authorManager) + { + _bookRepository = bookRepository; + _authorRepository = authorRepository; + _authorManager = authorManager; + } + + public async Task SeedAsync(DataSeedContext context) + { + await CreateAuthorsAsync(); // CALL the NEW METHOD + await CreateBooksAsync(); + } + + // ADDED a NEW METHOD + private async Task CreateAuthorsAsync() + { + if (await _authorRepository.GetCountAsync() > 0) + { + return; + } + + await _authorRepository.InsertAsync( + await _authorManager.CreateAsync( + "George Orwell", + new DateTime(1903, 06, 25), + "Orwell produced literary criticism and poetry..." + ) + ); + + await _authorRepository.InsertAsync( + await _authorManager.CreateAsync( + "Douglas Adams", + new DateTime(1952, 03, 11), + "Douglas Adams was an English author, screenwriter..." + ) + ); + } + + private async Task CreateBooksAsync() + { + //...omitted the code + } + } +} +```` + +## Testing the Author Application Service + +Finally, we can write some tests for the `IAuthorAppService`. Add a new class, named `AuthorAppService_Tests` in the `Authors` namespace (folder) of the `Acme.BookStore.Application.Tests` project: + +````csharp +using System; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Acme.BookStore.Authors +{ + public class AuthorAppService_Tests : BookStoreApplicationTestBase + { + private readonly IAuthorAppService _authorAppService; + + public AuthorAppService_Tests() + { + _authorAppService = GetRequiredService(); + } + + [Fact] + public async Task Should_Get_All_Authors_Without_Any_Filter() + { + var result = await _authorAppService.GetListAsync(new GetAuthorListDto()); + + result.TotalCount.ShouldBeGreaterThanOrEqualTo(2); + result.Items.ShouldContain(author => author.Name == "George Orwell"); + result.Items.ShouldContain(author => author.Name == "Douglas Adams"); + } + + [Fact] + public async Task Should_Get_Filtered_Authors() + { + var result = await _authorAppService.GetListAsync( + new GetAuthorListDto {Filter = "George"}); + + result.TotalCount.ShouldBeGreaterThanOrEqualTo(1); + result.Items.ShouldContain(author => author.Name == "George Orwell"); + } + + [Fact] + public async Task Should_Create_A_New_Author() + { + var authorDto = await _authorAppService.CreateAsync( + new CreateAuthorDto + { + Name = "Edward Bellamy", + BirthDate = new DateTime(1850, 05, 22), + ShortBio = "Edward Bellamy was an American author..." + } + ); + + authorDto.Id.ShouldNotBe(Guid.Empty); + authorDto.Name.ShouldBe("Edward Bellamy"); + } + + [Fact] + public async Task Should_Not_Allow_To_Create_Duplicate_Author() + { + await Assert.ThrowsAsync(async () => + { + await _authorAppService.CreateAsync( + new CreateAuthorDto + { + Name = "Douglas Adams", + BirthDate = DateTime.Now, + ShortBio = "..." + } + ); + }); + } + + //TODO: Test other methods... + } +} +```` + +Created some tests for the application service methods, which should be clear to understand. \ No newline at end of file From dbe69735c93c9c9ad9071a2df131bbcc58b86a5b Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:25:02 +0800 Subject: [PATCH 25/77] Handle the case where currentLanguage is null. --- .../LanguageSwitchViewComponent.cs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/LanguageSwitch/LanguageSwitchViewComponent.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/LanguageSwitch/LanguageSwitchViewComponent.cs index c6921d3210..3dd1ba2b9d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/LanguageSwitch/LanguageSwitchViewComponent.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/LanguageSwitch/LanguageSwitchViewComponent.cs @@ -1,7 +1,9 @@ -using System.Globalization; +using System.Globalization; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.RequestLocalization; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Localization; namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.Toolbar.LanguageSwitch @@ -23,12 +25,32 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.Toolbar CultureInfo.CurrentUICulture.Name ); + if (currentLanguage == null) + { + var abpRequestLocalizationOptionsProvider = HttpContext.RequestServices.GetRequiredService(); + var localizationOptions = await abpRequestLocalizationOptionsProvider.GetLocalizationOptionsAsync(); + if (localizationOptions.DefaultRequestCulture != null) + { + currentLanguage = new LanguageInfo( + localizationOptions.DefaultRequestCulture.Culture.Name, + localizationOptions.DefaultRequestCulture.UICulture.Name, + localizationOptions.DefaultRequestCulture.UICulture.DisplayName); + } + else + { + currentLanguage = new LanguageInfo( + CultureInfo.CurrentCulture.Name, + CultureInfo.CurrentUICulture.Name, + CultureInfo.CurrentUICulture.DisplayName); + } + } + var model = new LanguageSwitchViewComponentModel { CurrentLanguage = currentLanguage, OtherLanguages = languages.Where(l => l != currentLanguage).ToList() }; - + return View("~/Themes/Basic/Components/Toolbar/LanguageSwitch/Default.cshtml", model); } } From d3f4a3c92853cf0ada86e4d0142d8d73af19bb2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 21 Jul 2020 18:13:44 +0300 Subject: [PATCH 26/77] Create a new Database Migration section. --- docs/en/Tutorials/Part-7.md | 16 +++++++++++++++- .../images/bookstore-add-migration-authors.png | Bin 0 -> 29296 bytes 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 docs/en/Tutorials/images/bookstore-add-migration-authors.png diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md index 713399a206..31d6eb2719 100644 --- a/docs/en/Tutorials/Part-7.md +++ b/docs/en/Tutorials/Part-7.md @@ -69,17 +69,31 @@ builder.Entity(b => { b.ToTable(BookStoreConsts.DbTablePrefix + "Authors", BookStoreConsts.DbSchema); - + b.ConfigureByConvention(); b.Property(x => x.Name) .IsRequired() .HasMaxLength(AuthorConsts.MaxNameLength); + + b.HasIndex(x => x.Name); }); ```` This is just like done for the `Book` entity before, so no need to explain again. +## Create a new Database Migration + +Open the **Package Manager Console** on Visual Studio and ensure that the **Default project** is `Acme.BookStore.EntityFrameworkCore.DbMigrations` in the Package Manager Console, as shown on the picture below. Also, set the `Acme.BookStore.Web` as the startup project (right click it on the solution explorer and click to "Set as Startup Project"). + +Run the following command to create a new database migration: + +![bookstore-add-migration-authors](images/bookstore-add-migration-authors.png) + +This will create a new migration class. Then run the `Update-Database` command to create the table on the database. + +> See the [Microsoft's documentation](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) for more about the EF Core database migrations. + {{else if DB=="Mongo"}} TODO, for MongoDB diff --git a/docs/en/Tutorials/images/bookstore-add-migration-authors.png b/docs/en/Tutorials/images/bookstore-add-migration-authors.png new file mode 100644 index 0000000000000000000000000000000000000000..c24e33c6282d068556ab80f39aa8a91109c0bde5 GIT binary patch literal 29296 zcmZ7d1ymi+6E+AB7A_Fn2?V!b!QI{6-QC>@!QI{6-95Ow1ozBWI#!gJ#5dh#&|Mvp} z(lW3BfB+B|;#YFZIL&nRLR(lF%BJMxT$PMlm%a!Lkc;Cx6mBcY@yp9|s9bWOdFa%k zinUxiQLfeX6ddpuLp{nQxlIbV-59&iOwGK0uWesmC+YX&zvS3N^sQM=Wt_UmxJmc^ zm%+#=X7Ue&g#2G>#md3t1pdp#^3px_uqKLuzV^{-Q4sx?d|4(5u@fc4LzO0T`vb#8 zg#J(3lO13u$p`cnNiv38?!W{8qbIsH71Uz<@n6J(|MfIF6QkBLsvE^mI5YOT z4+;B6?y~?f`->Q4bR1;GJ5N>m8u<(VTiRLjwo`>_% z3=n|_1W;ZgxyK+SRtv~M8Zf;lB|^c?#Hk??@@q5kfxc7suL^r>9vgQP6^LTOqvY|F z$Ao+r0B1rP9JT>(j=LYodS+&x%&~~f~0Rrm$`F2G0|SBR-=jBD4tcD)>_G4jFj^< zKD>TfsmS;Q3?^lXt{|DGfy^yw4%L1TdY(S>`denHahJr_(+ z0k+Y)sbD0UBn&a~rOUEiB8kY!lrO5&?9agN6GZyqwocI!sSCpLe40JpRBL* z%1n%!G(yO`ikb3Ct&-Qzt&@qmr%$kV_&h#VGz3ksJ+L6)86reM65Q7WMX-QyotznR z7M_80n?{tDU$Jlayk4yYS9bbkEObVq_jiC;d!Qc0SNh>S6kGNmlu0@;j_J0t}k}r_8pgpdaAU@TBdsLn4JFItPdZGNM@EZDtxJ-Qg%$4E+QrJe8K> zT%-xNcXAnu_{C;migk!PN<*G6>NzVf5^KM&IZDVp za-n{yczJoPYJb8CM4h36McL{c3CIc9{gh zlIHjbfkcfqWVa4>iII)O|37v!7BNuYFLFSqMj?ZdFN9PKbb(0(38N47N8%}P8dr@n zYNrEVyF$igdoi~erkql<2Qi{CploT9?rhbe|g__nU9c~PR)E{X1%ULuW)i!7!23u&(1}j zo+%sW#EWLYJ>1cdrGK?>2Mv{^$JO=61L=VX*sF|6zBFe0Q$4|9#4=Qv=^` znVL%cCB=v$(IQy?Ff>B*_eX>dGwuBX6UWi62mtVz$VU3o#C9Hyed&8FftGH8i1~?K zv9fyW6Z-oL0Fz`s-OVODq{fC#X7$7By#Z>YjVF*@&Fh4pW39l$)rWdz&EqA_WdXDc zjqL#E7se->*u?GJW3UZ{D>@Vb@@3Hew*#3PP;O$xUe%O=zY0b`B=a%O=8g4ZIC1!2 zrSyd=`#Apy4TH1Lxa#iqKEl94)8aC3Gp9QM5DB&a(@=D*H#Ca4-9X=e0c+S!I~WWU ziU9#+hSt#)w%prW0e0(6RPdTpP6b%=8ra_@89pAg-?C_vpqLnlPj|N^$iW$K%S$b% zR@WDLVX=aYOw+P(i}u)bi1(wGhnLk<7hP%4po7FF;!%51VEBmyuOFJLD4NpbPqX3X ziqz6rDf_-77L#`>YoAIcqW`#V{1!K|i!OPS**PcMDB*GUShCui+ZQP$_L%&@R7vML z!{&0cFW#J!xj7To>LEorB#Yk2<=$hpO~@qWy!m&U8h(^eEo7a`+?mRs&%ojDTs|`c zKY&X^RnyjG($ep>^N8u+xl<)mp=Hk^GBe*S#KN^v-heRkl$p;3!W z&_Wt^5+*<(K*JAwQs`VxQmUC(^^Jf7q51S0d6T^@Yg|A{L5h6nQVRA|ifak3s>BRO zc=|3B5VD9wO~moH6*EDV9!mu87UEx-%9}L(>`Gm4a}&~$lK~?*n9^P zATg?X+o?!|qRI=+tPu%anh8FO`xG%Zkfz;D+n*B?gHrIP*UsOSD9+$1sViWZ)GR?n z!O(N#?z@&p_jw*=k7h0Y>%5HKyj z3bbiiC5wWh2F*f`>p?JzTMjT9ws%RW-w1^K|5xdWNe%N!@_t=H?tl~}6qw_)yzhhv zL6$eL{02-y`4eY{l48-cIHhEem;ZBDR3uZht%}2Moc`Aq!y-zR!~F0cc2K_-Uw)8? zNdC;2HgJUdPKcAYKB7@l50c+%{C&X;4wgGZZiLjiNVCW4QMr8Hgc!8c7O3S^O-W+5 z=k6&j$&|5QV2_{^pF^K^bE{F2WcCK(PQy1$;8CeLYW_1%+h9qYER)t1Cg*( zbCK7PmrX2WJ6A|ExG_#(Arg<(roI_0jb*;U!NN`4@d*Qtfor|Rq2qO?apB|yyHmj; z=924C#AD%l$VA9obH!$DZ3EVf=&Z%qSP{>Ul4vhH5INk^`lE8!K(SY6gh*FMv$HLA zLJIzE?-)s&85Tgwdp1idJrtW*55^-5imApFcCM&=Eg4*=;?X}Csp_7bEwnx361o8n zTSyxJ?7MX@auW@5Mpgc6p>titJG6s9l3h!9JY_o$I@4xBV>T~v>?L`yu3-P8=c@Dl zA$vJ!z!FDNTdZZ(eX48`R%SZbjJJblr^(~xrZ9qsZ10Y~4AHmhxn&R!zx>U?KCf%p zAMlfTTB!i7raqrsTRU`~?^+TkE6j)C$jI#U_4d)ATho2dS%v)g+7XmsY|)BX_AK!( zwzG(BKX(+RsoI#VXtp>UGG!`KyEvN7@r$J9)a|Y8!V*JSo}O~hwko2dYVh8NdyR$I zLdo=GLf6$Pr~el86#&KG#~3p6dA;rvhO>l*^P75Ck6OK^p)G35e!;YC!V?Low&A02&xf5J6Tf zHhZCZyTtxk@}5 z=@7%8D1|qNfUpY>ViX1ioGr}nVpRu>fC5s5+W)pB<{X3n*X|qG)~|g6eu0=Q0VG`b zwV4m(E6~+MyF1mrl|T%^r_(%7OhWO!8%Y=bkr=b*C9}9Z-z+Ak{?7UcBt(Fwpp7mM+7$8_#Wh$*c827}E=$aM&SB&mbVMzUj25 z^UJ{nv)7i6ARSn>KEFU)=b|WZu*2*X)3J!RPT={uAgbNrcm310nmys4?{I<;W|`jh zV~6iK21^p8@b%j$d*3cs78!ynEleR(Nz`SYAIl!P8-$6iwZ?dmIv=OFmPowkSn_d`fSVd9D=aPu9M6+S!}BFGr-JsN;Dh~C>(Zk zC-Zb5@+svBr=;S#uLS#e+5!LRd69vlps3!%$~!3&4YQ>Bn|p4IFF9iaxiyv*X-9pOrU_PGdnyIu1Gf5C}NaS|A7Va1vAwvfA9|?PvG-#j`*OV=HY+waD zt4BT(&9UW422<3rBd$jbw{gy^INZ>kUvh^6oF(Wak1YvPV4rOa&LpgU7B`H516!t6 zAUWQ-%^gKlSz%np5s!9q0_F!L*#HvfE{!O8A2RT5B9$@=GeD00{2x?(c>N4+6cGoC zYugho=7GbF156o#9Or~<%coo>g|CPdqI7*wpMI!mt~)=Hz#dy4j7c=o*M%4zLNren z!Y_1le2Z+PAXFttvH^L{KKH;FHvh+f#~%e$d?DfcoZ1=jcQE>o2$%=| zpc>KQ^+IUWLK5%c>|ZoKjSmir>vI4FNGi&6>3 z+jr#+TRW$PIpD}0KoAnuPnmS#U{9MwPFhly9J7V-YeT@QGf+&3w9p+A ztQ)CFQ_K$Y4(6(fdcs*Acd0!*j%@%&TV`?p9i1?X*5YhgSp8d{_)nn|)Exs)=b7*m z=69`*ASk!WW~A(;w#fm@+g8uW;~~qx7st#y&F+yHoi1HEzj@jYNl%z&R_ z87XyvLS`bemXr5gZPQ_}9S0?wf%%`($qVfI>9+7%?Fl0%x5ewi`#N4NhIDlfM)1v_2dcT${HaF`Zb(cpxFq0+$_anEcDZ?Asu15idv2?E3eT$~mKYAOL*{)Ab0 z#0zm>e|Klh|72j16KkQC+D7%yWsh|zlnv2|ij0WK4QbB6iX@>Krz2VC6ET0?-xC_X zbErmAM?!{Btt^e_;E^h+KYm zMuMDq#iA}Daj)^0Y$RM^2cmt6QrVENQ(-aL(!()7&7J&WPC@Qs%}BT-p)q458xFnx zf&C{Tdq~|exgrdCnxLLTioiv3AxhzTqJ7A78geblk*{XUdr0OTlXONc;wDNvSq?wZ zK0Bk_i>92kwgSW+Gj#lekOVXY7}u9kN1a zq*NeqaFQnhJ`t55NpCxrmJp{;=I#oJ|8J{E4CEgXrc~12P7(UqAG_-U^Fkhcm~S*X zk}!$yF4d&kPSxg96P9&89iLzF%=-kM(x10=sOz+M#9iiVtY)0)~*C{b~^Xb^dD7nkDA{b-slN2*DJ8@&`Z z1OQ|JDYiM-U>|$nLvSYLmaEv3?JB@I7rlX*WkLHWWw}s)|4<)K%003nGSpGS{ z#`sFIv;+8G-1#WRB$cTHMvUSy?M+dK^MOe=s?$e1h%YpqMj-HLqJnN(O9?(N9uwQz?saw-tT(P)lpgwJCt{!aH>mWXz1e% znPyk?n!X9lc~0%FnPro9*FW*Wv-f^r_I||DGJ~oJbRcL8JeR@t+SDD`Z@BBCrS&c8 zXYVqOZHYS&0k~F9E^zv6?|hka$kYJ`o!?Ybv8mNS-v~&$H|Y9DZqW>i-ULQD4ts=Q zMANm_1t7r?<~eYA2vyRkDqMedl@@&|U#%CJ7PS9{N3kdOJN~-z#qLHRCk6r2v*ypt zy4S)V0huN_D?WCpJA~wFfeahM?v0~cZ>!pN8Zd!T&ffvJmIxNhZ;c4S)dE&bx@s3W z-rLN7*Q{l_W8z#iK%gzV{P_FCK94}Rv7@|}-|z^FOGL~jMSuQ9Zz)Mm(XP-07zDm@ z6!$&UE^Ie3yjzcP{%$+G`uF{RyPtiZT|$xl?VI&^6hLUYQtG$c7Yh*L(&3iexX#dG zy4L*JzK1Do{2A%;!F=+${h1nm-dM$dgQKNaP5*n3S@lEb=Je^vd{Ah!#jZG1rUw?|x6~iq6 zOejKWAMS}A`To%6cA+Sk)r;EM}KTmp9pMVJvMCkFpYueM_;BTG_PGmox$pLb4H=34z<{6;P^4!|Q5>4Ab@`FSC zq(uhC!8q$W^k6{I22Xx;f9UlHZDj1SwL?1b3H_0@2)(xGUy#AOg~jv&Z&3`6L_u?c zpO2uohEsRAqnWyc&*|X~qB!UXyxkO@;BGhL74RRe&631C6Ad|R!EPv4J{P%7 z3W4{vp&I$Pb1{D#sOR>#-}aZG!&cKly^BW6E8mrJaWA;^iL0cO%*CwMK)c*md&@)E zV0>ndY!>nhac)|I2CPwt+24;N9dG!x@iz zoHfUyOMhSGdvBWF`P^ssQk=r&j;vVI+9ua9ZR);PB`f&I z*-a)TPI6(xoU=_d_TSLr$k@NeAWak#;eZS#+k?le_ru4?9--nw2*x&~BvP^j?NGk%1;11y)|>obj_gynNzC?K-O>hF$ze;(*sjQxlm4 z04YC0>Xd9K3kL`I5pb|?EL!lcjTDjxt#N2^l8YBIrGGTFR%SwE>8obf28o{7;uU40 zl4~{>KTn{fp#pZ)0^q?XR?ITF6_eMJmhS(BZALn3GKO7N^e?#o68I0}M1HEbx3LsKhHP9O$7CY-?{zFM;to^i ze`5c?*rx%*{{v=y{LOv)?)&ZcAG=yXq^Ekr`eiNJrGY4cJueVEys*5z7r)RR;@2U2 zJUl#1&aiO5ln5OSwoKaQRqxt-q^J12nG)aNe31W^!?JXd7$iS6#@K~jKQAJh@Ep_s zD3Foa@e346g=R%n2JNY(+YJm*GGvP9+$p=j-H0vcUs%IY(+~~-BLiSHSj<1c#n%8Y47=@ zdTdyXj*9AI%2#LCedEEkC1KLxf6_k2VkoG^KnK!NDet3`G?sNy1;#BJHwM655LsqD`h#vzvIl+D%J`c=u}jFBTyE;bICA zk(Ck+7LcPrn=6*BE-l4lG;wlsdwhR`R;kw6wfhU^vxF`KGRSDM^GBfeRJFC;d35}% zh|fw)^*)}9*NAxzIRQ^W2`n;Dx|yp9VH*|U1x4kj1k zso3-!Xd9niezQ&c@UBdWxQF}$!VYz060U#WTq0Cm=Tx;nth|6N37Pp&WAk_^YmQ>l z>c6v( zwV3qWDS((r|6#j?Gud%DqB8n+f~kr{OIrB8>6B$m@%(Ve(v3IwSJ-!gl;^|C*!V)% z7cv%!(ZJ+vxXkYu64+9nIvNx|W{P0mQ`KimArXw@q7hWo#Wr28bJ_1?GGpM|!h z7hyMb$1gw`czO@@t64g<{K5>#M2Cl`O@jWJ{^DjZ!d{7?T&l#7dTO=V)YOzEThWR2 z8C_u^A!d*3>AIXO3MjEV+Poix?QinjoYIkI9x(6`&O+NOo_n2bmy+{%(q5bpulp*A zk`|^`E*Be?^~+N)gR-I2)a!>n|F)CEPpAR4*BbSk^T#D^?bYjz_tM%q_omMIQgcVL zq3RFO4cqn~g7M^=m&4$5yAK`nubN>;@CO@RJGQG+?yO$@3y2%3%dh=)Q*^KO(6J3i z&F`C9J6FLQX6K2|V#JO~bnR1SRBrEq52GAD!G$KFh@DqLb>&qJe>GdBI;V^}6B4vQmAO18Wo^P7d1I5?@yP{*I^@+|rBx~IU z%b?uClDbGJ;8!)r>&=R_uqh{G7%H~9dQx3{qc#GNW1VRo&==3%k2sc?IyO1)l0`Ei zp83`z>1u-egSu!97g%y~m`bC8uJjEMg^J6a!KN$_Sa}JL$+*fFF^+~6ruYI3+*<4n zhaGf%BeC7-qisJ3^$hyXCHEO6G<*2dX*G&_2ob-+stE!>MoG*X9wF&;mD0Rlb|j{T zLgcE;>mWWX9oJX1)xCQxfS>BTd~Vw9Hf8w%^8dwmfuEXS#4z9F8bE>Wi_JLRcA@qw z6+1P4{RR1-w6t_!U~rr2(*t7EZg<~kIUJL;E$8h`+vqpl;Yq1g^*UWI|9e|OW-lS7 zr&>e(zPd(T{8qdA5F#Ro+=_~|Bd0Q@vd__{>rzwK*6zFE3d@t1muKDVAmLgQnv)VA zN&Uk~pz3U}8)ho=@e=8Jo~3$MJ$1bd=jj|iB9>SXot+&Hm8&%DTTQ+Uhc4=pnfkI7 zAoTizLiR{SO-8A_sPGpnE<^7t%?Z|?P^azM9zy%SjSkN0_S0onA2aNT zY*aLIvdzmL^k;waY|Ui}Fr+Z7{84ekzQOTM($zTD;SmZzHhbf3*HRY`OYAg&>-f2w zmd5LhU6w(k%X(jP!aKla$Z%iqp1hW6=@mF*;O;`@XE3dcwZ*>fBed zTw@)eC<~WOLcEUFyO~MWxt^!jmypV8&^wB6wg2$iKXI}2bsnyx4ltIISxUHoF;jNm z%YaE`Nw>Cxzy-mg*@#EuB_cT%mF{l_lEzD|oYXU(k4#wobx$!~4n-xWNF41si^k)0 zdAy7TBg^kb#I%_()AYnlDp1{lop@9aCnGrl{A_sdMnrjC5wX)zinKb-5^w-5=W?F6 zk?%G2JU)M4TN%#=c&pc4IIioyVMc`kfZc-k%}5$fdULqYze!C7QYgr-vRAY`i3Z#k zlesGyz%OLmm99gIrVme>l((b1D*9l;ENIQ=Bm)T?Vx`V=E)9aT637`(vA;kk{@lZe zwcZEZu7BV0PS&GwW2+wX@i#(Wf(|3^;?VA_}qUDlE%;dz;qLn$u+iA z@x1$~%&gPrTpN)*bM0m0*lUq#r%PB7UEwR+(pBcSCcH8Sh=AxW1DOQlvmov>CV+3T zgHyByiprOL<1OWJ+c9bFCpkG2Zud)UaJocO=R>?NEjU_kvbKF>!!xGle8iPF2Qaol z=lgeQ`d7~16e!n1ji$3*QEkS6_t!W4bhE`2PT!*hBLHw*NZz!yI%)Ka zax4Xa(V8PwtxUXfu*0?I8Q0*|PSjs)u}*V>s^5Sf>y37wOp=#DchuS51>^^A11`lH z5ADR`74_Ca&h8D>6~`Srxf~A7G0QXTe1<(z(ceKiFzQ|!0;U5|u=-{$8i#bk-I5H) zeb&~T+6xSHtP)hg{1PHpfBS3NCW}7BW zrg~s<&Hm>UTPwIbQ$@SuFYKnG<|k8JKDPZ>+R%g@Yh&FQZDvMRs1OEGs1UlFJiX3u zy6YcEL64=RC0Z<9DnoBan%(U>3ll_z-*!;jzP*p=d%iakLMf@!O7<^bmX}k>0xvG} z7v$QKEEdW|%g2YpxaP&Cu7sd~ivQiq!(#Jqhxy*s@7||Lm|0J@G?Av0T~ATV^yNC< zmBV4WZ`0kFQPO{xTYWxPhWB$n^oNJzM4Ra*6;pKgdo6jgdA9G{$(M9r1@ZKhmrLFY zs<)l(2bnf0%3Wz#7`-@sN1Ik}k^8kCwPg zR!0=$=Jc-fdfjcu(!P0rxXmj+BC|M%mpKlhj&(O3?Ho+Co4B<_6+^<$-1g>QoUc2( zEk@3v`trEft^2MhQA8TfExc^d+FJ9_8=e~t94oG4&cO-}<_)NlrWt{UZ&%G#cU>lW z?&zv$8Lm0ik#f_jU}LN|eZ=Dce~PW0VUi{?zc zJa~2aNFJ!`;sqK~*fzVkZzz_7BGWn!n^Lbr5@Q2U?h|#rmE9@^;R%!jNo2dPvS6NF zpec;w1Ub8OJCCr?Yr4=?kE>f>LQ{*L5jWnl4&=2P#KDHz1EqAXd-BGDgv}0vv!g00 z9?#$4i<#w19jn_ZsS=#8jamxs5}SFeOU#GhVSL|Azq_0&st)d06>P<&>Tdlp&8#sw{wDCU2m@Cark3`*RXi-?VH`oo!bGg`s92xl95PttwJ{2tE0_+EkOQ6*TYJ*@*BX%{(RSrVeR{N zH@bmNzO;6ESWbDjnZYpLPMdq4rK#&I+j6{pl*$CF;BQ5_@;buW-j2m$bevhOmAmZJ zMnz03VH{$!a<`q9U+2y$EoTqgSZBTuhmUVle+`6{5Qf{d5NZ#>C^YClOwzH~EJ=0V z#Fp-*({;y%add|!lWo+W+IS3o!?&&uA5N{m9i?Xoz$CFSKiOE~ zeJX4n%l$@gx@?_%vDs+9HV#G8&ZBYC-qc$VX1}!YkgT=ritpVU)jl{ccj@9If3Xrj ztUQBS?P({Huna>&SMdY6Xi zVnWnaYB)Eys&;bF=|ZOdR5#hjS)Vea6cEc3$vV01RucRap=uDwf1oKvWlRhMs5AH>Z+pdeGm{6 z5ZL@i)7SWr%G&MPvIef?^RV3p8$MuQ{96K~V! zoRYVc{hgk7|0lTB!ZdD&VgY`}# zT#-uU)5X7djPTaoo}g;i#c$P_ycHj$C>qQ@2D`-%T({McksQ1ifwSoLst3K= z;=Fp)pJ7km7H+pddfg2sEdSZNwzjt9;&CM6Xu{=X7C(NJeYyUkfjboZRea8(5t0xL zXkR~fVf>M}V#YLT#PFMtnu)3Z%%!=Z0q_$dk}qE{H#g6spZ^21v9L|lmLtiOgW5)cjXe;X*!iC6hjITmV+_#9Ohk`PZOUt;`Yumm*OX8&4)$BV|vf?Fkcrr^o4;fKqNBdZYyqFnT~pD z>}5&vqPohemY9M}wMwX!76GQ{mt%M}5 zF1Q1Zx^byL7daVMmz2aB5z-F-|~t5$#58Ikv9>ri*Yi=FPG(Aqjy(k3G>7W{Cxy8CUTKWbu%xAS!wvzxSt zT8Hbt0=^*p(@&CedSAqwkO2zy&nrK$$WJ|wmiOJqvNdbVPwv}o#-fw~8I!5*-D>sI z6FSleBfK z)LNh143DyW4`_US`^={YHtd#u*GgMv`I@+IbnCdifbRDoSPOB)AqR|LofrGv!`dn> zn{e%qcdOdpY)k)D(s9+lreb#eA=7GiGdKk_pzRb=6S&1S-^b=Yo9yLVX?`3><@l9G z_+>-=%3fG>>^XYF<9>o}EKQrORB05()AG)=x=TfFU7_Xu=0L~oEI3jaLx_`zYGAIE zh?K(TGxJwHs6o_e!bU=$4fP0hnG;+KS1Jz(JQ?!Kyg$I0XN!`6L+ zu^PjijMrg|2|bjMEtgqn>O14>?FPK3%)E29-%hcP?}5>rYqt0Gjl8NxSy^4d%JCtw zyHr}!-gfHX)TS2?o$Zq&zEwz;%S{}mIN&E~jBJitC9dE7?qg^l&wf=$DyxJ2v7A12 zuur3_NTL0_)u?~0%T5fFkl$J>jDq|P0RUD~5~K?5Q#KGMPWXfhqCNHJZhte7m|C7C{h_jyZ~pOU@@M zT0AD~-1_8^W~yG*)Kt~bP%+d!I5;SI9QyEcm^dGl#{RO1$xsCb>8f(3{(gJ@jPXsz ztT$g!WJ;ZZEDlgk$S{i$0~xw$WOTgO_hu;`f^t(G&$um0bl&%pnN?(cD8r>dvJ;ui zjEoWlcVvxHKN2bTMg)~AZI#?E+Uq&B5vE8P3uLfLZ8T(qMTY{x#OBp73uU5JY<}F~ z=XEyjxy@zx^1M7$^Ss8cU#t7Kmm9DiGo}m<=eCj(Bfn|}*Y&+FazefUhtwOdlcJiGtZ_fhuOgIQZ;Ve?sJ{YA zn4U6b)|$;qn@swDWdjpUKI6T5zM>WtF5!KYP4vz#j`lK6QZkBOP;6Rk&)kkAACzdS zcy$l(*8*0;_D}#45ET?R5i9}ZG#dE^o^j~koX@i0`0>Q*HXfFlwffH^;KhG4^?cd) z)<*LyH?riv%Ty#2IejXlx2p+~lgjWly=CK+v8Zjc-c;oP0#gcL7)A8)H(QHGV1Y@- zbYJR>+xdQZ!BOg4Wyd(g&UY>IPk-%Z#@Z4wDvgoYb*R=E08+djJ2*cWRNj}%FTPiv zLi)|p=yEBoU-oYv@Uonht#DIqKAOm?PUoSBd)!CJ*Wy)kIhMqO0b{ibn@Uo;JSJjV z(lF4#eVDbacg@DW$SyfGZu*|b0k0>A_GcwlVSiktG0{7SIt%6?CE)VxK9kRx*aP5G zHczL^oyq_(Iob;!ScnE_J92dYjm<=cf5CyCZ%T@D_N2PNi8Wl)&=O5A`d6g<-MxDj zffCRgejUn>p0oLm^^ygzdq&|54lJr%9=C6DVgY`$z4=&#aMAQ530jt%EKJ0(uo^&* zj%vfc870twc2Vg>oyT%sacBg_A+xtMo{deDC>|daJr;K8bra|X2gR4U;Zoysv!7mZZexk#JwEC?#D%{tw6=?dI0-)HQhJK}99 z0c|1b2aw?UedTVh_*zfMS+aWg@BM(NCc*-EL6F(!DWF?sFHHLhfBOB+ zPM?k>FhFwPk29x(Z7Rz~O)De-D*Kmit8&4k?ndY-za z`AM?_ds^8;^LHjEkn`xhAioDun0|!jfg5bDKSX+C@2guG#^WwJtUXp ztlk}`V}8mi1lb*j8z+M!a6anYua#aTZ*7S9cwhX)>;fU389S>8oZnI*w<0W42RUH#|Qh6e;#n`1}k51+2Z3j zG7>U3fAYg;hNd2~`FnZO<^2*L7nelanMx2+YdB=JTzA)p$cq5K;OgYW@RiYi$1PQA z@aH(|rkAFI!t648NO?Pww^mw5Ye(E~JD95Zp{Pubf%J1{ymh<7hD>;m*~~$<)AQnU zrHr+1`+cgwh4I&y>?{T`d%O2mnyh^{);>EM!0)`_8~@J)#}|A=2(BTBTmlFcYyAL7 z)xd%1lcZ^f%Y!-t%xWT-Bu9ETCWP$1PB*uPGwwzusj*I_T z>NzM`5+H$T66tovfx72JOqT?=dhQMBMs#bh%MFNpGU=|Tr1mTY3VdHz%br^LN;0Wg z1(kViRw?axL1fEWNr_b;C+{$13?7&TDaOeZVz=Ab7m14H@#p3;Ntl7qdKHJ7z1Cm8 z6je2mu?~Ux5rC-2^8WVtMy>C^Tvc*qE^hbsGW%FFZ4>x9c?F)avOSK5V0SlzT zcshh(w^zGE(NbUIsWjR$?DZu|uD{RQ967Xbodl$>aI&o}8G#Rhg%Re2M2St8-R)x1 zde3IeZode6NsYxi)8vHxEYp-pWO+#& zGp=mh+J@oCgZ0bRQZ^n~f73R>^_Y?l_2hLsPsbyr2K>fG0LsMsMiNqLfpfLZwPWvI z)C-FXvpxY7;=LX)C#EHq<^~9W?>Gh$o7w90F3kuGP(8e5Fm4eC?^I=k{1vO9kV8YO z+sM|r0FI#fdmRtQhOfspSix~-!(k^D`zR=G_c4NAyx664-t6U0*F9=?bc_msEMM4I zS@AXTvXRQG#S9Ol&$f@A^vIs0J&odGn7W6qom47 zIZ3+|H$kqOEIOVKzaQ8)-5!D^!dXo3u{_@&Y^&E~Ac&X)4oTR68Y@Z6@=*CWaxj3e zQmGV;;nV79T9T7;Hywm4duqQKy-pPKe%wZw?4CpGcv)?v7~-E>o%clcHI5QZK~&2( z(km@5%dLw;oS4vS`4fQr{D|119$L$Ci|SyQj~dv(*I_8fnw(Zj>a0(AsVmq%M&n6j z@NBtm#}HTV6WYOmX^-IJMJPqakjUbf6karBYzmC<1&hYaIpf+PD3kiamX?%VHXw!G zki(x(f15viph%8_OgfFt@!wZq%YA!1l~G&!w|n+4rPQCEGFE>Od0pDh@G?!(mpiWJ zqN)643)1{Pz8Q#^9IfK(JXuF33M|C7AypP&1AdO}$he~NkbFQ72nLor*aj-=;JFmb zpF|$`4qQ&#anES1PVdgJ)2i01v%9niw6%^w0e;7k8Xb+FacvJ3^__hdCncI6M?=bB zCTQBt*Z%pe-Qra6o21_w5+4c%N}|MOV%yxZ#Wz!RceeapSa9=vDC1}n&pHVocp zu#5o2CFz_sOlNk6A#g`*G@Y{|i+j=Ha~eZIm04)Es`{5HhZZG~XTSPkeC_#FlXr2^ zxhCyIU``a_*3X~d(nfUk>J?lz5Bdq}Br}N&x&;wa%tkyaGNv@sxLBhsud5AVqT}|@q4-HF0XR=V{M5EKR`KjTyb=#ClWCp>XHr^hAs0h4HL{x(|z;{`xi#x?2Z7d8WTE>Nav0?qKEzO7YsgLP- zui@|gZZk@xm6g{&EgY6ER@}~Gr>Uqgt|o2=>6~RLzi7PCAagrjRpWMU zdiL`w7QxoXd||qN9SQI<^qbft*UrRBfX^cf+G?^bJ0{L4DK=ab)I3(T&JZd9TtqxN zvK}f9m-khMA8!vsO8*icHhrYAS}Is1@KbTCz!j|9eEzJ-efm(RNp%p#(@#sX(Qy#| zJG({W(o7@VP_~y%*sfMSzGgr*+DOoT$fP`Z>c}enBcu)RFbvzZr7VH04-CHol@w{EbOG3~Dm*5iI z2^yRb+&#GK0*ku^g1b8emqmiRWpQ_RcX!)ofB&lY{toZdRE_pbPfvCC)#sc!Gw`ML z;)2}-J1%+jm>V}KDdm6j*|@NhSyVc=ptdd#!xhoDPGmxOQ5uEJ4UfG{ISGA_U0g+h z%Z2v)x7+G|(llYeY+V*aKxtwsufw}=v=^54H}o*^BE_cTQm9yi-^1jdSosETgGGus zyW`l2+W0l}yJ}6yk0GyzKVXpZ-6-Ou|mJxNb0QhApTME6VPdi>PdsQxnB{Ud|3 zE;cJ9sfbHxrwf}XgIQc(2CsiRnpnHaG)20myPYwRcDir={rmx7`JFQFBOko3JhLrb zsd54UP(WeSm-+3m{U-a<+M2$G#>M8eVvgF!!6?oTJji;g2BSkJDiANh{8ZcJ&hwu$ zzZru|e$7Zm6XN?h-=c@KS>#?#O$CsIoSIlzl(I`oi$_hRekNLaevEyEaMsW8DKk%P zGS`-ux3zR6@jd^+%A29{Xo)b==9eKVw8EC}C>O)a`HV2@Qct3$Uj5BfB>)sxLd8kZ zH=(aJH3SD>yGuA1dPWv>?0)r2D@E*LQ&&s$2m`_T~r?^They|DL&}-ObmG%{Q+8?`)^0jiyXs-r+73=U z6YTytGHUIU83+2Nq&#bvZ<_x1H}AH8NPbE19v{jy$}jo32??i(HSHz3m>dq8QGNA& zn`0&+t(>pM#%%?Kf0{r2Ikr^xk&|#OKSzieb-kljAI*SBAFUdL99I&!=qtlWHrdT(v5C=^HR{<7w~83KcoSKV&K?z`rNJC&X9 zYtf;MOXAAK3=xcZSSy)b%5za<6%((oRf+-$)(vu$bD|_x_gqixhCn`(F+bzn#Xi( z7GPkACr1i4I&tL}#d!CNoSYm+uyYg1PgiW`{ewVuuePo0i=1Q!^&-rO&5gR^i}g8| zGes~Ol9v)PI)_Gv)RzH(8|r>xXfnUs(H9W<6u+gRZ9SuKI{?76U6(pqQQ=!J{;X*i zlXlxP{Qg!!V%`3rr`Iq88&Fb=Otx6c5NJ7hbzjdlDR)d z{isM1OSR^Hz1SVZdasm1MsX{CnBzO1!GBoo7W_H$<3!OA?yzIm;lRqvt>m8gc*l)r z)EtZ9$TD|jnbnL&7M~j5U7E$32dx*hZ2d^ANG``iE+@^v2{H&>zNExb{T)0NVErjK z{K|#oWJg_BaJpIcL9xcsI1{Q&3OqySlio?gX8trI;~2O(C!F z>wItoa}2XJ_q*z3XD{@7!6a}xwGuvTev`jjcD-)!l)pnauDukUw@{{&v?LWj$)?x1 zOF)iDTs)O}qE2kT)!dfzdEC{S=%EU|S*5JK1Hi}7;zaZU%Hr;$8+QwGMyfRUkNnm% z1YEiUj&d~dj&z@*a?Vzr9S<*662ca5m%9>Ra(N$Lg=mQMmPealUIPDFNpB zy`sz4j$;SYXJkxtP=?Ne1n8(Ub5PXI>lb6)h8-={;wRzF;0t}d_K@8dCdDU5(C}8jPpmNS={tP&1WGdekCz;Bn$2vxr_JFtSl>8 z7Q4+&pX8AEeGg(GjGMm>Ax;fG&G!MA0RNr3N~@3Fy?^S$AX}>qK9`T*RP_1#%9nt@PTWiMI8qbVryk?$bArYdTdKy8 zF}fy18Fdv!$ZMoVMvv%kHa+j>eS~#^+hIbZz;6E1B;47altC*)Ea>!;6&Ju(uSHhT z()L3wrmi?g&T>J8>AGuLASU_OPcG+`9id;2084t_o6}@D z<*M9kFDGu$vJMCU`0RR)_4c^x<;Y+OP#-r^#VR%6A)86fq*COyAIjSSz}{Nv%`+F~A#yRUGd833qne&#u~ z%+gd(=d-Xg5Vks9tN`EPzUz%3;PR5I;|FkNu6J0Ui>Je7NAwR!bEIB_`GnG>EB`xpko$Ok`YcQ%UBbFv;Wnqe$1j zW3%;bJ-A}BZrYj)@k3CW98JPuumcA>``UPw>U6NP*@vRmI_}qX>PSxy523e*{P~xw zmq*mjmzp=TrmghHt$OcNJ?}aMXoZ7*;;*^(+ zRc$3|4E5TlA!NSgla^BP@xywYgr_+I9#DSBL0)gfQ%qVxjhgL}o8#`#Y-78#z3u0A z^(+65@lFAke(*U7uxZxAVuEB7?|KM$kL&$BRMqTe`T^ege%1K1UYonw$2lg6oUwWp{mEL6Q0)6*l7hqhBruNRuvMIf;l(BatYfP+ai5*F`xP#*jU z<|6aXX4BFqff(?j@l?Y#Sq6I#IocdDO7G%A?NgcVDt$t+;T0*dQu9R)?t7>w^o6u&1v8GROqEit~Y76C_ZK0+2wp%L4`|2wASc3_>O zoNV+)^2Gx4e!jNbh3|1Vka)y2ihPLTj98Gs3II*VysPh?2&)$8{IY9@DJ zuWS2lEq#!pVT_?c_G(_^+FWTFax}Qu?{5y#!f0~mdAxGL5G&PyW^d{(UjJ&gcgW7= zE96Unw?I?*=)VC^iQs4VvY)E?hgAlM9e$QEB4j}j%R1i5WylYVnS2ZXl$D}s9q87T z<;tJX5)oESU~Ql0_J_rDn&<^CDuEF(x%7_`7WYX~RJR|`362UA7e@IyBrV4c{gUJi zRT?)?Okht_>5OWWh$a3hlhb4>G|_kPf0K0BWwp@Pn&#ZK6GJ$WGL$sO@fD-jE%>>j zBLosu02NR9`plxL#0^?9mqyYdXj_S{ay=6f?;}wx@#dFOMi!notrCn+na?}@tn0;P z^g{Yd4((EO1g`6-Ic;o;KNG#$>Yny4f6RJa<{bt;QzO+s;vUVNYbopulsfgtgrce@ z?cnI}-yP3hcc-lRT2KBkXxVaJqkh&q_a4TkfBq;>L`*i3XyBOtp|?r@a=i2UEdM@I z^(RX7&dkc}6&%v4H`(LGvc+DcdB)hTwMnP;TX*K=dqT9T<_Ygv^GYMJ{dEo_LzMuO z$Rw^&zw~1tQiG>;i@m!l0oT00k1Y{*Pn{2s`!Nc#ZSEUV&1Y4Dmd%|nka0!1f06{c<_jhyQv)b*>`H(#x47$%*{deQNV?_sa4; z_P>@n&woPngQJKctG4gf{U)Aj{S}O=Fu(trr2hOyDc+N~=p^{?1F7n!N7Bm{P#|F3 zck*bZe75&gjtzEo_P#|a90CZVSEnAIS8TZSQ__D`k9mv{AS@8}@!hU`BC>&~dm?%^ zqx=w97rAfM=7njb`3M3h#{gz7+9XTbweS76AH?(oXdSL1KTL0b6EV0dc0!F{(0=m> zJd&!`xq9%pFA8!(ZkTBI@Ehx6(RjmpGZG{7G&Q>Ju8JlWnBS%H()^jl7ZHfbPf|Bp zS;;j8P>|G2ZkI%&xH-2UW@KY;;L!1Xah*mEks@9BVm&hQ;eC9uQ1#!9dg!tQKokIM z6q8#2LAIM+M3^-Joai#l9W*YcL!0a{&tjk|jA`7wC~fOpqmz?*zKw{V-1E$W$m0^@ zlg4G|IIJQ(@c$x5HneIDL<%D&u2@MarSUe&_j;6JHfI!dtQ6=R#l`o1@Lp^tSKbVh z({uBtO`wwO+JAiLUu^6ZlC|pWwnXL&$v&vJ1&6zInxi`sQ+)^V=9=B-ht4b{433Y%Eln5qf2Bitv0Mh=XFD8w(}1z0Y5 z^p&PXb!<4d>y~UZ{#0N*S?C7Lk@CodgYe^E=p-#MV}BZyvlfzrDqc%&rmXFp_hb;C0)OG`FCG^q%X`jfbOm+G?6AeVHy z8~2OP8O?FPcg=>#D0w%7Bl~kJnTTYUZqIgm za7?$51>l4i8dKzm#R0l%B1di5J~MNo0tzBzsCQyzk}_qns5Jjv+}Ipvc7K(y&7VXA zbYTuBs$jw?rF%5t{A^uB@e@%I2$Q4PaX@Wy zKETW>L>$b;%fFfs-@xHn5oP4AkP&4jF9F8>?K-W_mH(T8n|no8EvNmlj@-!Fcp?_5 zQLI-pL^Y80)PXFMVTR#}O_@46Ca~Mra=$@*q{YqCU2-0uPYok9xwJ zp7rrYj{h&o1uvE8-|}!pmv2}tK_Aa{$5_W#GQ^`YNl7r0Hk|fEK22-zWk=|anb7zgWI%R=cW%hK8;KJQUPV z@yqOl#wTLZvp0#n>0O*0!=4f>kK1)bw+OZE+icRM-|4s5_@dAruu4-E~HITrDY(%kq;1<3-i`keL_H9T=*ig+S2ix($oF?RfO|D`EZO&AF@Be38ZR zam3LlSL&GlZ8&KqDrDvq1#2ADcnRuZN0w;mgT>Soz41>UkEu6NU?#rNL${1`?gD&% zBLSK#f||_F)?(ESVEO1DB9>LDO6q!h(LRhQ&*+~qlXQBWZlD zB8iFP)IXP`YjwB6)1|}7a5!x4+NVQkZxgS0yQa_x0es67X0Q@4Ve2G6o!KuWfyW2EB(sNg1bL+%4N# zt|Tcb9vMvv5Ydz=l#uJ&}Lc02Bz*U%{rous~&2lNQQ?UUAe2sIk?C`Lexsn6; z|3SWI_A^e7oqjqS8;N3Ne%zQ)4;fOA%H(pp`mN0V#=!CcEu!N^_i0F-@h-d66d*!) z`7qUp7=-SYTDi*}msm`zWh+Qj5bn$FyoGcTIm;?E(g z@^(kmp+b;u0MU8}Er%0WnYRq&eiHgN#oSDy8G$pjjSy`h;HPc#x7c6ys{*3DeV6m2 zwTiL&zdsK5tm%`(iu4t-ds+Ldz@w=?9+HrGKlC;)HSn)flN-%tE^Y@?dp3ni{rPls#NpNG z2faQrmIo%Q3H-fjJ*4O}8+Ls_C*&8P9}yQ>j2eBO^S+v84I%od0End%>l?`u_F5q~ z&}5eei19~71?9Y2bSDQiyi3gh!bn&9FEJl^+g zFFQ`ko2=e-8G4ozkFmF=tRvC%0p0Iyw%BAPm(FSY@PVQM>kd7nD?(cD;wJu* z(st|$okx$eDLRu6=$`2&do~u+v7D&7sIL@&&E>9w6Ed%xS?#DQK2BlfV=OV2lWlOZ z#yZM0AOEJ_fMsD-dQ)vjO761olr!W7(NrRba1KmY=t^Y zbG$%QdNO=Cq&rN}Yo`}clI|dQdRfzAKK1oWJOZtxEI;=j_%IVvZ{I1H6y26gY-d`jY%v9cVYZwZ2>A%ebmP+q$?f5tz3rMI%TM= zjRDwa%7J(inDRlinlIV}Jf12xLpwnns4`Gc2B|+dycv3EQB3t%#&|Uc5-@T^CW_-T zyoh}M@!R1rCBu5@SW!A|{3PTI8#@>k(U+MGED?PmY}}U@e{wZzA)`f(z~p%qM;}%c zo++<0K63!mTCf}G<9LZP*EUxgZ#U-Dzvl0B%|qS(Fw!E!##NoYwn+H`xj#x(q#smR zp?|DmR>&w_?D*@R^YGpU8N(WlSr-d5GHEX=+dU>tWX?(fXB9Rtra@ef>mFO?*>Y9Y ztk=Vc@nmKXSLLOhd*REcqX@HQ1-&Cv;zHgb3dk4nyWnyNJ~5NekG;g;yuIW2u!U1BF1#jnw zIPzIKC4s0-{$7tfOeRx6W;8rNlK>iW*Mdv~>42hsrt`XuMEU4)oYpqU2_ReQ2t^4w zeW|U2=nB?QwOB80qyo`@jmr}9^vIe%Zr0;>3eUX*^m810Yjn&H59{b@Os`Bz$zu>_ zcu1gkT3PPVEvA%`+jE(S0RM_^vR!RIQ>$SPI+;gP5;?s+Kj|zx zT7UMhC*mIQ+{;3gq>?B3cJuv_n;90TKdz*!F6($#JGW$?B;|4w(Vtu1=j{XzVMcbqud-Sivpn3o|G1$y zbFiCpyS{C}5Q1FbisR?Phjz(vUk+cUiywB`Yj!FF>yOhzS&Oa!a%pnF&*h+(7sBbL zRDb^`Cvc1=I|P6fzlE)NyHk4v^nHHvahc9%Wg>lBaKgIEM|aI+Iv?@=o#+OIH;60T zo0zml01!IG%zj3A{@o|(RVk>9{s$=r^{$!=-$uy(?CDFTdYpskS#oe~C)rT@<^%mN zhTH6Z+S|($I`(7EbKT1^5G@Rw47Vf_Q^|5@^0e#B?v|UpD`%)5Bl0JgDoe=ULk0wp z^AR2(!LMETXnO|HPV|Aobs*h=k`hNPxnkngU0Ly1}RdIG_RiiAUAF*-_ z$GUKK0XO$yXlf|KcclDT>vQ#!xgaTFPv@l#(qFk}Uy*ao-~i>LkBl1SOx$pQpOM`4 zeKPKsyzk(;PLcEVFW>u6{ZQx*L*buLa9T`5o!c=|#`IZn#hYIg#arf4>$VbVH!)DS zb}?Xyff;Jk5~72I+%KP2+2BR#HyJATZYCp_?xw96g;k44ardUbXrqV|Iw;9B5jDOo zyan1y*B$d#wtI=obURWN|1&iAXC9%eAS5E;qFV&r9%h70Dww2SP5Eh8i%Y%-d@gs< z&X|ts$3^;OYSxW8`A)*^WT2bS7HEOlP|f>nso`+dGl8U8RiQLFci2_B+Ub2-x;1BQ zE87QJ7tAP~(ZH!@0vud1a+*Ni(+IqB)}P+5-<#{7F^0onC70d`bVJ8Eyv$`6f%U$x z3K^xmJm7+&AaghXZ>0GL^ymeKZW>lTqDCilIIrBpW&lMVGVRZ%hCOP2dpH6>5MIj?zivvk~{ zPkK<4=pavxi*OyqAI3LIZ87aO6T$!FSQFV^P*e{B4;U0#j>g`m)ZCG=Udv*he5>i~ z?)j@gD3z_0`>x5C?MOt2S(5vWo55!Dg-4UdG^eUD+_SIeG0cU((?GARy-dwDVs%oe zXV`9HFH>OY4S$NG2DCAD*uxxmikmK}x z6M~D}4G$1m{`jegpL;U)&;AYjTZg z7@e)|$=KwRtoYN1P&zb`V|8<@gtx2Pxg-Mt?{Cb@jqOnP+0we^X<8=F!`cvi*An26 z0?Asf@nef{&K^DtSZwL5P(1yisW-B6`Mf5qkT-^FkF-=~mG_Iw)(0B)J_iFYAKAZx zivT$uD^_!c04veJdB{gmD1ne^eP;0hF zL4G=W8cg0sh&W1xyQG54nYm3~(+g#Nw=wY&;y#3}8vfBjk5+P?&L?lqOMMGWDu7k3 z2=AoU#^1IKyd6f@WmyD;6@qYf;m1dUhi}DGPfeNYuiG)lu1TCaNa6guqdBBf9SZGq zaPXHQ)h|5VHFdo|YBzF`obwg9>cu}#^&3#&#|KAnxlZe)mWA6#9WM{7%3T!Z;|X%- zRlLB53{&(9O5R=xYfsorq@Th|?$J=tzsqC-P^i$EQId)*pBBugl2Fa}ioLV7@W%kF zsdVo`KhQ+=4t|ZI?a+RBN(PT(9RE#+`C9^=|0ecK=lDBS5MAJ&vAnP_fsaYP{wXg0 z+4U*2?IKOu7E#om7jV{&fM(z6{-# z5z%j&NhYQjZ_2fU0&=>zMC*tj1#E5SCH*;C@f({fuhTW> zzKdEGho++_1fTKNAO>@~_MZD4s_D_^!ded_1dz5otoA~k;IH#2Ynq}|F zjcHf-&D=p~lXGa&JhRr$4N~J1bcJmtOFxv_dgxJUK1CjcvhqL@P^ke-kQzYbs&QQ- zDGiK-grh@?@w?W$cu`FVkX}6;*Y~Z=b2|6@6!rs`;7H*(ys_%}TkUT+a#PVT!4>iO z9vnmyHoni8i1Aze{ERqIyOLVaNxlw5?ek_DU5@3nutHw4&H*bj$#! z#3Hc7a1E`9=JQ#AY*WPd1918?9oE5^EeLXcHV$=FbtY%gpNv!UbU;++gmXJjiJ{;}Z7%9K zowi1}HcbX*Z^RoN2s%6W; z==!c3H~5xxi=HN$bn)VK=u2{RmB~;)tB{?H>GPz6S^!4SNaY4(MlS{Km)RB6;u;VkqM4` z<@?jkS?&&?#1p|$O67>`n@!^CP65zSF}sTMZ@E@}qvNZw!Rt;|jd^WD!{59~8=JDS za$s2%sAla?@1u-HMn~o11g(H&U%W*jLCQPN`wrn2P<(fQOY%>=fl`TX;a zb(8Tp$EYB)@<&>9y(u`re6ggQ^Iv{xf63oOAAW-sR~D+@CA%i>n&D^=l{t>vtoyxH z5BlWzKoel!JMD2P4MHSoBoVDq(CpSdM%?#-{~peK+r1?HOFjD=$ovHLryc4GJEFu8 zB%beOP8sz)$_^$bUpx{_5Za#R{9G;6deKNo$mrM)M)U5fEs`Ui6>-K$r@!1*xP5#c z=b?>~iMZNoIAF3nou62Vt#&vMlI-j4xEvw;mGNj|YJ(2^Vt*s<=SteKkvGvds<2bqYMNh5vR(JnI4`h$ z3O?bmnB}oOa65`7T@rGZ8c&Qv6}pQSvcG6rq!zF}xiEA)i{t9dX_{ClH3Z9foS%BU zB)0@)I&pc1L>TN$%yhH--W}KW$swLIrdZS)KgZaVI zc6?#nQ#N1>==so}<{jI~-I!KAhPjY`izybTyC~mtIMM)Fq70ci@5jq={u&Tp>~LP?^38U-fB#+&S~}i0_>;ZlsanZZVCMv$`IY z73od{g-cx}e|ALI2>7ivyzeSb!n{JLV!UdE!KL&okC5@5KOd$&>uu$ht$kl^_$N)A z5Q-Ec2xNHFckCX`1BV|Zr(gKEEf<@;>A?m~hFkt{*q((7l%JO-RWx0V72`$~-usK| zv~EU#)D>9>c}=a=7*=)TT3?@c@7O`CAJ0ZaV>@2s19nWE<}Oi#8Nn)PLwQ^A+OHFT zQuRB0_Z3q>I`^tJ6A71>R*JMW=JLB8^1#EcxG!G%^{jdxZ|NY-W?>JIyUJ26{dO*_ zglZ3Yv=48;YG~STBtWs#_eZ6}2>w!0BpGHdJ;k?o+C1+)lN^oReuOI)E4YvijUwT6 zB!+{*u6ibmYxQ6aJB4#oHxLX6E5ZFg3D0niwac?L54_~r-uBrL5BK~$xa#`D&JocG z1q~Nh3SpFHA$|7Bmx1ZtDK(u!?i#?mw2+Je<@pj9S0hJd03aUg3E>eH*-Ou(`RyoK zyd%QRKV`&fN<)K-_ZNl~ZuXfBT#8}lU0;heP1&zD*A(dtXc0pz^#iS!X?1k3#@P5XuY27*Dd4j@$t&IrlSm3s%|YT)FwG+TRf6w>o5U7F~4!x&6? z^lYtX{oAuJ^0e1SlV8#Pl(Ny(;?+DTUg1>&vPp)S!h1N&O9%aWye5|j#Ub^zV{!HI zF>8Cp0nbZn=frf0VRdnH91TB?e5Yxk#=-^ z#jJ7}AhhL~RaYk<5eYNxSj$cn*HK-c(YBT$zq-5Jo$hNnT56CLjaAW9bSwNV8`t%f zyS5{@dT_-dvg`5!-5ytDyHFpDJ*7FNHcWrKU?xbn|*?)9d%O2i8 zx)%9>qV}4npv#sKP(O>-+NEq&_Lnv2i#*xwpeC8eUcsKCe$;>b{|&k;V#c8|?I92b z+BLx!vk<3r3g`drRe1VQG?K-|{}UmImV>|574yiqU}ajkYxuU0ah4+(=0aD`tY`Bc z*46)hOEDU?yw7)M*(UXN)wVV+X$*i+AR7|&l>$pfgsPu2s^(J~R)Vw=CTuBkt}cM% zOrPDi!?H7XbqreX+kJQi1z`D@frDup-VG)^d Date: Tue, 21 Jul 2020 18:34:08 +0300 Subject: [PATCH 27/77] Add new part: Web Application Development Tutorial - Part 9: Authors: User Interface --- docs/en/Tutorials/Part-1.md | 1 + docs/en/Tutorials/Part-2.md | 1 + docs/en/Tutorials/Part-3.md | 1 + docs/en/Tutorials/Part-4.md | 1 + docs/en/Tutorials/Part-5.md | 1 + docs/en/Tutorials/Part-6.md | 1 + docs/en/Tutorials/Part-7.md | 1 + docs/en/Tutorials/Part-8.md | 7 ++++- docs/en/Tutorials/Part-9.md | 56 +++++++++++++++++++++++++++++++++++++ 9 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 docs/en/Tutorials/Part-9.md diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index 50d82e4df9..3028e88931 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -40,6 +40,7 @@ This tutorial is organized as the following parts; - [Part 6: Authors: Domain layer](Part-6.md) - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) +- [Part 9: Authors: User Interface](Part-9.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index ec86bd1ac3..d6ae9e33c9 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -40,6 +40,7 @@ This tutorial is organized as the following parts; - [Part 6: Authors: Domain layer](Part-6.md) - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) +- [Part 9: Authors: User Interface](Part-9.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index 16edf047e9..d3dcc4ab2a 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -40,6 +40,7 @@ This tutorial is organized as the following parts; - [Part 6: Authors: Domain layer](Part-6.md) - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) +- [Part 9: Authors: User Interface](Part-9.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-4.md b/docs/en/Tutorials/Part-4.md index 4cf2628c58..7c20975005 100644 --- a/docs/en/Tutorials/Part-4.md +++ b/docs/en/Tutorials/Part-4.md @@ -40,6 +40,7 @@ This tutorial is organized as the following parts; - [Part 6: Authors: Domain layer](Part-6.md) - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) +- [Part 9: Authors: User Interface](Part-9.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-5.md b/docs/en/Tutorials/Part-5.md index 4094640cc2..d18e22d7d8 100644 --- a/docs/en/Tutorials/Part-5.md +++ b/docs/en/Tutorials/Part-5.md @@ -40,6 +40,7 @@ This tutorial is organized as the following parts; - [Part 6: Authors: Domain layer](Part-6.md) - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) +- [Part 9: Authors: User Interface](Part-9.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index ec8815cc29..96c94a2f2e 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -40,6 +40,7 @@ This tutorial is organized as the following parts; - **Part 6: Authors: Domain layer (this part)** - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) +- [Part 9: Authors: User Interface](Part-9.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md index 31d6eb2719..95bdf553b4 100644 --- a/docs/en/Tutorials/Part-7.md +++ b/docs/en/Tutorials/Part-7.md @@ -40,6 +40,7 @@ This tutorial is organized as the following parts; - [Part 6: Authors: Domain layer](Part-6.md) - **Part 7: Authors: Database Integration (this part)** - [Part 8: Authors: Application Layer](Part-8.md) +- [Part 9: Authors: User Interface](Part-9.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-8.md b/docs/en/Tutorials/Part-8.md index 52505752d5..59ec6d8b3d 100644 --- a/docs/en/Tutorials/Part-8.md +++ b/docs/en/Tutorials/Part-8.md @@ -40,6 +40,7 @@ This tutorial is organized as the following parts; - [Part 6: Authors: Domain layer](Part-6.md) - [Part 7: Authors: Database Integration](Part-7.md) - **Part 8: Author: Application Layer (this part)** +- [Part 9: Authors: User Interface](Part-9.md) ### Download the Source Code @@ -552,4 +553,8 @@ namespace Acme.BookStore.Authors } ```` -Created some tests for the application service methods, which should be clear to understand. \ No newline at end of file +Created some tests for the application service methods, which should be clear to understand. + +## The Next Part + +See the [next part](part-9.md) of this tutorial. \ No newline at end of file diff --git a/docs/en/Tutorials/Part-9.md b/docs/en/Tutorials/Part-9.md new file mode 100644 index 0000000000..9517c9f832 --- /dev/null +++ b/docs/en/Tutorials/Part-9.md @@ -0,0 +1,56 @@ +# Web Application Development Tutorial - Part 9: Authors: User Interface +````json +//[doc-params] +{ + "UI": ["MVC","NG"], + "DB": ["EF","Mongo"] +} +```` +{{ +if UI == "MVC" + UI_Text="mvc" +else if UI == "NG" + UI_Text="angular" +else + UI_Text="?" +end +if DB == "EF" + DB_Text="Entity Framework Core" +else if DB == "Mongo" + DB_Text="MongoDB" +else + DB_Text="?" +end +}} + +## About This Tutorial + +In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies: + +* **{{DB_Text}}** as the ORM provider. +* **{{UI_Value}}** as the UI Framework. + +This tutorial is organized as the following parts; + +- [Part 1: Creating the server side](Part-1.md) +- [Part 2: The book list page](Part-2.md) +- [Part 3: Creating, updating and deleting books](Part-3.md) +- [Part 4: Integration tests](Part-4.md) +- [Part 5: Authorization](Part-5.md) +- [Part 6: Authors: Domain layer](Part-6.md) +- [Part 7: Authors: Database Integration](Part-7.md) +- [Part 8: Authors: Application Layer](Part-8.md) +- **Part 9: Authors: User Interface (this part)** + +### Download the Source Code + +This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded: + +* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +## Introduction + +This part explains how to create a CRUD page for the `Author` entity introduced in previous parts. + +TODO... \ No newline at end of file From 56a653db10cafa8eaa9d35f5c8484617ca57ca68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 21 Jul 2020 20:13:10 +0300 Subject: [PATCH 28/77] Completed the part 9 --- docs/en/Tutorials/Part-9.md | 457 +++++++++++++++++- .../images/bookstore-author-permissions.png | Bin 0 -> 77873 bytes .../images/bookstore-authors-page.png | Bin 0 -> 66247 bytes .../images/bookstore-new-author-modal.png | Bin 0 -> 63907 bytes 4 files changed, 456 insertions(+), 1 deletion(-) create mode 100644 docs/en/Tutorials/images/bookstore-author-permissions.png create mode 100644 docs/en/Tutorials/images/bookstore-authors-page.png create mode 100644 docs/en/Tutorials/images/bookstore-new-author-modal.png diff --git a/docs/en/Tutorials/Part-9.md b/docs/en/Tutorials/Part-9.md index 9517c9f832..7c6db34bb9 100644 --- a/docs/en/Tutorials/Part-9.md +++ b/docs/en/Tutorials/Part-9.md @@ -53,4 +53,459 @@ This tutorials has multiple versions based on your **UI** and **Database** prefe This part explains how to create a CRUD page for the `Author` entity introduced in previous parts. -TODO... \ No newline at end of file +{{if UI == "MVC"}} + +## The Book List Page + +Create a new razor page, `Index.cshtml` under the `Pages/Authors` folder of the `Acme.BookStore.Web` project and change the content as given below. + +### Index.cshtml + +````html +@page +@using Acme.BookStore.Localization +@using Acme.BookStore.Permissions +@using Acme.BookStore.Web.Pages.Authors +@using Microsoft.AspNetCore.Authorization +@using Microsoft.Extensions.Localization +@inject IStringLocalizer L +@inject IAuthorizationService AuthorizationService +@model IndexModel + +@section scripts +{ + +} + + + + + + @L["Authors"] + + + @if (await AuthorizationService + .IsGrantedAsync(BookStorePermissions.Authors.Create)) + { + + } + + + + + + + +```` + +This is a simple page similar to the Books page we had created before. It imports a JavaScript file which will be introduced below. + +### IndexModel.cshtml.cs + +````csharp +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Acme.BookStore.Web.Pages.Authors +{ + public class IndexModel : PageModel + { + public void OnGet() + { + + } + } +} +```` + +### Index.js + +````js +$(function () { + var l = abp.localization.getResource('BookStore'); + var createModal = new abp.ModalManager(abp.appPath + 'Authors/CreateModal'); + var editModal = new abp.ModalManager(abp.appPath + 'Authors/EditModal'); + + var dataTable = $('#AuthorsTable').DataTable( + abp.libs.datatables.normalizeConfiguration({ + serverSide: true, + paging: true, + order: [[1, "asc"]], + searching: false, + scrollX: true, + ajax: abp.libs.datatables.createAjax(acme.bookStore.authors.author.getList), + columnDefs: [ + { + title: l('Actions'), + rowAction: { + items: + [ + { + text: l('Edit'), + visible: + abp.auth.isGranted('BookStore.Authors.Edit'), + action: function (data) { + editModal.open({ id: data.record.id }); + } + }, + { + text: l('Delete'), + visible: + abp.auth.isGranted('BookStore.Authors.Delete'), + confirmMessage: function (data) { + return l( + 'AuthorDeletionConfirmationMessage', + data.record.name + ); + }, + action: function (data) { + acme.bookStore.authors.author + .delete(data.record.id) + .then(function() { + abp.notify.info( + l('SuccessfullyDeleted') + ); + dataTable.ajax.reload(); + }); + } + } + ] + } + }, + { + title: l('Name'), + data: "name" + }, + { + title: l('BirthDate'), + data: "birthDate", + render: function (data) { + return luxon + .DateTime + .fromISO(data, { + locale: abp.localization.currentCulture.name + }).toLocaleString(); + } + } + ] + }) + ); + + createModal.onResult(function () { + dataTable.ajax.reload(); + }); + + editModal.onResult(function () { + dataTable.ajax.reload(); + }); + + $('#NewAuthorButton').click(function (e) { + e.preventDefault(); + createModal.open(); + }); +}); +```` + +Briefly, this JavaScript page; + +* Creates a Data table with `Actions`, `Name` and `BirthDate` columns. + * `Actions` column is used to add *Edit* and *Delete* actions. + * `BirthDate` provides a `render` function to format the `DateTime` value using the [luxon](https://moment.github.io/luxon/) library. +* Uses the `abp.ModalManager` to open *Create* and *Edit* modal forms. + +This code is very similar to the Books page created before, so we will not explain it more. + +### Localizations + +This page uses some localization keys we need to declare. Open the `en.json` file under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project and add the following entries: + +````json +"Menu:Authors": "Authors", +"Authors": "Authors", +"AuthorDeletionConfirmationMessage": "Are you sure to delete the author '{0}'?", +"BirthDate": "Birth date", +"NewAuthor": "New author" +```` + +Notice that we've added more keys. They will be used in the next sections. + +### Add to the Main Menu + +Open the `BookStoreMenuContributor.cs` in the `Menus` folder of the `Acme.BookStore.Web` project and add the following code in the end of the `ConfigureMainMenuAsync` method: + +````csharp +if (await context.IsGrantedAsync(BookStorePermissions.Authors.Default)) +{ + bookStoreMenu.AddItem(new ApplicationMenuItem( + "BooksStore.Authors", + l["Menu:Authors"], + url: "/Authors" + )); +} +```` + +### Run the Application + +Run and login to the application. **You can not see the menu item since you don't have permission yet.** Go to the `Identity/Roles` page, click to the *Actions* button and select the *Permissions* action for the **admin role**: + +![bookstore-author-permissions](images/bookstore-author-permissions.png) + +As you see, the admin role has no *Author Management* permissions yet. Click to the checkboxes and save the modal to grant the necessary permissions. You will see the *Authors* menu item under the *Book Store* in the main menu, after **refreshing the page**: + +![bookstore-authors-page](images/bookstore-authors-page.png) + +The page is fully working except *New author* and *Actions/Edit* since we haven't implemented them yet. + +## Create Modal + +Create a new razor page, `CreateModal.cshtml` under the `Pages/Authors` folder of the `Acme.BookStore.Web` project and change the content as given below. + +### CreateModal.cshtml + +```html +@page +@using Acme.BookStore.Localization +@using Acme.BookStore.Web.Pages.Authors +@using Microsoft.Extensions.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model CreateModalModel +@inject IStringLocalizer L +@{ + Layout = null; +} +

+ + + + + + + + + +
+``` + +We had used [dynamic forms](../UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md) of the ABP Framework for the books page before. We could use the same approach here, but we wanted to show how to do it manually. Actually, not so manually, because we've used `abp-input` tag helper in this case to simplify creating the form elements. + +You can definitely use the standard Bootstrap HTML structure, but it requires to write a lot of code. `abp-input` automatically adds validation, localization and other standard elements based on the data type. + +### CreateModal.cshtml.cs + +```csharp +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Acme.BookStore.Authors; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +namespace Acme.BookStore.Web.Pages.Authors +{ + public class CreateModalModel : BookStorePageModel + { + [BindProperty] + public CreateAuthorViewModel Author { get; set; } + + private readonly IAuthorAppService _authorAppService; + + public CreateModalModel(IAuthorAppService authorAppService) + { + _authorAppService = authorAppService; + } + + public void OnGet() + { + Author = new CreateAuthorViewModel(); + } + + public async Task OnPostAsync() + { + var dto = ObjectMapper.Map(Author); + await _authorAppService.CreateAsync(dto); + return NoContent(); + } + + public class CreateAuthorViewModel + { + [Required] + [StringLength(AuthorConsts.MaxNameLength)] + public string Name { get; set; } + + [Required] + [DataType(DataType.Date)] + public DateTime BirthDate { get; set; } + + [TextArea] + public string ShortBio { get; set; } + } + } +} +``` + +This page model class simply injects and uses the `IAuthorAppService` to create a new author. The main difference between the book creation model class is that this one is declaring a new class, `CreateAuthorViewModel`, for the view model instead of re-using the `CreateAuthorDto`. + +The main reason of this decision was to show you how to use a different model class inside the page. But there is one more benefit: We added two attributes to the class members, which were not present in the `CreateAuthorDto`: + +* Added `[DataType(DataType.Date)]` attribute to the `BirthDate` which shows a date picker on the UI for this property. +* Added `[TextArea]` attribute to the `ShortBio` which shows a multi-line text area instead of a standard textbox. + +In this way, you can specialize the view model class based on your UI requirements without touching to the DTO. As a result of this decision, we have used `ObjectMapper` to map `CreateAuthorViewModel` to `CreateAuthorDto`. To be able to do that, you need to add a new mapping code to the `BookStoreWebAutoMapperProfile` constructor: + +````csharp +using Acme.BookStore.Authors; // ADDED NAMESPACE IMPORT +using Acme.BookStore.Books; +using AutoMapper; + +namespace Acme.BookStore.Web +{ + public class BookStoreWebAutoMapperProfile : Profile + { + public BookStoreWebAutoMapperProfile() + { + CreateMap(); + + // ADD a NEW MAPPING + CreateMap(); + } + } +} +```` + +"New author" button will work as expected and open a new model when you run the application again: + +![bookstore-new-author-modal](images/bookstore-new-author-modal.png) + +## Edit Modal + +Create a new razor page, `EditModal.cshtml` under the `Pages/Authors` folder of the `Acme.BookStore.Web` project and change the content as given below. + +### EditModal.cshtml + +````html +@page +@using Acme.BookStore.Localization +@using Acme.BookStore.Web.Pages.Authors +@using Microsoft.Extensions.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model EditModalModel +@inject IStringLocalizer L +@{ + Layout = null; +} +
+ + + + + + + + + + +
+```` + +### EditModal.cshtml.cs + +```csharp +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Acme.BookStore.Authors; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +namespace Acme.BookStore.Web.Pages.Authors +{ + public class EditModalModel : BookStorePageModel + { + [BindProperty] + public EditAuthorViewModel Author { get; set; } + + private readonly IAuthorAppService _authorAppService; + + public EditModalModel(IAuthorAppService authorAppService) + { + _authorAppService = authorAppService; + } + + public async Task OnGetAsync(Guid id) + { + var authorDto = await _authorAppService.GetAsync(id); + Author = ObjectMapper.Map(authorDto); + } + + public async Task OnPostAsync() + { + await _authorAppService.UpdateAsync( + Author.Id, + ObjectMapper.Map(Author) + ); + + return NoContent(); + } + + public class EditAuthorViewModel + { + [HiddenInput] + public Guid Id { get; set; } + + [Required] + [StringLength(AuthorConsts.MaxNameLength)] + public string Name { get; set; } + + [Required] + [DataType(DataType.Date)] + public DateTime BirthDate { get; set; } + + [TextArea] + public string ShortBio { get; set; } + } + } +} +``` + +This class is similar to the `CreateModal.cshtml.cs` while there are some main differences; + +* Uses the `IAuthorAppService.GetAsync(...)` method to get the editing author from the application layer. +* `EditAuthorViewModel` has an additional `Id` property which is marked with the `[HiddenInput]` attribute that creates a hidden input for this property. + +This class requires to add two object mapping declarations to the `BookStoreWebAutoMapperProfile` class: + +```csharp +using Acme.BookStore.Authors; +using Acme.BookStore.Books; +using AutoMapper; + +namespace Acme.BookStore.Web +{ + public class BookStoreWebAutoMapperProfile : Profile + { + public BookStoreWebAutoMapperProfile() + { + CreateMap(); + + CreateMap(); + + // ADD THESE NEW MAPPINGS + CreateMap(); + CreateMap(); + } + } +} +``` + +That's all! You can run the application and try to edit an author. + +{{else if UI == "NG"}} + +TODO: Angular UI is being prepared... + +{{end}} \ No newline at end of file diff --git a/docs/en/Tutorials/images/bookstore-author-permissions.png b/docs/en/Tutorials/images/bookstore-author-permissions.png new file mode 100644 index 0000000000000000000000000000000000000000..a20093d2c4f4f6b0862c311eea44aa74195788ae GIT binary patch literal 77873 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

;1mA!2UpDbGgp$(;IOqTx;dy}Xn1rnZ<=fhW`N zA3MbS(yo*8F$sl5e_i8IsT~TU80!T z8})EqEr@{r%7`0kK=tddNh{uG--U~Fe3^6xHMlIoVf@0=Rd0`DBbbHRrgETFZ=K>f zS*&rN@gaQo0*wmaAbQ{NcFp-@JbDY8e4yb_ONZcYnG7(7uDDY&Pm>=xXzAC8ds!YL zgWmG$WCY!F+Zo1Cl9R*5UJbU8o^}zC&&p-may0hDzQH4wpXT5g9x&D zBo3`IUMTjXbNX+wt9{D%qB+NGZ3s$;&ZerjJIM@~ujGNHvND%FbWK~x;GgaY^&;Bs4FNDuiXhW)A#X^yg^KrO&EHuw5 zH}WSV=I|Q*0`1V^2t)1967Cm?CAz&Q_ltKk%dPBn3h?ir4KRM%YCasNJ+zLbnG-+H z^$NTV2IqcBfuZlXek79!ER=!FvbpR!Z`zZSkHy_#o{;7RD@3)zdAzk6=ePGc2@e99 zPS<#2AEUO-3bP-v!G|(KpeEswir;Bm!C7#X6hi6(spLaTEsnkPMw{<1*uV>tXFC+l zB!-ZsKo)v7Z=rmM@>#Daa)X+>*+_ZY;JXb*HlGu0y+6do9STkDp7Qk>jcnl_u`e9W=`rtXaeCXu^_E!Ta6x*xJRa*-=9%f5 zFnW#GdRuoesM!QCM(GSwpK!WmvGq!6*ouh*3`cBYB1h2!^CM4$y_>9kuCH7K!&CPEbYKT54o!_3!}LdA>CY>eE#;~T zl>D;&FfYeDxyXyIsGwsl)2e8}n6XjCTHNb;r&VCq8{V4HC!LmVXvMLQRypnW!O6c8 zs2!We{FukErGqVe%_MWW(m_06Vnv1>)+(^K;zqbyu#jDMxrY_rn(!CB&R&9A1s=1mX5b9T9ydtstpNw>WjH6aElf^sQyM) z`NB$Lp)cOZP)+Ajv*e6Qrcagp z|HRKAXit(-RgDeqd{yvZb{RA!GiJ`G9ww_hIQDsJ(XxQH*_2!y>ujobEc83&iW2`` zwEM~qle3y$kKwEWH+0GGrAs_HZVgYSvxDJBeulujAT?Pvse| zF4rt8H=z^3?s)N2Xp+1A-U>*9yV!r#QHN04OoOx(w$jVr?4D%ZzBcYeCh=9z-?-gI z%evQhr%X=gr2N70Kzo$2N4*iix?)(}n1h=v2A4AW*HWLnYl4SuKAvq^eI0pDOinJH ze(9Wo7hVub2dIQQa$}?g!*mbz)DFi>W;N{BZ6j6bhNaZY*;kH6D?(rJZf9*OVGYk? zW!rqV6$DmKN;@@SbCv_2Bv3DI%VIL(TAj>gBIv{gG99I9R>(?hf_Rd*-mZMqJGf+O zDo$l+`(>H;XCXt17^j|Ac=VM=Qs3{T(xt3eg{;&vO2ozc^*w+Mv&&z}R_0q7nz1@6 zD-7au$^b`Z`&5yx%*=v=Bp(K`wbM|xzh&eraXc=w_P#zRN5aco-Du&EU_la=>GRUp zByOSGc#mJQiyCb?t6)0Mh1Ad1Y!N!L*QO-z*D~eb`A7Im%_Nd3Fa& zFq1RS!+_6`dTN8b*+C!#aL7JCp1p%-5DF@HS;ux zD`H=1D_+&nK zB2WB1WTY}>U}DuN1K)K_JJ(LyPCXnNrT%F5Yo#7fo2r(PIE%R8ygYJb*C7xE7s-a> zT92a59E}vCdSt2b$ywX0H1@;@SmI%*F{R`?6obRQEgJu)kGKfGN?GRho5yT8rjckv zCS0B$KPj>JW#h8R-o6F355HfjjDHdzbdG=*BrD#vdo3|_A_Sm%@G)^}yms7b1;&Yw@8my26>&+)kK^ylrt zS?%UGyN3J{b4$@EJ8!pmO;_FUxsZ8;B?w!_| z*%(G@Kd0GSK?7UmQb|D%xy`6Ga!mt`)VkBiH2o|2x}8uGE{=XO;1$XEDjVf!m)YvK z*$NVLO*Tc@>bvE^gl4CFAmQF7o~N4<;wSOuHX*N~)5XY5KxW0yxL2a`|A9*;S7o&ImIcis{?` z8CDl%U)(!C`$jTOlVcd|lq*{AdIl@&vw zsZ&*tUIlHxQw{K~!oAcMuMQCwjKz4c)+C3(_XP!B0bbcGDAm>2AFQ|0Z~s zmVX$Vl5xxa@swZD)JygiG>hR%;ikXq`B@Qd!$dP1O3GCh0%_=oN+&nTjGZdKR%*y^ z>TW_tPQLXg^p5oIL@Cbbib=&liCga_5Vyh0YI$C8vR}aW;tkuzU6lqI2%j9;&89&+ zw6Tb!`>H4+-Dec+4sTNj*Hr4^H@Zt@u@j#M>AdSo0Mvh_DsjMK-$d zhZWV!)_!cv^S1XJJ-0jt4GsX=3H7Utregp&gE54eqWl&aE9tSK2La8bw=M*{|Vy*>oxwkp1H$yV_3*h)7GcJ#NqD=%Z zSU&&L*4M}@5_FCN4WVS$#tWnM)&vEhw-{#bCfBax+QFylxj@4N=4jiB&sxRI3rv|t5){{GAAOn5X0>g3zN zr<&Fv^TaBI<65yzX<`VJ!?~yn^Md**;Ab6q4%T^2e4MZ+RsSsUqfohAyly>S(kh_I zX1lG833kVT*=vjuTSkAlP60<4TyM$M0+R38oBkGADQLt^YtkZU>+U$w~6&bTXa0KJ6aX@LbMOSV0-=VMGIx> zs#8*9VD;icG5P@kb~{&tuAQf_kLlan_f&_U_v_l0FaA*y9Jakf?lif}+44+k6E0#( zLd5rtC^-jAt?^Jo;$5)Bn|{~hR<)vIL~=a+%nw5i?tZg!Z!5!(GZHUJfFPWgQ2v-~gLS_%bc(u9ej0%AD-KIm{@rK^iQ?1k2CZ$yz!n=rxTJf68YGP^bSXm(`8@+sePni$bR7OrGpRYdhYMgaW1{tmr7n zbOKPz7hJ~3$HLc<0bSvluBzD(7Ot6RpG8#u`MUbaBO4q_m-N&rra~xt8WSMUz}T+E zG-81Lwe|b?wGY}^C$ZP0YB0NQk(}7?MCal)B6GLeLL#b` zB`Z}ul(5RB_q1by@tafS5pqMPsc`am^dkDEWpjK$=OjBAP+R=SM)4qOz~vbv94snL z7W?We-exunu0gyZ12GUuA1D`*#)%~C{Pd&c-A@t?K&7fV!JZ&&e!2|?I~WFQnYt$Ovw3303xii{M%w4P z?q_`=ad4j9rsR8Nh&mPAO1Xn$4|VAKxUqg($cJ2Q`>Yq8wI)3`R%acXIU}>Ai3lG3 zcA6rdUfpqoSs`M+SDL)YOXYK{XrU(2KC6I{xb^Ny&9tEMa&Sq%&sd1OT2Yp;zDD2p zNBh<8Y%gL`qVb@zXl*)QVVXs`$U^ZOF;Iu6*+hRBij!x^cU#&Dzf6X*DjUTY{I<+o zJR(J)gA1itD);b%7JHr+#a}|*`{x5 zg&g;^fFRE=xqBto{c4Kk&ZA_NxeA^2`J2h$*?|8%P43WAtZCDwnblff1vf8T)OcK! zm248z*}3l_X8T2ixP|?CZDH-~qB^fKt%N}9oypb%rBl7kT)?DSHl$FhqIBb!{nIzs z6439%lIeWh=4IN0re{qfNP}QC|HeYzfPrggLi_ov3p+gHq=0t+g=AT*2FFBJ1^K(we5O-y72o z<7-fyAkfxJJO2HkHQ3v-`usv+y<%2fWk>D1lG~;!+9d;BJk=tC$TQyW(BMn|)%Y+@O*B`%WGM8H{t5->W zC{10_;blX`WRO5!5}eKIlefdy-lq65-(RM1l}W~d{Dx?(dLTU&D_{(ISS)SdyKtfw zd2rgRH0y6FN@2+FK5k4jcIuEe!*3N}N7!J{E<k&etKj~EivN9L=C{e8l(8wO4b zE;;y>{frR8Oj-t-K&9#aHN2bs}crila96KycPnrH)Rir;&V$ zgomYBGPf*s1+}Qb-Q2{CYx0!2J_V9xiQO90ss@qXXZ#TY-oem%MzO%zQbxnwYM0$l z!_lH@ye;?&nL~YhV>RU3(Qg%Q<2pfZzg;shIPcp!Sdh}{M#kEwiPXX&X{;Bkz1(?> zeScET@9rN(VxSK~82S{&FmpP!DSqkik~3yu6vTA3~KIk}s~?ol(~!NkSnD)uMRSc=J3yS-J? zQV4L)Z`jq(`3Qa*7N|dA@zWYLR56x077vCGgju~MY%p&XXW2_JEZBM>KS{+Wrl}F5 zPCK&J4-lbkc(n@LJ`ZFr{{D!nw=q$mm%@SKNl^hYLGpg}OSFxfm#M*<+aODB=g4V8 zaMm{>6=#R>+QA+Med8eSMdVR;l1TpJ)MPOSeAj_x`W_h(HdPR zZoJLEV6uw$vAh=UivvY+)%$=&h9{*VEAFDHZpTii9k0afrZS7{#-B$p&@LVmJBlGJ zHr>#2w|y|DnVu8l=P1Hdj#caUYptIJ@>bo6MEaF3F9TvNid1zXraXW}e;07U-p;2I zjM`&yPf8RN%;}Pb>KL^>Lg3*}KJvSdo}2%X|lej^tnMFjxb}J9oD)UHt({O?`VQh$s8c zH#K^({w5I~UxGfurQnX5=P~;^q0w0XD;>oxn@_VRk>uJu%)Z8MezP|gO~ui~&lGAW zwjex+AB{~M^O62;-j6^TZd|h|i?;&vNaUZm9ir^l_}`9JR}8~?ukTgT5q@@SoVsi! zLkq?$kev}wtP$UF!TQMOcZ+!{v`Is2Gy7_$NLE%s#UP#$sgabdL|?UjbrBX7Wk}mw zmBY`#qQ5TP`)OF|)Set)ys;aL+21Bpb{RcBXqaT6O?hfP zQav0E8=ArjmY+&muj!-7L@?tP@#POnh2$FmM^D3I02U`?{rcrnuDMp*(45JZuzAlC zWtByNu6%;Sz1LV_rBJ@7<8Q3EBYXsc4la({NqyBV3Fe@Vwb8;~CcSJ|8lJIw&4v$0 zHxRkL(O^|lpucBRMnmf4q~vTu21MiKXNkN{Syv1Z-i(gq+b7RZP)Q_@~`jP#)*a zvsHyT7g#~lKqpGCNFUdBruCqlhfh^CV#i(%IVAK^iP@8fw2mQU%YX+?$)PzmPkAhdzDqp67hj6Lww7lO#U=UHYdteZO!q9 zaFp5}6=c!$c(~?DY}GRM?UxtLOImuoA(55gV^f8tlvrieMe-Qd7oWrvzAqL?a8Bj0 zTWb<_)XsY+%Im{I=Oo0=}z-N5)!zh*kC@Q$@F4n`G83sL2z35_3g^cs9+Lnkn8>cjiiwhY>^oy2=O2fBclF9G*7lUjUeYr>4Hj(PZ#=^t-JEphR zeS3&gu-DQRlkN4Q|!&o&spN*=D84XUndxFH8y+%eCAYs&TEgQy9ELSHn+9twp$CQQhv zc%P*OUv4-!>8c=WYCOnJWJ*Dm_v}ph{n#4hX_gp{%}QjNJBvuBM=4jbgTavqMQUwf6gT_Kt#p4N?}8W8$5K9cOK4mY zxEl?V@;k3+D5C=njimMH)z~f%yiA$lS7?ouvgXN0yj|T`j^O)f#9nJ?Ga|f7C4!*8 zCqq6g(7*Ma%G_?CL`SDtXrYe?@I=}%iT>Xo&k2ib(@98Et5HYEL_jp3=Ww)Rb=s+&@Kz<{Q2S0SX8(kU4pP(Az%WGOXf z_I%xoJ624`5iJ8ZV@a`zrZ|`dYg^~RAJXmd&~eG#lrN{3;kL=OYg>&(3C7!RHrTtn zxm`VZMM8ipwx(h?h7(LFj+^Y69jwI_-S$egSRJcUUMF$fstRLbUiQkt3>iC=c>XqJ zwo7@x!<`@BkSa8Y)FLqtEZQ48FF9i;CrA1*d*(C6tLqzSmlo8a+m zV(%rFtYQw!)5>BMh{8BY(jKHs3%GjQy`AjslB(9G)#QJipkck6W3@vx_+dwOVCZ8l z`@5fgak2&Fg@D~l>>mgu2~OW}o2*V6E`st~>?D^s3KLlpMHr0&##`m(+(yg28QkMH z8g8?4<3B~n>WsWC6->y|pal|lW4y0wu5;A{G#^F#NL|-fXovZqmQMeqc3`wo18xh$ z8J>wheM>$uJf(RI&ja#D$CgI_61|71OKttMY&9nT%88e2ID+!?f9MdSpY&ug3>3qU~ zx7we^YV`>mdS%;lFHGDZ1A=Z*8Fucv8oP+7EZGJScI05b z*gfn^`&q;9f__t^`5YT}1MxbHZfT<4_B`Bf1WXrdAD&)^`BKvvXzzJc1D7>s2VW!y zeBipTvN>?Z$7lClo((LD{4qZsfSpaiCZuw&hfOd!a}b?hW8^)GUzkaVv(A-*NhUN3 zFJf%~MMHAbZ|Hgj|;L+5Y{Lh%dAszv3HEIwLGl6Wu?l=e*hpyC8 zFRjc>YedUr@VN@%)0&!FS zcM=fj0XdLd_^;f#`~MlRdcGr&uLc4=uj1t5y5&Ius-`0+C!eyStaLGD88= ze|6DGIru;1#Qrf`Gu%J979~!D>Ht}8G5H;R|7-TRBjU29 zYf}X<>VyBYmXav+vZYYdJWzY%|ERqf=KiDqr@^7m4$6`9_7`mWK~`gBb++DRbs#OQ zX+OqM_~vtZwcUO=qp0iJP^R?fbN~YSz&cSVO-uwV76@b)91za5T??|b5siBY&{xfM zkwXl6p7JuBG-Le^!2ADMf(+#cvcyBK7ksou@%J1I#=lAGdZmS`enKtA$IKRc3Fl55pZaj- zQWEBbbYhB&1<5FV>#qhN0E);`ob98AF>_~?PR20b3&Ldc#}1GuQ21$k){@b<;$BpjEdXoB9iqU zN(R_(b^eR1vsLeP3e2uB)mzMu0nJbF+stU~Sx;}(_UXH^tmuO-i{o6k0PBmX;C~4Q z3B-&T()L15`qL=s9Z#tTm6;@05-gsQ*7L|GJ0inJC+#gNCnQ~vpZVZ^?qEm8V56p! zp6V2sp2r{C!KILvG{FW3IP&xg#0` z>3IKvtcQ2SR!dp-g59`U4s7wAxU-cVW!&6}!C-uV!ei!U{Fan`%DH{>K4N_i&ctX` zVT-DiQZf8~fYLW=T1wwL3I5i!KNIgUXmC=qkKd|Ps2N<|G;yjzknkcwky zQXYVbSP%6@tZi{GoBmS7HJu-+D9ig;h5W5vFmFB?Cy|M4X|#0tA z$zUZn6RXlC-3=(@73f!tD-ntY&bEm}5_SL<)x7`MU_%kjRqJ3psH!6~u<30z{&gwI zVN1fW0aMXrsu5-fr{I|O<2ZTeQYs86mQPYTuIjqCjbf;xm8Xm~ zxowQR*Vc>kTLKJ#m2FgwD5YG$G_1)y;x~19yG{elm~r@z8%8Ha^Ck5WBYRcCY`g)O zn3>afbHYJK67SK^?M1If$a_s^Y(^9|`rBi$ar)uXc4yS)o#XP82@-miY*J2#g)U(clIdkj2D`QZIeP!2tFO?-5| zj#Z5y7r`H5Y?`d#?kk_6u3Pt-LKWbO-eFzMXZ&EWy7vVTLuWDt^Mky{Mt`6=|JBKa zE|i}rE@vkHmKg?pwMp=;wd=m}t07_AkD!X|Y$HwG>FV)ap}q!)ptK9=1F*fgAyEV^ zD>&(R*-X`~&4 z^*5DyKM`&EB2i+eor!#1*Q}(WY}L)-F`^L8U@U5}qJNyZ>omu5UY0^QMAq0y}d;Q+#@@Vs-XuVG6E;M+bfU++lbeCFL^DL#SM_*pq7B!f^?ht?hZ7#_aIK zZa0_1EopIcM@s_=y(`>}r?tz;Pd5vFVgqy5z~DrK({U+k5}oQaGG)XTzj2KJ!ta5_ zc<}KE?^E}XSJozfI8=eN|Ka}s5~%S6AGccJ2}!@RE?Q*>M9RB0M(QMrHVUygrxS&C z_Ba`tiAi*eP-YOQ^H_%=PIsdkA;$+-(wjDJ&B{PbR(ZV6G4xo20~Gf|$<1kaz(|JV zbhE^iTx26;-2>et%zDC0|9oQg1TOi^|6m^{Jm-OlmC%t)E}#&Jb?WWILC}p3gOVYE zuAR6o5!a>gJd%KwdHL3SK7flrpP)yDnzuYIp45N@lAlTeno-PSd-jXrend->v?>mU z_g_xgN5lpVG#m_If^eh>YUz@7yK^#%<6F<1WhsVnOadky+&C4+aW>0TzmqUkwErq% z@adjJZ_t~q)-O)TgdSzCT_)v{x}BMLc%(nOMy!!=>6>Fq6Dw4Y2X*Oer73oThkO{P zz$9cnZUvN+FXqpuD1~8qCArMG*d6koiD?#Ha5pG&-c~8nr=2k##VslK#beywA!%EcXG!%FuOKX5OCigU|w+ zfcFR{Wu%D!?29CE1iH#%&^nnFOl{=4bG(|4Jua~RO{^{P(7`we571dR;Li&#Dl+n% z|MLF7yZT{eB2zk8B~6r;Bxr9?iU3${WmRaxZZB^$d*ch6vQAT9dLAyW)16L2D!{d8 zdxI00gFe2C$AA~*dU+U6LxD!VO3T@9Z?g==JD@sIWX8h>y;^QqYo4I7Pdx)`eaJ4f+3b z|Lo9%Qn8s=EblajUIA5G{5^lq>2bZht3T(ig6u9iu!26ghyAqGNrhK0LyZs zCN5Qi-|9I@>WT4zHG2FBIB0N$=QaPc0)ReO;X7|S>j|6tydy576YB(stt3;$v1f2Q zu~@TD|6n0PZ&`_+((S@-1InGYlb?|jkHrR4G84Zv3R)t1AU$m>k##51X2_k$d3$BQ ze_9M^dVL?84rIj4$>)eaw-6WI;C}h&r_cSllR+S5{?{C=;h)8RG3MGiujm;@idlj@!$Ax zyc}@VSic^5D(QC~s0!cad6pfrmQ8d!$-S2`HuzvfhaeDTVSnU#5acJ)R!8Im%-%ZQ~0FtzMJ*GeQ-Sya}X89w%-0Rp=z>H zaapO;W2x~(0zRc%x)BQXUt6-Olr)4uY|9CIz4d<5a?|}K`S6VU%``$aUObUAZW1izI((`_{x?t zhG)asduF2)WgNf0NbJ2}G?(Foq{5a?v)LRb1m#+3QJpW6@pGB2QO)Wz`yMUcr^rfJ zA@~h^Lu+*paY?zOU4$d+J){qih1PPY#u{cw8?l_tE_e zpoBgW-Ei!jyMX_t8t}*LhqOw0ityg@ujbGC0qt|VC0&1PO^~7a%yi6)K6N%TzoH0x z@7IKElG2-~Y#YW#5Dq+DkI^6em(F{~&6fSVe(bqC;eU8O)p)9HY)b@?b%*@;FqW`R zWwkDd$9m1%-vCZ)Z)h_Gc}Qri+3>u*!mW2P9Ei64Vn^X@M$3M_laN7z)8uKfbAO#b zRj!=cn`YYjlGq$sw z;1n@_>OY^UZ5jDokel9O#ODvVa_?mi7gAUcx8|oHgUyTh)%xXh ziKGReRvlE1eeOLJ^ha#GK|J~wP|1I&x10+@R9ltXquK`7lv-hA4%**=yG4pgs4S_oA zs8@bQp2ciFCCYdP4qRtICS2A4uLf}5)cY?q?d^m{ryVBw=J_B#XYZY$64^SM{Y{uK z)>~-NR*_>6utW4n5@-G2_}>+2NjbAK0q{J`IZ)v{(OO{>!j?82 zY4LHZxHc`xQ@Kt~i!3Q&y0|Ao!Ze2RcqGNFaX|a?beZ2wZ6cw}K+5MmlooQ3?a!}V z>$Rd0@772r74K~}RPm<;k36&S(MHtEwLI9x+Ye%G@JIQ*iQ-;{wh=Dz7dj!HVRpy7 zxlKa!lt|=gK2LaJ^VqKkdh6F5Dr`wF&yW#+CFeoFd{T=;lSEf`LZkk)N z!NSjz(Dq_?e|7gyi_Ljg*ivq{vng-=nm9T6vF@hD(%|}T!kiDG^3<%O9h}dyVa8E* z_9HML4icRuzJ8#VtJUn9f7bfgBo;bhGwIoa-(BTeE-G8rr#{A6=~ilOiy2r|oVC0% zj($k|$ldHW9jZ~sev{Q%4{UUgydLMVE8OyZr>Ho0)0({S&5!hDXWckX9k;#h*nr0O z=h{j2ysyN#5le%GPnTmt-SPX`3@3W{T5U;E&%%6#DG+%yOH>z{sC&0!1gC?NawwW_ zw~W`b3&$@fPxOX;#2xj=S7>cVF*+MYB^n<*j;S{}4Y4V^b@!ogenEl#3;V5Mo3CBl zMeu<0c5O5^Hpu9nb7@0!B3b;t(jYV2U@Fy(+W0t(Mj(kf^}6UrkX6sNtExA4`f#*5 zlh>8edCpH*m0qxb+UX+XZ@rjb5)mN9?c_lc8HL?#Y*j@O=94AJB5C9zNr8alojWVq zt2(>E84ntuO7Y9ZW-G1J_O2_|wBmAE1F z-{`Q0ZsxMY<|3u8o9A7!(cs{+gs?OFx>vmXW#k%cw7Oo%3DpD{c`8NxepPA!ES11T z%fQy_)2+p|1f=m!v>~!5e zCj=mq=bKsd9lDztTVf_zC^O@}lWQ&bYJV;&NEqSd)(XwC9Lt>4Q@|KlgjC(-{&Y8A z6cP(00{C(bqg6JiLr%H)_51@K@h3g1a&`=TRKEJlRLXqZz^2rufBT_LDb4`nGB@7A z$jK*f##-qA^{2gJ?&zFx_9OYsc?J4!+n98#E!E;_qk5a_MPfH(r*SZ&=D~8jkQtMMOCD)A=6X)qXwFm~zY0d4!T6}+jj=b3R2M zuJ@=R_cODA`*%DH#==c0*Q;kqgrLi!(iZELODvuI70dMPmW(sCUOH0{xAJw53yy_H z9%IX4^bQ`?++9kPP0X8*!=(*(h4Xof$?^=S81uTKcP+c>7d8Mw%QEM+A)k+9NBN8; z%v$d%KSOt8J6W$7UaD|O{e5P1I2!&AFzdnzK_viRbIqbCGRenK>fSl<@n z*DovW@4Fg2fSq)>Vv3dVS}natVl+Ew^TA-I~v1~3{t@pzF74gEoOGYbU zqtxSxb}v1r9$LM_ROd`ML%Wk1_{7&JC3OT5s&tPi00LT7OLh*G6`UdC_ zJiIosJ+XXz$nipj>9~4P`r@Fj-$zdqM$SQMFH~Q}{t-h$e79b#1y7No1JE6U^|o;m zT3bKo6krP1{}@(y4DkLCH;5muE~6g@X6s#3$t}Ezy=39p4QF0~@Oo?x5Z($v2>tjY}w9YDS?VYoZKi zFA{j{9A3N)l=oW?_Z;@(sb@+i*8n-bYe4WF3?F>yosm^@=RWj$axBj!6V|@o&YwIc zjaTn<;fTIr+JJkvP7crm3WVG@Yg?Y>_4cl&Jfn`wW*2=$>Sl-5f*qzyzshq%%x`Nb zL_!<=_|r^ht<2v#tNJOwu3hKJwf|i0L4^sQ=oM+ppdughAI$Kq1!St2lShY;3dhdL zcRE%NnP>dv4}QQM%nms+HAKyHvMr9^NDphb;TUL4?3wzs8KteNKxP)w99rn~`CqU0 z^PXUt0O|N)rV8U?T#b^FlAKMG4-vZuqNsFVPq*C24;EDrRZg!!*}M7&mHKWGC$1%r zigq?jWumWPn4A?v$3|o9V!VuLZaq6NK52LTTi~TI;YYRO1$cyXX2gBP>9m*(xns4J zNjpvN7nYXsx}ssl_W*hGmj^LmFW)5*58dA<(ALKrQH+izl^Z{Tr_`Z}sT8lZ`8412 z!lg3676D)2P z4?_ynQURD#;zE)`V_`s#EVY&p9Ph59;H(bo`;Jw!bt)7V)&9=yPH*Atxwvidm4INn zbzuOVr6635u8~)@HuY1vyVOKkfMr*6=g*3B7{J-&vS#4EM`t5~zf0c$Q(ry@azK(O z?tns!9{a<|J{}v<6xN&Hod^8NHY;-VPiJ%^LpliQ7;Y5AOClZpCpp%Azk7>}szuj2 z`@$C)`Qn03qIWo_5L(;U#)$837Tob%pP%hJOOd+X^xNHW3}Q66#7pwt@w|yI!yMIa z2=h3D1Mpf23On%UJPrz32ak_bpC)tDrbaWIKdE|}LB=m%kq~u$zEojwLt)(iB)B`` zsF(ny%#x)>US8D!fg&19zD_aYH_hzF5?wXYX5gVB5b06ETWub2?in!W;w|x;RCQ#T z57cDTJMOaNK>_zWlO%4!S>*lVVpXU#V)${$)`XrzaqHHimTE&;>!sA1Clvh>V5j?+ zmKgs#Z4c&#Yv99)t7N8|1^6;|HpJFUDO@?4qnhZ^p5Nq<%eo~~b9sy@(7L%sjc1F*-k4=Eo@cHlCD)%HLMiejBD$!XXO0);tsc-tEa--=^1ow z9^JTj$0}8?*a|2|vmTG8XJt5)$eNZF`{IdaH4X4@3rEbtl1_oX<+@5fHOtS(dP?Hf zUfLO@V(gKs*C=TzaiN+=dPvnqyN@HN*LR<}j(h4M?{744=j-Wdu;p_kg!zsZO6ki$ zFeMZn1<9BY!TF(C15rZiVRyORb-~nQwHqn-EN8{8@7m4u zREzL~JEO6ZUkA#=k$JCihxp_by6#6y&clhOH)S26hD>Ezdb*$}&Z=#bdnL zJ9mL!kA_XprP^#4tvFaVz?v}(Kn(8_KNS4c!S$_felt8_lQk#N!F;8n49jx(+N^Q^ z7IyRa;qcL7k^x@ZB8Nv3-3he`55I(*k`3?6aG@BD$BcQgQxwCzh0VTN&6KYNw)O{p zb<%=$1K8m947hwqI3+J|%8z;aQtxyvnTYu6+4kpDreHYFy9W@L+FvEz1~O zShuqDt3YdM@zQ6H;&G1XPwKC>~E$m7GOQRV#LT!YN> zF8UmS5JQ#&Tn**Y;e5D$r*f`sF^szVA!_eWcDXUn!E8zgatq$8kc&ws=L`?2TN`OI zWll};tO%!>T{{fl8|A!;x*9LNnK|1g7gl*!grNh`xblH9A<}3XRVf;Y{ZsxpXu%iI zHD+tOwhqac%jB-}~RB3FN39Y1i+B)<@WzJH-nvMy_PmDtGbt2_Fs zw+DL=N5~b*~h#RdK;r<=b3I=SenaamQbLGt%xv$*mFtd2T)V)Uz>Br;Mx!L<~ zIPzs;J-_)#6HQEJYF?X(3b^ew{M>wJt*yCfn8{E4MP=9p(-o88RFe}P6R28$jqq;+ z^$tD6pV^B>ytWH-x7;N`bMK3sR z#2u?6bupQ4nUBR;r6s9+`KtLdHGz?d_rC~>7HWZ6#vn_1BTu7nGLqHB)=>D zC--mT97Y2yahmXJLF@CQ0pFUI>Cyw{$2=$7%Ilq`^Yz2Lmpy$t)@<=J&5^5#d+W~N z#FG&&RA8rZS0PqzuR#X)p(S*0$hWJ^rb9te?y##P6bKUsBf{P}ChVu0Up8s5OS)8p zNxjx z-d+l4y3w^VgUq`yW#o6prW*OIBlU%{SGRq6turAxU++aYKADvZN zDz+cR8d`6x^leNLs~c=-adQQSCQ05(2_?mQ5+D6k^5Ml#%ZD>%s}x@>^w{e&Bm^nL z4~NpwzfU>gwATqY(6SCDsTTPzQGcOU&*#eDm0r-+5NH5~fOdey8GXMq#40cSvU|H* zb(-R}w>)zkow5Dvr$U+8`j;N!hx!T`BP_hGvD))R82$-gz=PU>-GA{pxoP=#=IGJg z;C?53&T(WlUjeRm;xRR$Fq1YOxJEItm$5#W_VWGLcVU>m2co6Xd?V;^l&0Y3Z4u&{ zb@6isjS4(Kl}%RlD5(<{#Fr*%JF^_;f~YkS9!L?2g{5S0BSF$jLvZ$vfD&7)1j0ji zWw>^`QGdy`w(m+Y2135<1_Q0*3;<8ld&*}-i>PZd+y8cMS}G9q<+kd5^H=b>YdLq zFkFzi&Wl=a4yx$e0?%8Uh33$KV)+bqrxk~KVy0uR`0+ATg_N7-j<#o+GQh%s*)6fL zo$^aXy>Q8ss~$f^dNecA=#Z{!!rAmZB*U3f5Gp#l?f8LJWZ-ZwIK(i1cU+I}jozIH zaN!ScFBe^Pm^H$=yAbg+jot(*wSoqE`Tjq(SV2$zi!pHRzwonffBqMtHS{0k z0c3SBPK)K95u9`Wv`QiXH_EZd`Oo`;O9Sps3$KcNdS(Co>o%oO=`WVw?{Ss*(E?#e zRb~&x=O3rzxwB}z|Dd8xZ>`a}9SwD+85V{oTFEZgj1!aS;m^e`Dx#s%KXbT+qCflD>S1V|)aFuTvj00Dj z0lO_zWc4@k^>_1mOoGw&=8e2r(|y)G0=o7v33aY%57|jN&AsTeECk7-0Pj3P7D9}* z*>%!Ro9vRzIrzXO)&!><4r6Sy?9HPE%rpWyt6_{T#n7vk*9 zWggW(Khc3A{-&XRde(KH5&iTWM8BkFu7U2;YA3bh?K5`JbG5&rT%~yMYy){E3qjpc zY4JMy(44r!iI}GB4OEW(H(7)7$zjAYlRa*y6b6wGRt&G6pnU(A@HCo}o}te$PkW;zgZ1XXs*CiFk@HabN_C;zj08mZmli)iZQb>=N(j}ldS*}(^zLEejaBY7P_RYx-@{L> z6v6Y$zkaO3Dog|UvA?m?Z5J8dWrHDiU{uPLyRh2rcJfcl zuCk0#dvM_L=D?*m1eLV1unH{2F|)vSYPnF(%gsIA=q3esrV6h3A&R|M`_sP))Qt^5hNpYXSz( z_g$`ET+$#sO+|C-9ZDfMC-y?nPlznLu$#S(oyv{9xP4)nJZ1%i-G$5hKLZQKP*Y}1M7)AA(~Al$X6t)n9v zYKGKBX=I37cb_bYB8rR(HB?o*fnfq`_9uVh>Fm#cQK>#%bBBvrn*F#4Y50>HB5o@L zFq_^BnlyOih7Le0!_fQH(om-Xql7p}87=UTIe^8#!7L zEUTtgnl2=V@4nB?8DQaJf+of{bH%5jU?InWWTy2w75E%;QVewj2zq7mkL7*-OvhHpfS^p}^XXW_awU!#S#Pz613-$LKSl?Q1NU!POO%XmylQ#q=_dB6&9o`-DSkWb7XUOS`p6qtH{=;@i0c`=CzhqC)||IL=$pH0zP@mW>%3sj2MC(-3T?v5sJCa} zl^Npd)QnmC$wBI%ccZ34HS{9jgT;1P2_pQ=r}pPc6M8|QZxo2SI@tZN@B(WqVVcxh z`vY0~!#?m%r2!1q@oQA1oaykJBnT9pd9pXhrPpiW(erMm<&YT-UpX#kFqA?e>in@^ z)7?CvC-rj!INyBR1=$+3{m5al7Piq~Ab8NBp9dO_{lft`5@%Pt&$U1qO7-n)x;2T- zqp&qdieV4qEgws(v5$>5Q;wq=8U1YWweI7UQLY2cMT?y#5;*)wX)`S9x4L`X*ztUE zxCISgLG^KfKkJZr*EXa!N7fR=hKWzb-*bQ>l22XX8$qEJ#Dk{B9AqT>UqYJP+^`Wb zv-pvkc+F#j0!UgpT$8CecB>Rw=lPzZrYm>vftG5sn^m8jC~99r?j2el0I9I5;jFc? z>3M$9rVIPrJY8;EBMmW*!oV2p6jw9q{ke{!*nr3tKQNim4W8WZ%o;q%hg%4Mari4q zq`t0B9@ePe4xc#Fn0ir2|Br2!vcp~3d$hsx&U{lNWC z8YaUcO7{Mvc9Vbz1fzdEFjZMxc_B2e;hcK^dyxNZUZ0?)4IzB7`1C_D;*TtP&a!zu zwi!1!f}elH>|Xv9vR)}HQPzvwX^`muBwo(TRcf@m2F%Gwe(7(uoTXO^(c&ZaPq81_ zhjcSdRS32W&yx6i4$B}~h2B8MFWS+NB31%p*b__tgV)ArE;d}H^!kQUkRpag#NwuJ z%9l3>Zaz9GzXVnZ%tG3=eVpjj`5L#p4EHKtq6Vt~20oIz!82LRMt#NiD>BbAzF_mf zXjTlRco1%+Vs#9Df&6&+iswC>la_>@vuC%Ge|~%QG>yk=<9*zMc)$2q-UVFWwWHQJ z1ApqT8iBb4_#Ji)?Gbl9gZ7_Xn<;6&-sH2k zn3!G!z+pkp#rHBel}F5C;DiV(95`*W8x+|3 zqn*SNnHgYyZ1h@iIOphN9(kiPfsV78HsMz~CkuUpoZsj0GOuc%rD+ZXX3aR=?iB7#l&v9s5Q6r3nQt8L9s87V4sUHn!5 zyIvSiVKaGs#9J_t(YPVt4JV8T6!DdhD1Wrd@*$)e!pJb|2!LlZT^&MQXjdan|B#Tz z7+I1h$#DCp6v3wNCufdB3zuBffH`?=Z|@Jk{-&4PSaK00`nq4}M1_qrDoUX`Jx%Vu z1Mn3M{0&GQp_;J1K%5|@>|iyFKoGkooTwt^K&Noj^6IqyL~r&&db)L~O1roHW^%S| zfScn`r4HkYWKVs{DYUrlDq~cxezSrbJJ0MXkY;3Irr$bz6x{?g2l%kkM0X zmR%dio*WpkGP0{|DOpd&ds(2WfBs}$6V|>sE2QGkA!D_A-J-b*nk~~q8lW>Z+t|cU z+_)0&!ioOMk#Vi=zBs4g3lTNKl~|c4-V=r+Q!QM{K4%}g60I`mIdkXT(vcqngkDup z)QPogTs4B^!`|eOrH9*029}|n6?7e(RauYK6)GxDMDB~r-r)s)P96fRw*;Pg;tLk` zOIO%s8EYqVpg%)|$KU(j>zQ9q}#jl)}% zPWr0HZvKpun?nKHrTL9aq4`<7Ib8jOj$*A6vcD(x zTPikt55uhKLWV?H5)ZtN)qM{Pe7B9&Jf8N(K*|2rLJF7_U1!hbc~t)iFV`CsoNsT& z)yr9dowpC?GN(e8j9k!rYjM@-eAVL43!#ALOC zRS67kH`C0MxUbijz;-%d*Dw(L{yo?%3x(N9sobyPs2MIsCC@m}3mHxg1Qo@85kLAk zd=tH8>rOx7mcU&FBiLQJ+2Fp}wADfFm557oqIj2SV>%YY>lK!nK>Qzj4{)gxBVIkW z6nsX#ihHT*`mA+JWf$0Jq#P;6mf}#>ocqTngNSnbHOEhVe6-ay>qM*aP3hf(M8+&# zj(vz9`0BVJU3>G=^E1MShR1MpS9ZzRR6i_XHj4%!7_RYszblMmv&}kpLgxHlXL$y6 ziwRg=>7_K-rn9%cFeuyNZ~7djvUn)`rJlQU8>0kU8qisBv?9U)Ic*flHYvLhucK8j zf0MPISaRV|`k(+|ht(KsTo4Kfg{Ke=`LfpWhYr`=6B6NQJI*B=v=2|r-Vbh0BfFA_ z+-?GuYvHh|5;sAV#Tv3da7it>4B`)s&~)1W5jz)hD4k(+WEI&`Zd&1PtyF+(F}o;x zL-E$PpfpZT?gw12@rs`aw#|u-rBgD zigMa~E{TrtrdtablX5myKyJA$c7Ci?XkkBldVg!Qz}2($%?2p=&bR58i+mv%re|!r z)-w;Byb8hb=q4jom5Z>i#vkNaymmd*)YMi83`Vv-4=aV~l%8X0MOFkhunG%01m%NJ>m=z8c6uy&57l!5Lg@-1x}hrh+fYY`5=PAuo(@wZ`M8zHS@RF)mU% z^_s8jf^4>{8Ac7&kwS>Bs^T^cmuEi=)a|XgU@j&QE~W*!j@nHrw{^ShZjYjPo}Bbc zF0C-Ro`I9_Qm=dzjwWh?o7Y@@E!3X27omJ9hD>NG1lR~7 zrAN_~Hdkz5VPL~OMdU4$rP(X=<(pMw3Z)85-@VODj_`sh_6N!0wmDn$UZ97AmWS7+ zonCf+tNt~6POt+pd&mY4DbQGH4BBQ~9J?jP3qPa07eIFr^w8D1d|o18XFc!ml;H62 z>f3~p=qST&?M(Pl6FsUZ!M`NHRk9vhdkuVhkR-ah1{laaJ~};uJ-2GrygO33LxtJp z9B0?)G1|`}!~4*2d7rNVZs@_DkR;hPa>#slQJ>WY{NS?%X@a$PQ zEY^l+kOTbi&3nF}Z&wDU7tz-b!#*vHYZ!X;u9*dPkjhPNU>aQj2BYowJGoqM&Vp+* z{{RKk+Z@_q+kb7%>X7OiJtbJ1Z|f(zOVE)EZC48n!3Cw$u5%|TUpjkr01$S2E~4wz ze)3NS16x(Gt2I>J-r#jpcDe7td1TBey1#Nrul%hZotEgj-n>%!(ZI0_>sxf8Ul zQ&+EACUQy##;#Zy^t}C`g_N8}KYI@}ch1IH%sGfY*RGyhKx{dJQo6LXy#2x1YJwQ^ z#SCdxZY-GFtET%?!1lHx2-L~dQ$*Lo2sb*C-z1^A*Djqo&}#}1pWU3fnp)(hs@555 zX#k65=1dSrAv4Yu*{rdQ%;OG9l6P`*Dccb4J49_qn$!;1R`;H;6U1_lIjfk`m17<*u z0AEtsxt=HJ>a!Hy5fny7R@h~EaKF!JzIB=LM(tpx{YcvM03UhX*WJ7;Ruh#O38{{8 zLghVRbg$S<)T#ah#Q%peR)eI{(L||HJnn<>#q)Gs<~$ypkeMtv9XQc+^2holEoRC>T|)PZzB{Xv8h63I1=CjUB>Ya??Ldaz89aBdXU zz)QU9>hcLIK_0yaJIop+H~`{S(sAWqzkIzxtOf86_IL`JiW{ZYCJbaV23j#u@0iqNP$_rrlWop}`vW=yJ)UFSBV(fj6!LQ?g?eG0!KA;q?Ep zR;7)fRlmFO?AXL=v-@?k;IQJo6tzl)X^2Wy(dBzflu`6@9@7&|85z+pOHD%RXU_`z zR0jftfG>vZb4!a?wUKJVHX8W-*oX{&(ou@lh1D{ZcGSTQ(IV0&XVO8?`*)fqa?v;T zmR|d=DH)csHm~81)23y|>Z-;HaGgdmQ5!#B8XwcQkKdDFhc2Ds#57i71F(8sYch1$ z{;ta7PYkq9g6k;!#P`S^0TWh<4d8xi`F$QVLgqMJd3|PQrR%o`b0@=R^HYS}5!ilg^fT z&(&I2FUZ``ao;1+2L|}#p*n*}IU9Q!hsj9Zv*b?F3w!ie+m0h%!0Z843hatC@Cf6SI~mzFeV~c+=Nf)!#Qm7l!3k%~R(J}FV%J6%^?MpUX?9S0x#&eH zvfAv;YM195h9tpwi*k5c0Xrd4R#GRuj&FIE)0x6w;w{MjH zq_=_okKwZa6`@h!Uj9D_vwBKcG!<9)`z2-U=Vuaw zMUX2R&&+WB-2E7L9oiF)v%tdo04sX%#>E4 zG5ngLYL`rh`k5J~|LQ-G#AG6@_tWGXmp*`GwX=LUuk`Pe9921-xn@VS2&&lQp~($R z7LR{mOXf#Uff(K2?SJ%${!fIx{q`Jfy3qVe7RsBC#Y2T76k5e5wPQD7<>m9p`*-3u z$OZE^D@}N$J>G?3KuFKSPbJI&1xt# zyZSBVd1HLMb|#7IOU-2gl8GO`SU-=so^&_b5JRa+CDzn@tmE_!th%%MBc;wr+aM-X z-HhWHFBsJ=XXm@@o@Jhv4(EPJdiT|{QV4n;hqf4P59~nHo9l@fodq%fPPPXzo1^A) z5aAy(5&X}@!PtY=m|22$$UX3YknQ7I1D>u1&nK&&m8k)qpkTg7L* zW0H6g3^#%*>@96@jT_fau}(+G`M>P{LT4m9U@G5N;kMik8LwEbfhDfORT#6}4-;E+ z_Jt9n_QwUj)C7j{(;#N9AFHJ2eL8Td*sv`*D7r|mklC;5A^9{YIJY1kagKSySuG6gFadi#xVn8F?MX=q{aXE0AsUCNDIjpLZ1LJR@CopAO+T4mp9~d~q9Q&+O*3XI@IlgMGw$xPIo>@@% z+_;9F<(P!o-}l7siX>Eu4si1vcT|m~QVGq9l^qunde_+Hs7sBOQYH%WzCToB*h3r^ zN!Lz+l&;nc?wRW$q7bH(^?cd|$D%Ft`33+wy(Zq+P^ViyS&i+yjtj+`+}tjOsLB9+ zr=f{&@CMnj0Vm&X7ikX%iAvvxs5H-HJ^u;*1N0C%J%yGohbo7Pbk*1l3^cDAK`KK} za}K!L$1S$9p$x6pM+t3_xKMb8fvm4a=YT+gZKH+>xiyDF1}|?GB>_EW!fKUJ)Df(I z7`PJXAV6+D5;ETX*Ne&kt0&~IjboejFIyLFw2zwLj0IMPw{PS5AiPWYyhfzM54CV= zb(ZjxB&+i2O*CA=&H*`}cqVJP`yA+ZjiZM<-rN6V}dvZapDHpk0r%*;Zxghxb;d(o>($Z zO5o&V#gvFPt3V~tTXFE>4G9l;gw#mjY8tQMX zmvGHiUWK5*P_-jBm7^%fEs;Lwi64dL@sqzayj)zJuVjU)2P~Vq?HUYC3THLpIxz2k zcTktxzg~6RaT?lh)YE>Mv)TVJ8P-6O1Xj@>g}f*Xf@MeNG)i zw!Ni+NqalkMo4OAqjeYt9r1Z2qiW{=N|HhS_P~D}(t{ShXY#0Bbc70p~^>|4J0&(@55^n@x zK78mCa2ne=SEety9(MlzV^y0W@bjSOtCw=+r0w7TF|+imC@Pnk{JOcg&`nvr=d+o# z31{x-X8VK4rmXSo0@7qWW<3eXtv90E8n~F<3TBZJ%W4F~u9&cKtcSFUitaOhAQ?pK zm@zzH7bAR2Sw$H1=$9DG<{7~T-jl*h&NX8g9OD@tt%Ozp`ta=2&Mw@e9s0_>s12*#}X(O%n*RyP4&F zm$+VGR5SAFieU!IMA%n9KOd*9+oDu23BZ@gVKXTZDqeX66Gv`lFpl0Fu~hN%dGrEYiTOaWeXSwINk6Lpp33uGb6W zxA%E$Pk3!VJQNX0t!`T?Qx6-C;U~Fm)4$GQkHPM@-YIg9?CcI{2JA`bCp#)j%< z@Y)^VY4Aiqe^$Q?QYx4V6S8^u9%+!{wg*ibVFV+V2Mw zRmh-}!N#tO5M^E)@oQg;!v8?p6mQ?! zXnf@{-c%!0sL#S z%1p+_pG07{N&Rohv)20!MQQWgF~K}NDzc8ad}WlK17=N6fv}{!p*~Y?_;RgXz8m3LCi7|$5+pyhirv!UX!a_k<9ZB>|zrOcJezPpkeTPxeW{`od0neo+< z{P&G!5^gfOkSs#zJT@wi!bS--DC zfPi`HyytRr54&Qq+^geG+XP>Yk)AM=0ndTof^}GG<+7@$DIPbX4hnwEf8w51{oR_u zsCmTyM;FF%#ojlUQ9;RrL`N{5%mZ&ppcExDiOhejfB!Gm2+MnIC-Z5)GSoUy{87U3aHYSrT1-zQq0JEyDuZ z+nX4_Ohrbh>+)olJqOM zD(hHmXd?WoNZ@KA)2s%{El^W~ohpHB?NXpQwH# zwXf|T#?Wq}Gv~&Kd-#&BY%>8%yaO=|)Oh{K*Zv%+GwdVVIpmX~=RHf?_aKsu^6+~> zefe5~X6%g62W(0A>)Hb&jiX~2p|K-v10<7I`8N*o^B z;drS|h{68;hMve#N>zxgOo%|G9EU|VtWT!2K z>fJTkY7?oz$n@gm@IBX&sbihD3#DPj!%mwK%91LcSvRht*7q{dn@e-z(E9zYWG(mp zS@F@;MxQ}{#7ed>D40RaPl``hOrPzzILm)bv$g`p6xLCiR1PynY?Wlx7>p<#U`sF5 z<3GO+$cn?Sy1eHOyqaC4r`PaCAy%Ph%xj7NOu@lQbWmgdkhNZUWI&0kD(4NesYJ6v zhIma%s|#w&&z`)uW^mHSJQHBZ(*60dQWOCw{NpEPBz)k;?pc@8Ju?W$6cSKDVD?1$ zE$itlJu8H75)!|Aa~at`2p zA%Eq*aON1zyC*lK3DAcM}nFb5cw5$B1o_@`+&68?J6Pk4BO75Tb-?Piiksdy7A420ZYX4>Cd8?RsiwvJ2dI#^2Z3DNv zYF#=q)2KbUO@OV|LUgPZ{aW_I{pYTdv~uL6hcD5V&Kt3;UAUFjYD$>u zUDyc|00oB)Y--m#53I2TY@lZ>+rvqmrHG)8xn9p_L#pwty^A>lVN#A&3Z08NDfMIf zoYDh|JwLb-KQfo(sV=#Lo4QSA*-_1e$ki;SP@GS__L#&gx1)A%Tqa^UvB>7zn>NcL z|FfXZw>cvHCk4UpBDyQL{R%FE`=|%9S=L%P_DeiZlFp9moe?;YR)6M?&^1TpYp;)s zIrM0T3H&r^$la3DwMf0&KP)k_<(OMWnfW8$9mbmr2;w9Z6;Wy{%DqZW+pN=%n7q#$EUutn-+{R8jL zZ@%Wz`;Lub!x#h1*6J^UCa})k_SoAOnPf|%ktg6WTZUBEV7Tva*>)_6%MWFz!23W- z^o+ViYprp2S>J(Ehp4SdVg+3!PO_LBhW!ptaALmayHl*Ln8)4|PDqRiP#~~%iK1)> zP6|y#>X=2)B_0VEM=+z&KIp^q` z!9scg6e=HUU&us{IWl#$EG{h2XT%XGgufkp^n{b?VmhMzcr#cDD-F~(16AHu9wcl!BzD-x!a@{CxeqKRYdBhlfxF;Y^4g9m4;+q%=H537DI2FP0n6Q=Too zw!)U}u0LYGhgSywH8rPI#x<#1U-ZPI*so|y-sxSOzXLgg3$(4)hO|gR++4*Z;e{H! z5VJtb=jC}CWnLi~=>Z6uq`+cED`#oegSrm5Tth?M2<`Lc$j*-KiZk`3M@OZ$X`(N0 zS7M`GxI|9wl~cX#VGx&HZ$V`08NCv!M%f16T01!mQ;zpqIyLLkq(o1DG{6KaG#LFw zV()5Pi9Rxsh7&`hmoi<~CjuAQ2wBh6!PR!%3%r!rO1JrlfgUzfCeU`toK>Sf?(>1o z@WGx6t)z{Y6`bX<=FG?H^fO^E4E~HJGuNfu$AjN)&s|;Z)pCzv@rjxB+7BT`WyIeE zZNI#&48I-1GNRv*e>Adgokq(}L(0A?n~B{{(d^VIJ{oIzam2f)^~@h~JTmy=z<`8Y zT+{M*8!0_Wwf+fBAfvhK^`aW8SYpqumwzkRZ! zJ@Bj(O^6x0s-RnwAqi$X#O>){xskCrBNbMd3u>B6t8frqUnRfg&LA}$I+)gKn8-S8 z|DmTYU7nsaA~)|{)03QNCjGvmr0!_nml85DE~`3r<=hH`t!Wi$g-;It>;{%_F2op{ zKk)uvkg;4oQGIqaap4!zV>v4d{0;dgpd|0hZO`_lgpbC<1K&priKXC1Td&wsK1Esh z(S`G%x$!n_=LBvu!i^gBAWHm)v5L(PXaerl$tlx%4`rmRf<5tuL05P}iPMz`k-TfN zpl|;YYp`4h>ndBmjM6Y%&~G$YX*q8PbC0W%@^bJY6TB0ubp5K@d$YQA_3OFc?SD2M z8&F5OVVsOH8!Mu4$kK83NK4XqvtALm?U_+y(^#!W>$2|~!=gn1r$@~Iy04V?eIuQR zs|M*I?g6TcQEfKnq^yUXK?*GPwS{bD{fP!DTbm6jN|(#~q49o+P(&at(kkL8@Sw_W z1$P*i_aw}L311tKu#FO|LCwwD`XC_EUQS+LC~q`txuvp~{aUgCG@LV|;%3{{vF?j1#%&x=P3UVg&8eoU*SOR8qHaXT z@>MiF=w!&4c3(tG8zpwzYoXFH{=;n5(4x8p;C|k>c}ye2kQBe6H*I`n>+(DeTJ$eG zJT}PE+{3H4y~dBHbFuwKqqaLn1GV2o89>2i3>N#c4eK9dA_!aqhCNvOF=(ny(j{+v zwgg&mWxaGc$6z6WhRiItWpzHkb_?RHaBOECg}E()T-?gt@t%@}YOfK!U7vbS92=TH zcV2qqtZEOFQ?79NSdVQ?ERBsx@Q2zL25GML8ZTk;cA_o}6S&uaG!7SnL3d9#riXUd zZa9b4PaeF{DyZ+vg!A!n-%@#+)UucOF4+o)g+TV(uC3{n4=`o4k~!4Ubly6XIY7&j!i5Dv<*{Y?`=X22Yi@ zLqnfswcmu&;}nq2n}*l-25$JLrxr~4pR9Bccuks|Ig^uCIHG7zhZcTmj!OUN-5m{o zT4HtMik2Bkzpx`L@;>BPXsxGmnf+5EDh4vt)knyz(@AOCOv}rc7&DR1Ts;smkw2d8 zNU~4=SP72~bkU7MoZ$d%LtX6#O8&Y6O*=WCo7STq-+?*EUjzqHpEU}io!kb=#;Mc6 zz;DKiXfdj114N{4Ag07O*DYw{f~9^ss$1^34Cc^(ti3xax3|#TD_Z32Bv8M*)`;P( z5$d};JX~Ui61;+-q=~|x?d~kU`4{N(K&$Y~Ycme-Gd12PQSSauStakJKOUf)#cN5pZgofs9e;dHO+2jqSQ7V4tMaEFKq^>9rkZq!*VD#W)C^sgr}%n!11r@EROA{znvwhO#uGj?O20)+ z@ohE9){UF_&Vc7fSJ5k$6(+|$&}@Vlc%Aahw*^xs?n!RNlW?Q63({m*Qw`#s?%qR= zb4DoMp^{9Kk6Dz}FWkctA)FONJ6usm=U_oAeW~rdVxz)Qee?nQr=|`9R_16}!EVLD z@z)Mc^aAOXtKp-eAuR~Bq(8Gsr4+GO^foDJChOE1?xv5!)>_y4 zUBAm|`9QX2D$x(%kR&R@>bv6pL+7_a{K(Y@j-jbeV~6AKjcM3@?0YGbTqE(OSTfFr z#d_FIAihqa-1j7exn=&$i63-e?t!MKg~=oEqb?pU*45{ziZ5gitRL#w?e|tL;vQ%I zw$|0pi94>cu8d8vmgfRVqVsU>)B!tqbsbDfOgIhV69-|!Vm@&ZQpcO$$R)+hH+}T5 z7F1xX73K^jsqfKv}dZ@_Flxe zw&E#ER1u!xc;?mQd~_XrTOMk9~EiyC545gp&K-y zoY^OJ;$qe%lOGIaRmtecK79V%+#!`Vd&y_3soB%Lr6D)|6vF7Z5}$Al7vamYAz`vw zZmR9^B)kQ_FvuI2dbs&B`M3OMUc{}5fbO?ol_<%|r|G6tU%{)S&d-80KS=B3NfrKp zw-WLb`}yC5o31B%JD;(LtHaN3XLp1zAgT_K*nn40{W0c}9}hmjGVZ4kON#8AeJdQ! z^0FxkvW@l5?}cg)5R1txA9DQyboWJ1?oT(FTQEAwxX%SLoYa!eQ6ZqqCTRYAt&YgnI9~tykv>&qWkthhdR>Wel zz{@&bJa9zUM9-cpipyrNbgdl$C8s`r-1okU*T3}X$;4{+!~L^CXFC^vMN{W$F$aCC zq5!`)?`u|;7(B;J;4~khVg-#?ggTdBE+Ug+h*7Wb*ysd73v0fKC$(pv0^ZK|ijB=E z@S5~5oXG>!xf{NN>h}%(vOk2|7|_)O1nB~XdD}0Vj3;5n89y9ls}I>?^zE9PFlGaU z$|uZ?>)p+4iUHC9vIYGnw5xCD!i#6m?&P1iusIFoXeLTHv6T&B0dqdw8;6b%VdvAe zTGU!sJvS^WH;Yi&@f_g&*|=;4vM;>cr#e`v?XeV#H7L&+j%yHWsBVgTak66_#Bt>5 zl;%>k`bgeCM)CO@SZtl$yjZxsFm5m>aKC8tMfX$9Ds}<9{#|FJ-b>flz<_`5wYp)5 z^h@2o3ve0Cn3ctFNyWziDK9X zTuV}aE*A3%My0Nfyc~jK2z_%pS-{fccf5?3q@p^w{IKqg&OSu{#BSx%#Y_CBOh=W) z0|gl`hUjhkG(nvTd91W_i*)+WOh9UsvL*xcl7laGe&7h1hYvzVy{)#N7fslXlbh@) zZ`{jto_^-2yw7JtZ=YU%liXRHESLTEN=vxQbfKtxMSkR;atllMB=hHD%ZQZ{M$}={ z1EcdAO;d7<<66C&ocR@|b|^7pFAr6BUAEQgyp8~s>08=e+Gn>lkvZskRH4L*I7FMO#O8_Kwbi&3@Gx zuyI_uE5CL;#L`$Sh`ahL5>@fD^~izx3XK2U(b-V8JcFXKV8Fp7H`fU}yX5J|zo8XV zaQbQwY6Kl(tNIL(F_WK`JY)y~;F?p6z>PCoh9q`_$cVG+=BKGfjRJzWf8r*JN6uf( zob0|_+I`t>RqOBks1W+b0sn3ho)G?jXIuZ@3KrUPRfnb7si_6nt_uCL*a>~Dt=ZZ2 z{bK*_>*{Mi+EJ{o&w>?b_`jpQrvAtOp)KKGrjtM4{y$+B_|MdSWj1!}Yo84`MBxNA zh#4+Eb6zc(F|$4R;wztX7hHJEne+Fw0RW5Q_P~(rIIGWwzou;71<8C<6#Mn+Yf9RF+sawZz@YrszI&Npvj0gg z{?{vidn%)%>QVkDPr`p5$%Nr+{W#Ec&KfTsPogOwiSs|-EzEhdS`Ym*XQ1vi*|NZo zt;Vv#e3kh~j%-MKw_vFnvIEyQ)j9>!z~+5x>@E?+HA4QOZr=M5dmcm)&RSeTsRJ>D zA8=L#4oyOIp?Nb%s%Q?0-mK67G;$Rw?VzM_2y{cX= z+e-EUNSKS?aoJb5(`P!h3bgP5RR z%voCtd{#T^mx7xTo8T`RBS-0})Th&{mzv_^9EwY^#z^>G-h)2}dlyeL@u$18nf0Ht z&r=~v1$@|hBaD4JPeULnC^x_QvrwQ*QIRL&>Q@qEx?|HBQ%!HnQ&b~x3FO7=qf2( z^kHf<)RJM7^i|)edy{N0DJ(-aUeKuD)lE6@>2B^*4D4stg#u1gf`K{n2f=f%l^xX#R7G*TrVOm*RghdXot?-)t`9$h0n9gNUxsHc|8 zY^UDOAAn@2W>9%6{SKeib&I($&-q7t*j5R%9H7{_LZ;xdb#Nc@YV~5B*h@8i8Phm7 zj<)=^Xipjdf;<({>8@Z3(9Hu3q>MMIlkpG6zP;XHTUW%3R%too{!pJdEmqQby4PQz zFBoK$tNncom*iJ!DiW`O9CqC-q|V^p?EK7YyVk4Nw_*o(DiT=!+Q1l_s*$wQmX`I7 zr~ITz#85NYr(kM9(ypyavRPId#McEfeF*QEodQ2E^tHrXw+{$9V6J*#{iFYrPz?8@ z*o<;QZ(J4k!NNv9Au&>#S&E*@d*&HS<7F9&t!5S}vhX;R37|NS-2zzj^#zO^G9Zdb z?x?c`)>R0>hfSXmGri~wvC1X=FtSS6e&s2}wnysNg~!sNr$bCnQt>xs`vi<4T@(%Upyx+-2RWtl znG{7)$q^r`JH?zk(_|?^>XRZcD`KmA6h5vpSkx;LZL^rk5^Dom>VT8_HhpsU5(>`_ zS=rH7qRI@M3g{fY91yYxE4ImOU8YM#ioIM4`#Mo9Lz}4ZjbnW$ zYi}*Zn#-{#s=>O!6wc$1dM?VX+8HSwGxRJ#zB%|H#4>ljV5A|k$enYFh z0gg_-*CC7@D9Y9P7N`tTQ|l>Nv)7v7EyTLLdV!nwxI)Lum(L?!Js_Oq2AF?+`Y@3A zRkPQVQK6_;F#za+yzC8A(*}JJ%_P|Zb*QA<(O80A-@BuuOTd!xTd>Bdh2#6#vBI*3 z@=|C<0dr#NZ8q`s#wSK}35?tt;*?8q5H*3=PWNkgzY^5Az_wbxnCibTN;938- z-A(6~+FIUYNTcOJH+kVfF0aQWUn<20o-pPTPUdM^&I7Ab(>a9yz1(6zZ@H9a1dc6Q zcwzuLZ>=mI$1ZhI+q)H_Gw`N-cz!s9zVXIO672^qp4d(yZh=rgk*^ypfA|+Y*et$N z?(!O6P)G1zZon%7|885WZ6fzju-`=}m)>pVNi&y7&AXIy(ff*|tvMD+Ck)kC09Nfh zM3G3aWcvq}mw*PcM9jxupz8ZnSEI5k*wka#ZTI~c7bEN5TsxPcUbU|55PHFqr7gu! z$0zW4Qlc40K2Vd#7KTvm01y*x+e&Mz5AvA$NWLAtbB=23jkbd=6ZKM3QyLsjYqlTLXtBtDj5K@aYaA&Y&8h@39W8Rf%IiCREm>D5jOm8{i3RY_L3mfr=If%Z zlgrhvu-+PsBX5I`n>CRoxy#s-pftH%T;5e0dTV{QXNyu&>-k|5Pe!4=tqh>SbnH|D z^+4E7wo|IILfZ>0l%lLUG7Ub^b) zdQ2{4h^s}7W(w?LFM`Y;Bj~mSkGnOKY+EI2^X%pp-t;7;x3+Ew=krhsGFjf0W!wXG zwih?NWL(TqCGNuH+Hoz7p&W~hBap&u0deOp&v>#H`vOlGU^3+WVSqL92$>-d=m-zv zJ0WSCI4h}mTUkwS1IZ;GSOd8dg}+YDfa#LNUOaZ<1hH0Z2yqD{Gvz07YEFq$rB(6> z)bW7Q-<<1F9S|H}Z&PyVt4#O}5<#IXHlQPu$z=Cw!(hXA3DGduiDu^eZ8X<&=o0)n zw1pa%zUP?MB^~USS}d0nOR|!uq&o0f4d0mt3d>k8HjO_lB-Lq!Yu0*56FL_gaG6e- zbw5`bn*cK*-F|Fm@witbqT-cV+)jGn<_&g#yTfDMMUhD3GdJUDMP04rzRr3N5$m^+ zMa!{GP2%NmzSI^ySgzxU?+Iry7#WySM-CE8Mo+mM2q7;}s}!vz)7m81qPt^Ou=e`>k~X1R9PDv);Kn-7BeAn6Q%u6F2W6$pI4Gg1zyoyN$o$*>d$}(j2G58a#OG1}m>@<7qL}AijBo zYa?}J$oU=00W}+XO(D)QmPjg+)`LCuUHex4SoWZA(+K+PMKAuQLGMT7d49vQ()DX| z8B@ic5*DCw8R22Rp_;fz!^nNKub3C{N&w)P{TZ*zC`or;@w-W9Cucqi13%B5ja1<- z=P+8Av(EjVN;BPPgAf+3hxS{1e*mgo^&$DUt52r#W8MUwci`TL5&q*Ti8k*zsm|w{ zbfLy|80Rr>8F@@5NH7#UY1oNx+7+t=xMh$bp+rv5qMa<)y9kqOP(D1#T5ovW=<_=3 z8PV87jI=8Qr~^r#>t4;4$i+8L`(%=CB*%z`O`8jLG=8e^HU6&KvYad+GGR7b+Q^-q zmf!Ll^K|u90I5hX4)TF>sR*eym^#WTtGj8Eu-=$QaXLMKFP&62E z5~W-^RAhMgie#X^P!C$Zz9dPISEPF6@HA}eE5(1oM-l*|jfjRjtmYtCIe1Tz96l7= zV;lfR+fIY-WuYp6m`a;%^y0BkhVOTm&l`X}O!aX2%W;=kv)A2tW2Aj|^3Q_|cwGlN zRe@f@vFX?dZ(qC!EQ0KIspQ%A+*GZS~P#4Rf$;+RMzhM-0W6B%`hf-|meW-F`mQLRPEg zE-&n(EMo5?mERe#!FNji-G1i2_hq7O#IFDFDpD(1>%Iy#RbGLZF&Vxqv2bb1XK1~1 zF{#fYmpZ|+2Y1>$cimH%DO_07EKbg4u%bpTo|`cK{2+wAqP?oV0Mi{ESeSDmA?vsW ztu3y)4UO1p;GvBZnfi9}WWH9pNGom#$KjwghrEiRkpLEH|3J8W{er7m-@XIHWoznz za*+}Irb?gn`)gE>#DTjsKe4PQjkE~8*2sdqYS1&oAjwyTvw@4;tM9G3`8QR5-n4`h zR?s>gUl7LQd6=<>f;dB}!{LsuLQH$^c|+(>5%Nc*>mIT)IM_(%dGEdmsLZrihPy+t zEcn~`8i)3@CS-^R(E--t@X|He^9r<=0Tgok)`n}GAvb0M^;F&2pdgFw`V&^UclEwC z>N;L#fkQzcwGO-PJK~_$ARpwyqM5AM0W-F73$*(AwI>g9f<}Y%0zioL6-8Sdo6=Sf1BfD~;0=-))n*lWggr&a2%Q zY30)aC3m6ykV_4MSqSu(h8t^wr$l+!9SX$tY!3tT-hnWkON;$%e#>XL&0)y52{QIUKEGH$D?O^!8>i zO#teHl?A+^Ozz(CpZQ*3RGN~H;Z1%2cC%R)28%N%AF~LK&BT@ZKG*fdsw0y5Q`t^~rQ;6DH@tv)KR)K6a zAEnkSj!(}et^2GZZ#b6tw}a(w#{}*CBWzn&ZELk6N$J}uvU5-?Im%Aw114+henWIr zQbMis%bF;U{Z~4lZ%jzmh&;=at6rtK-%Ud+t9w>@j*9KUytVR3Jb94UwG+$skVe=t zxsxY?8SI~39>eAG(tRK!lg7GRy}PpR_ZK?@Ys)8z(LV5Sa08F^3NdM8D86-GcDUwy zZ1Nbi-&Ybq0W7R3I<8ikt2+o)M8_C$`qWN(&CXiY-+lPZ;8j=adhK~P;)7uA$tdOu z>%%PnU>kI}6eeBEjfLv#dB76R(=VSc;CVwPJ~8sUzRe2K>$@G+in_;Ln9mj*J~mPM zGN=NDz{X!kR&15=aoA`Jx?&})bLLuZEZkdW_uPfYoboJtUGdRC^8K8_`kRQN;}<*l z_x1iVI<@oS1%ZHOzQXikwcdb13TuUH!#tB3?)_?a`4#t)Df9$yUVU#^xx;|I^pIG3 z;8d25Nn#a|q?e4^;cvCJS8Kc~4It9QVuAzcg+U_=6?OZ1h;bR8<09>BurFOq7Is0I zLA5pkKEjs|NB>a1y!44jan)DHAcZ3aEajt?lV|=7xxlWFp6S6MuYwFI*IrgNZ=s0T zF;S)E%XKRtv7%o>GjG*IjcX$}QTwuv*&XY^ejK@b3T?6BT9Tplr7Rp_#2!9Dad_^~ zlse^)5S!;8=6V=Lg-SFJCqa?TZ;$y7X4kejdBsO}WbIoy4LReFWyC$0yf(!Vb*Yx} zDbZSXfNnN(fE8fi@3~UOvM30W6eB|_Sr9jKi(JjwtAbF*7M_XxBlAwKZBAc%1t>x){fDF+PW5dSNk2(K$PC4 zg&unMJ`HK^BPBih4`{h+bC$jzblk?&RM;;52O1QeGGZua3r+NRm(V|iWfEOWn>v&P z$N5vjLpnr*1hprwS~)vUY#t`(H)FpS)hC-)JeIbZN2}xR`x=0s^dM$+l7vi~3T9-K zhPsF%?i{qhOqcd#`0Ck3O;CSQjaVO23Mo_ebDQQSN z%@77!rSvgj*@e|*0i5PDVa$&(^YsP1E>g=%%C}rSwrD8Wo`BCqrDlZ{)$?B6l5t$R z%?>hhLCfp8FnOwvz;{WeD2?kjVfKHf>z_4UZ_L6%)5vGap@AMWJJLc(L*wX7KV z6;GlU#Ial7rYqHj6+w1J;Q|wMRNk+B4wVi;BhYw5RzDx{<+mIcJ&T&W#UIq>aYDQ1 zZcV<+=mGBck~Xd*v8786)(6(^1LTAZVZ|s z6;8f?LzNRbJBBhz3wcII;$3Mdz5_VO9iHX>ruenSGuTCmQrW+ONGv5+U5b7e2OT|} zzlYbb+cSNkC9k6K0JJPY=(c^}PY^q9{Re2=VOqoWlch;nm9qk8{Ch2zsmwB!l|0}8 zVlpZ_W;~q^qc?UC77JGKL=`DO;%6cFzFQ-j1@ zWsZ0eb?@TYr^j5Y-fC;BQ9Fb0-VCGrCfQbX`>b*-lfHC1{0X7B`GEgyqXv!0o2=6x zoTK{G9hgZ`t6eu7t-WI`GUv6Vmh-U%ZU7Dd5K7E|ZJBJNKfpN?b&AcqtQ3BO)feXQ z*)rilcaw7%z^*;?j*(ztzSI0&u~)%4rf)BwM7_eyCf>_owKU?wjs3t8Bmz@<$VpS79 zVXRdiPLQY~UYh%vUY28(g7v?b=HuN`x;@S0{<8t^_^iXrf0(8fjX@CKEO4$j{65={_$@1 zla>qv&K$5{6Exup6nQ4WU$$HmZe$SuCtjhdq+>Vz;_u8>NRjIiNioXrCRDDKd^7jY zo_{FY$Iu~GW7s3PE*owvGp-^d8PNW2pVvn`Qjp-dk%X}lFJ-5CJ<@gGog`OYl&_w_ z$YEAeXZ_-e=m_S4c#djL|F9_Gds9`1(mJnZN|e8j4lutPef9l3z9nI@7Z?oj=U;?C zk%n@Teux77ob-T{$suP+AiC^NRin$FD@Ba~VZ}a_&qT9@Pn^64@wp`_OnMgFT8(B# zO!hhhO)HGk>V?L+0^DxX>s{rRS4Q1_ZFDrPzRdj8%sh3!lu#ddr<)ET}30E83yB7;BwiZedH_P<3Cp-IFSI|fXd3@rK zWIw4G{B|&{z>Mz9;BoFn>XH3LBeLjBApE~~7Mu3>*es5ec!inG@<6L$f<|{+L)Zl{ zUTX*MdgapJ`U%Pvou@6fy`E@f#K%hITAg$ZIa#p;h#H0@OnLgoBm)K&RKiW)a)n)~ z>U%F6KE3l8u|QMrehn1~G8KxSI%yE7JzA`Cd19zp?ZQ&aneJ$hU87lL$!L*K6;e3W zQj+GZdNVI{wd>$ax@^bAN%mqNu2x#ueJTIAg{JMc8BbJwq=OOSqdpe~b>PAhhut&{ zk*S+htdY4WgxhzXEr)7b6B0x`P#lcqsk-LQi>KnN+c`frYlptDLcC8lEA#n9JBJPg z;%jo9X;5a+s$(DHK5Hy5>ik;QA7Y?WwQ zkutc@s&s^muFFCZt_5;kF@Z}+l(|R6BIo%b3T6kqBMXvieUk>GhrBqzRBML+KCN1r zB>4H@S|zB^LC|BhQc6N%0;AQxDeg7jo8&g|aJ-(2Zp;cllqOAqVUs?7*I77BOF+1s zr?Tz26>b(>|8~uK1=74RcGwz)w< zAtllSiRJ21kuCi#m0eYOYj?Fl)!49E=W)gbjM_=WszxbrfadWkOKD2f`i&}~|6oi`la2zqqi?QuEyvx_` zi?UjX%Di+Voz0p{r>B8?Mf0)-8nT+dk> z2dDFg8tVK|L0`~=gb{q~+#|P{3Ulb!XCeNE(KXf1qxM~8G$fG7w??u(0rGXL^wLozOg^g|PiJh<^a zo~w5tkM!P&)Hm>sRB&jp>7}aEt)T=L`|=nB5qOUAl7VWRU@A-|{{8%itXuuz?V~n{ zA1?HK9|DWe`*wZsV6FPG4JSyNf04VjDW6x|WTGybLVv_9wE|jZli4k5$ga4tukaj_ zN{|kILODl;Q49L`{CsSd!wLy4G1c}KAa<`Xi`}z&6GdnLrz1)BAg@U6Iw^T)DQdut znekv>gf9cQ7J2sg@tZ6X#;4*ZDr%y_r(H`|venju+Of4Mk)YBlo6u6OIj$!gG2iHH7<~r%U4G{i{ z=^K$&JwOQg`cHr@LjUcm!G)aV`vIii!F;lnlD&MoSX%%d{iyIUpiio_Y-N<3>Q>$2 z?2!#y;H~u`;iLfDAfCd5ytCVOd9b*5yihh&ySMLnY`qWf55f}{;x)4*+Wm3TR(rIQlhq{ zL>3%AX6Pk4a)Yvu5_-Rs{>tNg_8O*uJk(En<#&T)QDUKVuB}pO4tsw} zA~M?lnJn||PS%37Dr7Xt%D>pGXLOHFhq`$Gxn9$r@I{y5A2wpM4*_(T4SZDv*N{FI zwx-|9hW*n1XkrPn>+M#qMn<&(UEat(fJ>NE9-7t5a$%2 zBFiMCo^eTqdO}K(L(ljv;8s${UC6qn_Ld4BknIHgNl2!T)L-DA!`g?fw%5mUb}9@L z(4>08ILQqa_v!`}!V17Ju*td}js;Z|O@s!bz;x zGsEOi;F$9(^gI3+x8DC?Mtq>g|DD7~|8figH1L0lIsd;6?e?$12Xw)Ilx6>FkpKP$ zaW+l`yvN+30zG}K2juoM{C{<8^fQhB8KP?D%!V*MpCPb&7Kss0J_jA6d&%kXQsCwa zRT1x&&`j){WF%*7o0`ComUZmJx9`N*BTz&S{Iv@SwM$T7$^5T|!2W9v0Hf(&Lg9bR zlmZI!U$RgA*SP-IxCUAP2>&yd3ZNkWe;U_N+*e(56vTdaQkRS1%et{ zsHj=F7Z9-9dWtZ-U^dYpjRzRI{t8ki09f=MiqenD#v@bC!tN`ct|%M^QfBC_C}%LI z?U-}LCP#JmenqBY#s8^w{8+zzHv2EQV`GT%6hcZHd1`t(a(~<$$&x`>ebN41i-#Bc#Ly42?3s# zfFQ=O$iTJ?qegJZQ~Ry03}h}cq!6z}Qu^s1qrh_n8}Bam5O=ul9Zz|-n$FJmw#Mz(tZn+v)V3xIlL4GBeGJq`w*0SS$W8PJJ=nxJRi02*Dj5mSj&~yXfc5$;VYg4{8xlq~jY zFK4m+GdpJ`(CrXLcLlrgjG(L*m59TCORC9-8r#MLKhy< z=EMsAo6pa~DWJ<^o+^5z<=3ucEXj~A%VU1In*3R@|Hqb+(KpO&8!0X@$V}2{LerdE zdyZ5n^SAU)ttQGhmHfpf3rK}GH=BfJu+mPegxnnAzDa9n4L{`J#rM_a?-h=T4vkOz zOqlz5dK)~MoI4cYnVacxwh+U=wGn9w%Qj(Kr6-%;7VLbsjD_J{8&x9k$4n1pdg&jt z64WXzU1-^c_?t(w|7vvL+~=zBW2Fi+J?;|wq|D=%#B9Df_(|YzzjYI9OoehSOtdD{ zQ0Vm*;ZF`Q^84t$)^GGqOiZ5F>Vx*;FpX_S{D0Zs0ak2GuBWXlC7LeX?y5O8@dUvd z%J(8D^wFbdk31s12HqjJ-{AOvXqn!n^=}^y`a5r_mR<~}GK|rW2Nd`ouelt%+y7oO zAXV;!0b=;~l%fjvJnd0#>yv$vv_p!rYjFaHuGW}VIpEPlFskDD7-kHj{B^8N*q|8R)0Hj z;Uc);e5&*DZhIT!mXwEAXMVo#`=*CD7<4e%CjjnlJDgnX_q$`|wUw>%R>Opffk?=(HbzruAF4{18t-nXug(T8){zGfau0a@J8CF7ZxXg9EszoSWDV z7I08NBez8Q*}GPg4Z3RH2XfeZF7YQVi&4s4r_M7F?S-hNxDptM=-k0y;CaD>MAQKp zCtIOwzZpeW8;W>_hT9%foNlacP{1kK@cp4xS>XCtcLp?mOm zc>L%!`^xssu_`xZ_YKy7DdYQU2Md@4Y$>JPZgujubLg=B=P5c$aM#d=K*itIgGs#0 zkg$?8wp|o$KjL4QR&2rs_dc)P9SZNq-z6&S;~f`)&3q0%&K`4&?p`GuEDLfv5_0LAd*TbXdbxs)$tYX8(m-`#UT zQJ z349b!Lor$GNfNWy!U^joB|>f~jK?%^><(o#V_f*A^!2{jYgB;T1z5%831cgHAL~PI z*N2S!9>YPa>AAt@{_|t(3VRkF=rcHd%W%SC2`(LTo(pgwG{c)TuIQ73a|mH>I%Zdj z*i5W)6n6J3v$k&lMd;s8)l_85g1bYQE52@tw`%$ex0Uk4y6{le`VdF<9IbGAx$~oc zxBKg?T@hte_kgw&*|wu5;${c<%d0=pCM>@Xl|#ksI{5wXpO0Wo2S*x}0|d10MP2zf z-!cmNAWENs;&i4v} zt=`m^H9xpv=@cD(gVD#B(4@dvt?50}+S;|$y_Xo?T=9l;Lj;g>ryuEc%vnuYwEkg6i08uQ-Rr-Z(>PYZ{4{<#AZ?d`e+M!uC%C=-x`t(G@7YwDYm z3CJH#eB!roUVCFrcPq_8SH>neI<|OZPjzM!YFm+Z+Y7m`x(n_fZk0w}RG((xuQs?? za_-?}H>89$LrS}zdR1{Lf6X$Op8Gq96xMuQUMi$)1hmG$uHRRxt^O>M5fV@ZTVUXt zzZNaWug#EE zexGz@RWSa0c@vWSJf|fWKoje79aX$JZ(87^hzslbHGX8e4l(Zy@TR@fL)eDom*NVT zkF|b;Go2$pNQg1H|5wdlm*)ndX`}=nmmZw?oV?ztb%}&~c$G{xS!?v&Gf|rutCRLb z#rf8KuCDx{W;6j_i+;;tS1sShzgqI$HZV;#!$Q)oRDs7e&!mFmVk=DhnU}S(*Ff~) zEf7+a5cpdqE9#G{mTTA2H@dr7OR|K{AuYSc=VIy(LY%x6i1CKRTJWIes2Lch+g@qV z@!XVLe2><;EVXW0p0I-3jS%(6>dx@;wa4C&Kv@8heQ#QV=|LjHvXDkAb&y$0Hc0p%LN@sviwcRh{v)>C(`Cj{bv@y5M`muGJU7WE z;$7_EH*@vyw%dsaww-5d&1DNJsEHA+t!l3_&H~EH^GICs%l(e~fM3&}!8EUTGDoQL zqGt+H1|19{3TaE1UYZ}b*%ae3ov1JUdKXmU#AIU0jJ>anyeC8N$Y0!52||`{pHGbL z7wzTNETrFpsg+$Cg%Ksb8Gzs<1U$l_IsT96?WvqiCI_=7;k*3{LQ`^1-slxlB&Ix3 zNcgX8kMA9Sfe!D^LvD*wAQxo68hM`UR#p^BgARh#AlU-&j*k6FsUNp*3B*jWYUO*G ztntB}jF1zT3=*@agzDg5HmU@JdLJ7=oyp4amteH$sT)j_ty$S7P7yl;Z;s-}b-Z6i zyTl^oJXSsx=`Eq!!oc3F^yfBwH^& zv1M+uUe-_0@XW^uWEa4bAl2(?@>&ijegmS-_PY5VR&Eq8ahzB2uW} zu`$H0M>AmMDn7rsl~E~qyOose41%~!mJ+HzI{BHXN5QgnsBK?`a!1-fdmU?O`4s&sv<0VS%Qu=|ODl$BEM@%aG7g z1Ng&x&aT@VMn>aDC2UJYJvwLCi-+&eZH){rnp{Z^EUGohn%R1IY3F#{Yc>1vAY+7l zuWu7%46^p=hVLemOPEl;zsBBMC3S=X2y-|Wx{TS$Eq5iZ{Wj71o*$Q@`^8XGPPzI$ zEH)OtS)Uvt-Jy1VqBSx=a@gGjW0akUvXP%G{)qYI^tsnWg^wA9y(^FXz@Ymvj7{}rL%RoL9@VFfz4bXly$7fw zLHFVMe2*ucGN84Aog1kSp>5wXC)B1Fqs;oyx%|qjYle89n4-)}9tEFERXo=0krFo6 z{(!S4=CQ@^22i~}>3R8O6-5dD=(p5Alv0e%b9+2?T9Iom0cJ;Qt&9-Ao4pRD6)9&Z zMVxEnENy;we}I3>zRm1{ctt4tAA&x*(=Wj8Rob4u-K=n0G0-^l=0dWL%}oc(29A$a zzX%SJ9!t_C$U-}(;&qpLlMXw-yY+=_u(4qOMAmQPq?gO;U0I8r*Ay+5qQ@-|aET=q zGb-hS=cl3*yJ^V2)Zf8Kz0dW0jbo!qs_y9Pkmhm4+YRSWSlKgx~FsxM2;%W4LV9a`sMr3RmrOB@|-~da4|c- zSi=#DHKz)8x^NxL#jEIqGsrje)2j@Vzr%G`YofvXu~)tZ?v(?QySUtf*V zC$EwNWRZ$a6-9rCG0@emzBx`P_EDR`$%+Ns%fduHAZ z@;^WnXzbA*OM)6)VfuKd#HEwp53F+C^;F#RVWK86)+$G%T_eA|=Z3;Y&rxKO)(fYI zgc{e9^?}^cvC(i06z3iPTT7rk=@o2vmXL^9sg0A_H0vG!Z;ccz8jb55d@L0gIZlfJ zXT&5YR%_-^e3ep_&2#g6JZFpdQo%}eE^`by%qC5ILP%q-@6)a%EwlKnNWbHbH5|Ct z?!6FbDef}OQZ!fibeYBUw%IO+(VzFbv#Ug07MIKm&191^sXbZyyP_LQJyvH-P{u{G zOUSP~?7)Eyo3Zh!X&1%7jjQjk@^Z&iM(OX~)jQq`j*P4jWIfv$+(BBFzE4!&h;jLW zl%iDLy_>u1{Fk746nH!di_|_7ut;qhn|(%sQ0i`D*FS8uCLE+M-B1S^&#t>OXIlj$ zaP(S}$30Dv_@0{m=XI%)SC?^)duh`yOV&*XYuGo{D(|QbE;h&Ze~7@bBc#Ql@QLs5 zhEm?K3|ftlyTi+a57utkf3EH}+mN`Z?6IlnIs2nwzt6MeU^}MAgu`=lFn>hj@Dtq$ zALDIJqLB(CXplD9#f9_40Ze+6fDTAWS<)*k`6!u;8yZcBLiCF6seNn2clUs0>|95; zA4M$)RmA{@7df|?tky&*SIQFV_BrtB@xf6`-Fst>>Xxm`b}V`j*om@;ielwzs>4#) z3Y$Y^9`UZ+=_kzi!J+7wkR7>2UgqhA1R%DVb=7-rx}=KZmhL0@Ky51wE0E^md3G?U?!mv(9)hkrR@xN6AB@3m-_QBd3D;eet(bPrJ2 zs6=j^oyvkn!7$u7-!>!HS$Q94>y&pVv?Y19FO1w(vd*5WSgcZvE+AQj`S=Y4c=!|f z`}XOJ9p52VqI|R)56=*X`|YDPcsC*6*EKk56@}AnPoLexdbs8 zQSYfTtV)KDJ+SR^P3Jvba{#3qLOjXA6Q7B5CaIvG0;FlJLns@l&o-n!;*;!Xfq z_KYfPZ5P8Z&gf^)=4j^U)>pkxt;oAB3pJirgWpwL%5TeBGzQYk!YpmFY&MTbFI%sI z?9FS}>PR~uKUSORJKS8UWz{>= zI8B(1W(J&*a^*ei#%Xfl7oLE~tCGlyg^RSZLv5#a6HHR7;lWOLiY}yQnp%yj#&e^jwi5#p1< zLBL70z0Jck)5ZXM>CciOXW;9^X%tUY7e;8Bw<*)RJ4j_%lBmzjL-w}bj1h2obj+ruRQeZhBl{blF>HoUy331CqKSzvUp?wJbP<;RnZI4K5{WM*5#zzM^#XmE~0Mv&1?$-s>Mx8(Bu+jk}2RndO4M; zjV>KMH42+Fua^D|2JelOZvJ=uT^Eez@@Si`&KKwDy-^fn9p_O6TCY(~NnQE(gwkTN zHajlDiN%J)B?(SRIyfDq6jdGy8xxOT1>X?2?#WR0)1E(mITa|*{`{8~$HbtQEC%=crF$1zB+%CYbw~a!-ak#LK z@VD<7J2}Ru>Z>A3cOfre!a#2PfyvZdc1n3Y(pRR!`qzdTD&kHT-pP zEQ{;np(1LL?TST&p(hq4c`~Hax#mc(V+~CypLBon4@8+%b#s%*=B~;6ss5Sa6H;ci z+4ynz0?hy5af>qz?I&PbB9P>X0%5gsRLSvvjXB1d7bNIX18dhL^z(}T6AMtKB#Y^r zPV%4pV1aMDk()i>wm|5ab)}=kxsVyuN?H^W=5^lH7N$`&##PU*~xo z$9W>v-ps}tIImh6y&+#?bL%|fZ$%XS-f+}w5Mt-JrWabRHSN#*G~&dW`n$Fk=IywesAq?J|n=h&S& zd^sbCROeJZEcyP^$7+0twHbUq4pnDR=X1Nu!bZN8G1zBLeUr=BXVE1y2(4i?{{GvX zbPx{OZ3M157|c(EsEo2sgay{66GqZCwN8S7O+&dPBuCTL9NH&#taVWHOksfo>F2h? zT|+1OnOcW`_vFrYaU27AfMd%r{DCQr)opJ6fT9|N?F#7|He;v4g4PO~szn8Ysb(R7 z5<1dEiUm()?C}w{g1{(9Lw+4${%=#Ana_p<5_xDr7DKls2T3)Uj4!m zgQt}&_V>J>{D2-QgX)Uai0iwYXlTcG8z=5>s4QxGNxb*b+lDJ8D{ijLxO_}DzlCpu zu->Ijva)k=+E?|kZ0sPRI;vVZp9Y(KjIGd=j z5$HkPJwj_eUdVQ}qUKgeI@|5Ovstrzs!od`Q#&ixJ}xTk+jOLBdzBpC=mNN|6x?1#);_IX25oA`Q1^#7|LvZZA(vw#S^>Q#9#N83A z5{v*SRb$qRn8O%Jos$B~?dlV|AZHmiWo>@p)kTZTU*0~NdXvR}RJF(AaL(+PO=*Jn zI`?^v@lDO=W{pY+)oaIGxIJ2|>0K+9Drg_tGo z#0hG^a(yeuRH%Ef=JF?Bi2`m+@9F6W%mZ7M0;<8lCYX$=AT`&nQh8Bc{B+)}Ho>YW zvuRq3>LnZ6b|T!kp86f>lTTOikETY4dVcB+Szui-e9K?yf)fsElR#4vU9-u^gYy8e zDBfC5wx+ruka{1GBKIz$q+;B1Qgb(W@S2*cVoEhV_kr)!Y+Rj02XM!PSjgt;Rk-8X2nOFb05=HtsTsyq{Ntr;J530vlgKMd8hy`&mjL$Y6c5(k(fvP0E z&7|!~GB6qyAjj)Jdr$9a-+TIle<9Caeq_3cEmb^tB}X;q7cUJvG4Q(_{0abhY5H2< zF=R}KkMfKGF_qM_)vI2cxLhFhuG-el@Htf0rD;V;%hNSr;s%@Jno<6_bV8WW$^Z51 zto?7{|BF-%MkxH>z=t!12D3mbgRu$y{W1A>ltG46g>wA|Z)jzTp=`2Ssb9=i5;pU- z%xQ2*Kh`RLbM9h(ga+*!CHFDd@VuF*BruCGHyjR9+gPx*n zj)>n59+pO-}NcQelLJy4O;qG3OU zpDavtV{RlOR=@pjx8maAItVYShh&*eqm;`6uD~;+nFQL7@7IFvp;wNyIQ>aFpW~dL zbV0zCZGKOFZ@0fzFv)?Ms$GKvEWB)nuYAy3PGH_l~%QU36F zXb3q<@5d4Zx5j-9J~wJDUtRrr!T%6?k$g_!78vP**P76fGI4=u-iGexL+bOBVGl?YX92QiB2cK_N6cT}kDBzg6#u$`C$re+i7Hf=BxRX_7LvbDFx417 zz*a}}Kfo_ui_rHKkZe1wC|D<*pkA!SH<(?)v`F!~*diYktg+R;+U^7-Y7A`Ky=4Ki862F!k}ioUER>>_|0Usgtp=TDBxX=0(D1?r#yk~7TK(f4wq z@A5dDhFRq=tclT3CeQ93kHpWzDsr!!8esBDo^D@M^PiQg4N0P;8uh=YeTANj1!}|s zVU7I4Zj0(7IMZG*G?6J-HGFF^1up~!pI(FMh%rfa{g6Ej$>O-Vsu&VwF@!G6QnJ~of9n5JNNrd1_!Ec4L3dgi zyPFw(H}U|$i_GM6p8MFHmIeuN8I_Br zliaQ~G~C~H)nG`DG_qaxa3_ljteK(Zv_gFBV=dm7I*P-#>u`5>-!34UNvmSR0-;Ol zvk7X$^7gF#)(R(k0hYckNi&iz>6#n3N^Glg{=@EtwyhZDipPlJzU~ggme%v!8O$92 z%6K&t!@)JqP9LTbb*ktA9<#_iD5g(-vnkUP^mzMd3zq?T0hV{(teGfOH>vBMtQE7&88FSbFj6=Dc!7pBYPwb_f)||-+C$jyHI{^ z%-Wm7Zx=pZzZ2RU+^}h1i-Hrm@5+E=U1w<6V=NcWWlhzt<;M$va*9LN#B5QzXt@oW zF?Hbjrfsd1M2DymwV7nB?&E2JH+9MQr*cp*z5 z%xfLyb(ja6QJA$yptQFL9yS2|rQ?lk^PH?@?%>`uZ8|uzn|FDDQj+uX#6dND3t$a( zJ+2{2UJ$AC?sZ@%$*u%`+2kr33Nh!Q?gmqUMJ83dTplsc4AzI*XCwTMa%r(Tr)^GO z33e#KPAE`npGS!_ZjbCfq?)|wQLZ7_GjJ6kZ4Rs!_CNAowFhg{!Xf|eRo%B30!%V%?kw4Z`^xptW_AC%jAofo&7+w+ zs1wXk_G~jQ`4%8BOK2+BmGJo$4Jq6-s#L~2UL2ut$2xT)yHDdOqH=+`OBWJ}aEA?d zP-j8wRRPx8$_C%QNMF_(*`@BbbStA@9hU@$YCG_#>o&O@5Wr2u_va^;Lh+G?6D3B)o87u6VU ze~45XzIk)P^@SgRoqfCzVg|bRmOmMJ8GU%-iY~~tqo{pqFx+<_9eY|G#0C#8e&#+g z!zynrSo@4wS&ZQVa-ovtGHU80>$BfmC*lXxC#*%NVm@Ov#n{PKeeb>TEQ0m1ktS?D z)CLD#3C+dG|qftwG~cLnN93<&;lv);qObd&-xUhY5Q8O7!@;wYkc36!VTr6Wzq$bCHM3!y;~=Rk;CYeX-AOdWMYb>< z*ebEJt@OdwvZ`r^MeP z%IEA3Z`^Ju*6Z`C$UJqBp zmyGr2zTV7QVduVXo2*S9)3*Fvu<_M93XW&W<{65mMapE%zp-WXQKSLr?)YrN+DBFS za%?F+;QZNh+J@h99szj;&Ur5lHdaVYs+CZmVpjnVuNKExJF?Oy#@>PxGqvf+T0bx_ ze>`Dps7MiPUa{NX@VStE;{GSUp;5lCMnO#xey*{qxk5jyU(-@psZWyKK|@4OW$9xT z*@sRqHxel${$ldG!2z!yb-{0$?f^bd)fUtPH=318^@6p;4=p%_)p_@U@?WV8+?Xr_ ztKijyux3Seq@uF`Ov5>pORQAIgOG9aM&H>Kb8u;0vfK}q&^E6?Buj5TNxap4q>K-r2W{2^pk_uoOeYS; z33BcuMlY?Ucbe@&N{nZQZC05-xpt3nxco|snW{LUbpo_v50_|^M4{x=B8{l1AmBfG z)_>M)s-=wV6hBg?MdhfQIL6m0ExUM6A`)o$=!56AsOj~yfGK8OW8OIuI4K>OV2re${K? z8+;Y|xaaCQG50^&Kqzz5dRI&<-FTKChf`>`aO2_}a{O&G7cu!vu_X)hxkq@V3`Oe%A!^@y&+tZwY5UO94yM?&4zPwl6wJ(V6I>jT2&;7R4=_0v1uA72Cn3w9 zdFIT8@t`ki~HhlImfT2JAxhWmHzBKrKGTaO+L43KFqUGtHd*nH=^5Gn&h7tX z$XoMgQ@$B3%7tv}hT*$iUPlXTBY61SoL8?{Ah^|Xk6>9F^Lo&f)Jc`~jSTuBwa14| z=^L`G=X_Q`q4{btYHs}rdG?OA@EHeL;>$}IzxJd+hAKpj3aT}yS2fzNs!3H%0DcT~ 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 literal 0 HcmV?d00001 diff --git a/docs/en/Tutorials/images/bookstore-new-author-modal.png b/docs/en/Tutorials/images/bookstore-new-author-modal.png new file mode 100644 index 0000000000000000000000000000000000000000..dda0dab4c13e597243de58b72063808a20e6f49e GIT binary patch literal 63907 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 qONvV>cAqR}`Pxq~GBNp%-ASYW(V Date: Wed, 22 Jul 2020 00:05:11 +0300 Subject: [PATCH 29/77] Added the part 10. --- docs/en/Tutorials/Part-1.md | 6 +- docs/en/Tutorials/Part-10.md | 231 +++++++++++++++++++++++++++++++++++ docs/en/Tutorials/Part-2.md | 1 + docs/en/Tutorials/Part-3.md | 1 + docs/en/Tutorials/Part-4.md | 1 + docs/en/Tutorials/Part-5.md | 1 + docs/en/Tutorials/Part-6.md | 1 + docs/en/Tutorials/Part-7.md | 1 + docs/en/Tutorials/Part-8.md | 43 ++++--- docs/en/Tutorials/Part-9.md | 9 +- 10 files changed, 274 insertions(+), 21 deletions(-) create mode 100644 docs/en/Tutorials/Part-10.md diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index 3028e88931..f7a02725f0 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code @@ -231,11 +232,6 @@ namespace Acme.BookStore } public async Task SeedAsync(DataSeedContext context) - { - await CreateBooksAsync(); - } - - private async Task CreateBooksAsync() { if (await _bookRepository.GetCountAsync() > 0) { diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md new file mode 100644 index 0000000000..da2ba59bcb --- /dev/null +++ b/docs/en/Tutorials/Part-10.md @@ -0,0 +1,231 @@ +# Web Application Development Tutorial - Part 10: Book to Author Relation +````json +//[doc-params] +{ + "UI": ["MVC","NG"], + "DB": ["EF","Mongo"] +} +```` +{{ +if UI == "MVC" + UI_Text="mvc" +else if UI == "NG" + UI_Text="angular" +else + UI_Text="?" +end +if DB == "EF" + DB_Text="Entity Framework Core" +else if DB == "Mongo" + DB_Text="MongoDB" +else + DB_Text="?" +end +}} + +## About This Tutorial + +In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies: + +* **{{DB_Text}}** as the ORM provider. +* **{{UI_Value}}** as the UI Framework. + +This tutorial is organized as the following parts; + +- [Part 1: Creating the server side](Part-1.md) +- [Part 2: The book list page](Part-2.md) +- [Part 3: Creating, updating and deleting books](Part-3.md) +- [Part 4: Integration tests](Part-4.md) +- [Part 5: Authorization](Part-5.md) +- [Part 6: Authors: Domain layer](Part-6.md) +- [Part 7: Authors: Database Integration](Part-7.md) +- [Part 8: Authors: Application Layer](Part-8.md) +- [Part 9: Authors: User Interface](Part-9.md) +- **Part 10: Book to Author Relation (this part)** + +### Download the Source Code + +This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded: + +* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +## Introduction + +We have created `Book` and `Author` functionalities for the book store application. However, currently there is no relation between these entities. + +In this tutorial, we will establish a **1 to N** relation between the `Book` and the `Author`. + +## Add Relation to The Book Entity + +Open the `Books/Book.cs` in the `Acme.BookStore.Domain` project and add the following property to the `Book` entity: + +````csharp +public Guid AuthorId { get; set; } +```` + +## Database Migration + +Added a new, required `AuthorId` property to the `Book` entity. But, what about the existing books on the database? They currently don't have `AuthorId`s and this will be a problem when we try to run the application. + +This is a typical migration problem and the decision depends on your case; + +* If you haven't published your application to the production yet, you can just delete existing books in the database, or you can even delete the entire database in your development environment. +* You can do it programmatically on data migration or seed phase. +* You can manually handle it on the database. + +We prefer to drop the database (run the `Drop-Database` in the *Package Manager Console*) since this is just an example project and data loss is not important. Since this topic is not related to the ABP Framework, we don't go deeper for all scenarios. + +{{if DB=="EF"}} + +### Update the EF Core Mapping + +Open the `BookStoreDbContextModelCreatingExtensions` class under the `EntityFrameworkCore` folder of the `Acme.BookStore.EntityFrameworkCore` project and change the `builder.Entity` part as shown below: + +````csharp +builder.Entity(b => +{ + b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema); + b.ConfigureByConvention(); //auto configure for the base class props + b.Property(x => x.Name).IsRequired().HasMaxLength(128); + + // ADD THE MAPPING FOR THE RELATION + b.HasOne().WithMany().HasForeignKey(x => x.AuthorId).IsRequired(); +}); +```` + +### Add New EF Core Migration + +Run the following command in the Package Manager Console (of the Visual Studio) to add a new database migration: + +````bash +Add-Migration "Added_AuthorId_To_Book" +```` + +This should create a new migration class with the following code in its `Up` method: + +````csharp +migrationBuilder.AddColumn( + name: "AuthorId", + table: "AppBooks", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + +migrationBuilder.CreateIndex( + name: "IX_AppBooks_AuthorId", + table: "AppBooks", + column: "AuthorId"); + +migrationBuilder.AddForeignKey( + name: "FK_AppBooks_AppAuthors_AuthorId", + table: "AppBooks", + column: "AuthorId", + principalTable: "AppAuthors", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); +```` + +* Adds an `AuthorId` field to the `AppBooks` table. +* Creates an index on the `AuthorId` field. +* Declares the foreign key to the `AppAuthors` table. + +{{end}} + +## Change the Data Seeder + +Since the `AuthorId` is a required property of the `Book` entity, current data seeder code can not work. Open the `BookStoreDataSeederContributor` in the `Acme.BookStore.Domain` project and change as the following: + +````csharp +using System; +using System.Threading.Tasks; +using Acme.BookStore.Authors; +using Acme.BookStore.Books; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore +{ + public class BookStoreDataSeederContributor + : IDataSeedContributor, ITransientDependency + { + private readonly IRepository _bookRepository; + private readonly IAuthorRepository _authorRepository; + private readonly AuthorManager _authorManager; + + public BookStoreDataSeederContributor( + IRepository bookRepository, + IAuthorRepository authorRepository, + AuthorManager authorManager) + { + _bookRepository = bookRepository; + _authorRepository = authorRepository; + _authorManager = authorManager; + } + + public async Task SeedAsync(DataSeedContext context) + { + if (await _bookRepository.GetCountAsync() > 0) + { + return; + } + + var orwell = await _authorRepository.InsertAsync( + await _authorManager.CreateAsync( + "George Orwell", + new DateTime(1903, 06, 25), + "Orwell produced literary criticism and poetry, fiction and polemical journalism; and is best known for the allegorical novella Animal Farm (1945) and the dystopian novel Nineteen Eighty-Four (1949)." + ) + ); + + var douglas = await _authorRepository.InsertAsync( + await _authorManager.CreateAsync( + "Douglas Adams", + new DateTime(1952, 03, 11), + "Douglas Adams was an English author, screenwriter, essayist, humorist, satirist and dramatist. Adams was an advocate for environmentalism and conservation, a lover of fast cars, technological innovation and the Apple Macintosh, and a self-proclaimed 'radical atheist'." + ) + ); + + await _bookRepository.InsertAsync( + new Book + { + AuthorId = orwell.Id, // SET THE AUTHOR + Name = "1984", + Type = BookType.Dystopia, + PublishDate = new DateTime(1949, 6, 8), + Price = 19.84f + }, + autoSave: true + ); + + await _bookRepository.InsertAsync( + new Book + { + AuthorId = douglas.Id, // SET THE AUTHOR + Name = "The Hitchhiker's Guide to the Galaxy", + Type = BookType.ScienceFiction, + PublishDate = new DateTime(1995, 9, 27), + Price = 42.0f + }, + autoSave: true + ); + } + } +} +```` + +The only change is that we set the `AuthorId` properties of the `Book` entities. + +{{if DB=="EF"}} + +You can now run the `.DbMigrator` console application to **migrate** the **database schema** and **seed** the initial data. + +{{else if DB="Mongo"}} + +You can now run the `.DbMigrator` console application to **seed** the initial data. + +{{end}} + +## Application Layer + +TODO \ No newline at end of file diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index d6ae9e33c9..8db6986ae6 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index d3dcc4ab2a..fc8e724597 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-4.md b/docs/en/Tutorials/Part-4.md index 7c20975005..1f27e1f6a5 100644 --- a/docs/en/Tutorials/Part-4.md +++ b/docs/en/Tutorials/Part-4.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-5.md b/docs/en/Tutorials/Part-5.md index d18e22d7d8..6fc760f66a 100644 --- a/docs/en/Tutorials/Part-5.md +++ b/docs/en/Tutorials/Part-5.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index 96c94a2f2e..6ee16bb1b4 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md index 95bdf553b4..dcfe2453ad 100644 --- a/docs/en/Tutorials/Part-7.md +++ b/docs/en/Tutorials/Part-7.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - **Part 7: Authors: Database Integration (this part)** - [Part 8: Authors: Application Layer](Part-8.md) - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code diff --git a/docs/en/Tutorials/Part-8.md b/docs/en/Tutorials/Part-8.md index 59ec6d8b3d..89a8c58e01 100644 --- a/docs/en/Tutorials/Part-8.md +++ b/docs/en/Tutorials/Part-8.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - **Part 8: Author: Application Layer (this part)** - [Part 9: Authors: User Interface](Part-9.md) +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code @@ -406,7 +407,7 @@ CreateMap(); As just done for the books before, it would be good to have some initial author entities in the database. This will be good while running the application first time, but also it is very useful for the automated tests. -Open the `BookStoreDataSeederContributor` in the `Acme.BookStore.Domain` project and add a new `CreateAuthorsAsync` method as shown below: +Open the `BookStoreDataSeederContributor` in the `Acme.BookStore.Domain` project and change the file content with the code below: ````csharp using System; @@ -438,23 +439,18 @@ namespace Acme.BookStore public async Task SeedAsync(DataSeedContext context) { - await CreateAuthorsAsync(); // CALL the NEW METHOD - await CreateBooksAsync(); - } - - // ADDED a NEW METHOD - private async Task CreateAuthorsAsync() - { - if (await _authorRepository.GetCountAsync() > 0) + if (await _bookRepository.GetCountAsync() > 0) { return; } + // ADDED SEED DATA FOR AUTHORS + await _authorRepository.InsertAsync( await _authorManager.CreateAsync( "George Orwell", new DateTime(1903, 06, 25), - "Orwell produced literary criticism and poetry..." + "Orwell produced literary criticism and poetry, fiction and polemical journalism; and is best known for the allegorical novella Animal Farm (1945) and the dystopian novel Nineteen Eighty-Four (1949)." ) ); @@ -462,14 +458,31 @@ namespace Acme.BookStore await _authorManager.CreateAsync( "Douglas Adams", new DateTime(1952, 03, 11), - "Douglas Adams was an English author, screenwriter..." + "Douglas Adams was an English author, screenwriter, essayist, humorist, satirist and dramatist. Adams was an advocate for environmentalism and conservation, a lover of fast cars, technological innovation and the Apple Macintosh, and a self-proclaimed 'radical atheist'." ) ); - } - private async Task CreateBooksAsync() - { - //...omitted the code + await _bookRepository.InsertAsync( + new Book + { + Name = "1984", + Type = BookType.Dystopia, + PublishDate = new DateTime(1949, 6, 8), + Price = 19.84f + }, + autoSave: true + ); + + await _bookRepository.InsertAsync( + new Book + { + Name = "The Hitchhiker's Guide to the Galaxy", + Type = BookType.ScienceFiction, + PublishDate = new DateTime(1995, 9, 27), + Price = 42.0f + }, + autoSave: true + ); } } } diff --git a/docs/en/Tutorials/Part-9.md b/docs/en/Tutorials/Part-9.md index 7c6db34bb9..147de367c4 100644 --- a/docs/en/Tutorials/Part-9.md +++ b/docs/en/Tutorials/Part-9.md @@ -41,6 +41,7 @@ This tutorial is organized as the following parts; - [Part 7: Authors: Database Integration](Part-7.md) - [Part 8: Authors: Application Layer](Part-8.md) - **Part 9: Authors: User Interface (this part)** +- [Part 10: Book to Author Relation](Part-10.md) ### Download the Source Code @@ -258,6 +259,8 @@ As you see, the admin role has no *Author Management* permissions yet. Click to The page is fully working except *New author* and *Actions/Edit* since we haven't implemented them yet. +> **Tip**: If you run the `.DbMigrator` console application after defining a new permission, it automatically grants these new permissions to the admin role and you don't need to manually grant the permissions yourself. + ## Create Modal Create a new razor page, `CreateModal.cshtml` under the `Pages/Authors` folder of the `Acme.BookStore.Web` project and change the content as given below. @@ -508,4 +511,8 @@ That's all! You can run the application and try to edit an author. TODO: Angular UI is being prepared... -{{end}} \ No newline at end of file +{{end}} + +## The Next Part + +See the [next part](part-10.md) of this tutorial. \ No newline at end of file From 611e3808939c2a8feafca366f2ec02358720c2a7 Mon Sep 17 00:00:00 2001 From: shaoxiaoxu Date: Wed, 22 Jul 2020 17:31:06 +0800 Subject: [PATCH 30/77] feat: add TemporaryCredentialsCacheKey --- docs/zh-Hans/Blob-Storing-Aliyun.md | 6 +- .../Aliyun/AliyunBlobProviderConfiguration.cs | 21 ++-- .../AliyunBlobProviderConfigurationNames.cs | 4 +- ...=> AliyunTemporaryCredentialsCacheItem.cs} | 7 +- .../Aliyun/DefaultOssClientFactory.cs | 104 ++++++++++++------ 5 files changed, 92 insertions(+), 50 deletions(-) rename framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/{AssumeRoleCredentialsCacheItem.cs => AliyunTemporaryCredentialsCacheItem.cs} (66%) diff --git a/docs/zh-Hans/Blob-Storing-Aliyun.md b/docs/zh-Hans/Blob-Storing-Aliyun.md index adeb902830..ba66d55104 100644 --- a/docs/zh-Hans/Blob-Storing-Aliyun.md +++ b/docs/zh-Hans/Blob-Storing-Aliyun.md @@ -46,16 +46,18 @@ Configure(options => * **AccessKeyId** ([NotNull]string): 云账号AccessKey是访问阿里云API的密钥,具有该账户完全的权限,请你务必妥善保管!强烈建议遵循[阿里云安全最佳实践](https://help.aliyun.com/document_detail/102600.html),使用RAM子用户AccessKey来进行API调用. * **AccessKeySecret** ([NotNull]string): 同上. * **Endpoint** ([NotNull]string): Endpoint表示OSS对外服务的访问域名. [访问域名和数据中心](https://help.aliyun.com/document_detail/31837.html) +* **UseSecurityTokenService** (bool): 是否使用STS临时授权访问OSS,默认false. [STS临时授权访问OSS](https://help.aliyun.com/document_detail/100624.html) * **RegionId** (string): STS服务的接入地址,每个地址的功能都相同,请尽量在同地域进行调用. [接入地址](https://help.aliyun.com/document_detail/66053.html) -* **RoleArn** ([NotNull]string): STS所需角色ARN. [STS临时授权访问OSS](https://help.aliyun.com/document_detail/100624.html) +* **RoleArn** ([NotNull]string): STS所需角色ARN. * **RoleSessionName** ([NotNull]string): 用来标识临时访问凭证的名称,建议使用不同的应用程序用户来区分. * **Policy** (string): 在扮演角色的时候额外添加的权限限制. 请参见[基于RAM Policy的权限控制](https://help.aliyun.com/document_detail/100680.html). -* **DurationSeconds** (int): 设置临时访问凭证的有效期,单位是s,最小为900,最大为3600. **注**:为0则使用子账号操作OSS. +* **DurationSeconds** (int): 设置临时访问凭证的有效期,单位是s,最小为900,最大为3600. * **ContainerName** (string): 你可以在aliyun中指定容器名称. 如果没有指定它将使用 `BlogContainerName` 属性定义的BLOB容器的名称(请参阅[BLOB存储文档](Blob-Storing.md)). 请注意Aliyun有一些**命名容器的规则**,容器名称必须是有效的DNS名称,[符合以下命名规则](https://help.aliyun.com/knowledge_detail/39668.html): * 只能包含小写字母,数字和短横线(-) * 必须以小写字母和数字开头和结尾 * Bucket名称的长度限制在**3**到**63**个字符之间 * **CreateContainerIfNotExists** (bool): 默认值为 `false`, 如果aliyun中不存在容器, `AliyunBlobProvider` 将尝试创建它. +* **TemporaryCredentialsCacheKey** (bool): STS凭证缓存Key,默认Guid.NewGuid().ToString("N"). ## Aliyun BLOB 名称计算器 diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs index 058f65412c..757ad4b54c 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs @@ -25,6 +25,12 @@ namespace Volo.Abp.BlobStoring.Aliyun set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.Endpoint, Check.NotNullOrWhiteSpace(value, nameof(value))); } + public bool UseSecurityTokenService + { + get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.UseSecurityTokenService, false); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.UseSecurityTokenService, value); + } + public string RegionId { get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.RegionId); @@ -88,18 +94,19 @@ namespace Volo.Abp.BlobStoring.Aliyun set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.CreateContainerIfNotExists, value); } - private readonly BlobContainerConfiguration _containerConfiguration; - - public AliyunBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) + private readonly string _temporaryCredentialsCacheKey; + public string TemporaryCredentialsCacheKey { - _containerConfiguration = containerConfiguration; + get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.TemporaryCredentialsCacheKey, _temporaryCredentialsCacheKey); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.TemporaryCredentialsCacheKey, value); } + private readonly BlobContainerConfiguration _containerConfiguration; - public string ToKeyString() + public AliyunBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) { - Uri uPoint = new Uri(Endpoint); - return $"blobstoring:aliyun:id:{AccessKeyId},sec:{AccessKeySecret},ept:{uPoint.Host.ToLower()},rid:{RegionId},ra:{RoleArn},rsn:{RoleSessionName},pl:{Policy}"; + _containerConfiguration = containerConfiguration; + _temporaryCredentialsCacheKey = Guid.NewGuid().ToString("N"); } } } diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs index c889b374cc..0f8585f12b 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs @@ -5,12 +5,14 @@ public const string AccessKeyId = "Aliyun.AccessKeyId"; public const string AccessKeySecret = "Aliyun.AccessKeySecret"; public const string Endpoint = "Aliyun.Endpoint"; + public const string UseSecurityTokenService = "Aliyun.UseSecurityTokenService"; public const string RegionId = "Aliyun.RegionId"; public const string RoleArn = "Aliyun.RoleArn"; public const string RoleSessionName = "Aliyun.RoleSessionName"; public const string DurationSeconds = "Aliyun.DurationSeconds"; public const string Policy = "Aliyun.Policy"; - public const string ContainerName = "Aliyun:ContainerName"; + public const string ContainerName = "Aliyun.ContainerName"; public const string CreateContainerIfNotExists = "Aliyun.CreateContainerIfNotExists"; + public const string TemporaryCredentialsCacheKey = "Aliyun.TemporaryCredentialsCacheKey"; } } diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AssumeRoleCredentialsCacheItem.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunTemporaryCredentialsCacheItem.cs similarity index 66% rename from framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AssumeRoleCredentialsCacheItem.cs rename to framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunTemporaryCredentialsCacheItem.cs index e0d75f2954..508d94b743 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AssumeRoleCredentialsCacheItem.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunTemporaryCredentialsCacheItem.cs @@ -6,8 +6,7 @@ using Volo.Abp.Caching; namespace Volo.Abp.BlobStoring.Aliyun { [Serializable] - [CacheName("AssumeRoleCredentials")] - public class AssumeRoleCredentialsCacheItem + public class AliyunTemporaryCredentialsCacheItem { public string AccessKeyId { get; set; } @@ -15,12 +14,12 @@ namespace Volo.Abp.BlobStoring.Aliyun public string SecurityToken { get; set; } - public AssumeRoleCredentialsCacheItem() + public AliyunTemporaryCredentialsCacheItem() { } - public AssumeRoleCredentialsCacheItem(string accessKeyId,string accessKeySecret,string securityToken) + public AliyunTemporaryCredentialsCacheItem(string accessKeyId,string accessKeySecret,string securityToken) { AccessKeyId = accessKeyId; AccessKeySecret = accessKeySecret; diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs index 8f69603f7c..ea0c4cbf28 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs @@ -6,8 +6,11 @@ using Aliyun.OSS; using Microsoft.Extensions.Caching.Distributed; using System; using System.Collections.Generic; +using System.Threading.Tasks; using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; +using Volo.Abp.Security.Encryption; +using static Aliyun.Acs.Core.Auth.Sts.AssumeRoleResponse; namespace Volo.Abp.BlobStoring.Aliyun { @@ -16,53 +19,82 @@ namespace Volo.Abp.BlobStoring.Aliyun /// public class DefaultOssClientFactory : IOssClientFactory, ITransientDependency { - protected IDistributedCache Cache { get; } + protected IDistributedCache Cache { get; } + + protected IStringEncryptionService StringEncryptionService { get; } + public DefaultOssClientFactory( - IDistributedCache cache) + IDistributedCache cache, + IStringEncryptionService stringEncryptionService) { Cache = cache; + StringEncryptionService = stringEncryptionService; } - public virtual IOss Create(AliyunBlobProviderConfiguration aliyunConfig) + public virtual IOss Create(AliyunBlobProviderConfiguration configuration) { - //Sub-account - if (aliyunConfig.DurationSeconds <= 0) + Check.NotNullOrWhiteSpace(configuration.AccessKeyId, nameof(configuration.AccessKeyId)); + Check.NotNullOrWhiteSpace(configuration.AccessKeySecret, nameof(configuration.AccessKeySecret)); + Check.NotNullOrWhiteSpace(configuration.Endpoint, nameof(configuration.Endpoint)); + if (configuration.UseSecurityTokenService) { - return new OssClient(aliyunConfig.Endpoint, aliyunConfig.AccessKeyId, aliyunConfig.AccessKeySecret); + //STS temporary authorization to access OSS + return GetSecurityTokenClient(configuration); } - else + //Sub-account + return new OssClient(configuration.Endpoint, configuration.AccessKeyId, configuration.AccessKeySecret); + } + + protected virtual IOss GetSecurityTokenClient(AliyunBlobProviderConfiguration configuration) + { + Check.NotNullOrWhiteSpace(configuration.RoleArn, nameof(configuration.RoleArn)); + Check.NotNullOrWhiteSpace(configuration.RoleSessionName, nameof(configuration.RoleSessionName)); + var cacheItem = Cache.Get(configuration.TemporaryCredentialsCacheKey); + if (cacheItem == null) { - //STS temporary authorization to access OSS - var key = aliyunConfig.ToKeyString(); - var cacheItem = Cache.Get(key); - if (cacheItem == null) + IClientProfile profile = DefaultProfile.GetProfile( + configuration.RegionId, + configuration.AccessKeyId, + configuration.AccessKeySecret); + DefaultAcsClient client = new DefaultAcsClient(profile); + AssumeRoleRequest request = new AssumeRoleRequest { - IClientProfile profile = DefaultProfile.GetProfile( - aliyunConfig.RegionId, - aliyunConfig.AccessKeyId, - aliyunConfig.AccessKeySecret); - DefaultAcsClient client = new DefaultAcsClient(profile); - AssumeRoleRequest request = new AssumeRoleRequest - { - AcceptFormat = FormatType.JSON, - //eg:acs:ram::$accountID:role/$roleName - RoleArn = aliyunConfig.RoleArn, - RoleSessionName = aliyunConfig.RoleSessionName, - //Set the validity period of the temporary access credential, the unit is s, the minimum is 900, and the maximum is 3600. default 3600 - DurationSeconds = aliyunConfig.DurationSeconds, - //Set additional permission policy of Token; when acquiring Token, further reduce the permission of Token by setting an additional permission policy - Policy = aliyunConfig.Policy.IsNullOrEmpty() ? null : aliyunConfig.Policy, - }; - var response = client.GetAcsResponse(request); - cacheItem = new AssumeRoleCredentialsCacheItem(response.Credentials.AccessKeyId, response.Credentials.AccessKeySecret, response.Credentials.SecurityToken); - Cache.Set(key, cacheItem, new DistributedCacheEntryOptions() - { - //Subtract 10 seconds of network request time. - AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(aliyunConfig.DurationSeconds - 10) - }); - } - return new OssClient(aliyunConfig.Endpoint, cacheItem.AccessKeyId, cacheItem.AccessKeySecret, cacheItem.SecurityToken); + AcceptFormat = FormatType.JSON, + //eg:acs:ram::$accountID:role/$roleName + RoleArn = configuration.RoleArn, + RoleSessionName = configuration.RoleSessionName, + //Set the validity period of the temporary access credential, the unit is s, the minimum is 900, and the maximum is 3600. default 3600 + DurationSeconds = configuration.DurationSeconds, + //Set additional permission policy of Token; when acquiring Token, further reduce the permission of Token by setting an additional permission policy + Policy = configuration.Policy.IsNullOrEmpty() ? null : configuration.Policy, + }; + var response = client.GetAcsResponse(request); + cacheItem = SetTemporaryCredentialsCache(configuration, response.Credentials); } + return new OssClient( + configuration.Endpoint, + StringEncryptionService.Decrypt(cacheItem.AccessKeyId), + StringEncryptionService.Decrypt(cacheItem.AccessKeySecret), + StringEncryptionService.Decrypt(cacheItem.SecurityToken)); + } + + private AliyunTemporaryCredentialsCacheItem SetTemporaryCredentialsCache( + AliyunBlobProviderConfiguration configuration, + AssumeRole_Credentials credentials) + { + var temporaryCredentialsCache = new AliyunTemporaryCredentialsCacheItem( + StringEncryptionService.Encrypt(credentials.AccessKeyId), + StringEncryptionService.Encrypt(credentials.AccessKeySecret), + StringEncryptionService.Encrypt(credentials.SecurityToken)); + + Cache.Set(configuration.TemporaryCredentialsCacheKey, temporaryCredentialsCache, + new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(configuration.DurationSeconds - 10) + }); + + return temporaryCredentialsCache; } + } } From 5bc83ddbbc311fff47c3787fe5ea0ae699c22fae Mon Sep 17 00:00:00 2001 From: cherie <1134196982@qq.com> Date: Wed, 22 Jul 2020 21:20:52 +0800 Subject: [PATCH 31/77] feat: add TemporaryCredentialsCacheKey --- .../Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs index 1635e3390f..b08737eba5 100644 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs @@ -50,14 +50,16 @@ namespace Volo.Abp.BlobStoring.Aliyun aliyun.AccessKeySecret = accessKeySecret; aliyun.Endpoint = endpoint; //STS + aliyun.UseSecurityTokenService = true; aliyun.RegionId = regionId; aliyun.RoleArn = roleArn; aliyun.RoleSessionName = Guid.NewGuid().ToString("N"); - aliyun.DurationSeconds = 0; + aliyun.DurationSeconds = 900; aliyun.Policy = String.Empty; //Other aliyun.CreateContainerIfNotExists = true; aliyun.ContainerName = _randomContainerName; + aliyun.TemporaryCredentialsCacheKey = "297A96094D7048DBB2C28C3FDB20839A"; _configuration = aliyun; }); }); From b794eae22fc3b2d81b70ed1b2d0ea9c964e1bc9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 22 Jul 2020 19:08:26 +0300 Subject: [PATCH 32/77] Update Part-10.md --- docs/en/Tutorials/Part-10.md | 222 ++++++++++++++++++++++++++++++++++- 1 file changed, 221 insertions(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md index da2ba59bcb..6b934bacb8 100644 --- a/docs/en/Tutorials/Part-10.md +++ b/docs/en/Tutorials/Part-10.md @@ -228,4 +228,224 @@ You can now run the `.DbMigrator` console application to **seed** the initial da ## Application Layer -TODO \ No newline at end of file +We will change the `BookAppService` to support the Author relation. + +### Data Transfer Objects + +Let's begin from the DTOs. + +#### BookDto + +Open the `BookDto` class in the `Books` folder of the `Acme.BookStore.Application.Contracts` project and add the following properties: + +```csharp +public Guid AuthorId { get; set; } +public string AuthorName { get; set; } +``` + +The final `BookDto` class should be following: + +```csharp +using System; +using Volo.Abp.Application.Dtos; + +namespace Acme.BookStore.Books +{ + public class BookDto : AuditedEntityDto + { + public Guid AuthorId { get; set; } + + public string AuthorName { get; set; } + + public string Name { get; set; } + + public BookType Type { get; set; } + + public DateTime PublishDate { get; set; } + + public float Price { get; set; } + } +} +``` + +#### CreateUpdateBookDto + +Open the `CreateUpdateBookDto` class in the `Books` folder of the `Acme.BookStore.Application.Contracts` project and add an `AuthorId` property as shown: + +````csharp +public Guid AuthorId { get; set; } +```` + +#### AuthorLookupDto + +Create a new class, `AuthorLookupDto`, inside the `Books` folder of the `Acme.BookStore.Application.Contracts` project: + +````csharp +using System; +using Volo.Abp.Application.Dtos; + +namespace Acme.BookStore.Books +{ + public class AuthorLookupDto : EntityDto + { + public string Name { get; set; } + } +} +```` + +This will be used in a new method will be added to the `IBookAppService`. + +### IBookAppService + +Open the `IBookAppService` interface in the `Books` folder of the `Acme.BookStore.Application.Contracts` project and add a new method, named `GetAuthorLookupAsync`, as shown below: + +````csharp +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Acme.BookStore.Books +{ + public interface IBookAppService : + ICrudAppService< //Defines CRUD methods + BookDto, //Used to show books + Guid, //Primary key of the book entity + PagedAndSortedResultRequestDto, //Used for paging/sorting + CreateUpdateBookDto> //Used to create/update a book + { + // ADD the NEW METHOD + Task> GetAuthorLookupAsync(); + } +} +```` + +This new method will be used from the UI to get a list of authors and fill a dropdown list to select the author of a book. + +### BookAppService + +Open the `BookAppService` interface in the `Books` folder of the `Acme.BookStore.Application` project and replace the file content with the following code: + +```csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Acme.BookStore.Authors; +using Acme.BookStore.Permissions; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore.Books +{ + [Authorize(BookStorePermissions.Books.Default)] + public class BookAppService : + CrudAppService< + Book, //The Book entity + BookDto, //Used to show books + Guid, //Primary key of the book entity + PagedAndSortedResultRequestDto, //Used for paging/sorting + CreateUpdateBookDto>, //Used to create/update a book + IBookAppService //implement the IBookAppService + { + private readonly IAuthorRepository _authorRepository; + + public BookAppService( + IRepository repository, + IAuthorRepository authorRepository) + : base(repository) + { + _authorRepository = authorRepository; + GetPolicyName = BookStorePermissions.Books.Default; + GetListPolicyName = BookStorePermissions.Books.Default; + CreatePolicyName = BookStorePermissions.Books.Create; + UpdatePolicyName = BookStorePermissions.Books.Edit; + DeletePolicyName = BookStorePermissions.Books.Create; + } + + public override async Task GetAsync(Guid id) + { + //Prepare a query to join books and authors + var query = from book in Repository + join author in _authorRepository on book.AuthorId equals author.Id + where book.Id == id + select new { book, author }; + + //Execute the query and get the book with author + var queryResult = await AsyncExecuter.FirstOrDefaultAsync(query); + if (queryResult == null) + { + throw new EntityNotFoundException(typeof(Book), id); + } + + var bookDto = ObjectMapper.Map(queryResult.book); + bookDto.AuthorName = queryResult.author.Name; + return bookDto; + } + + public override async Task> + GetListAsync(PagedAndSortedResultRequestDto input) + { + //Prepare a query to join books and authors + var query = from book in Repository + join author in _authorRepository on book.AuthorId equals author.Id + orderby input.Sorting + select new {book, author}; + + query = query + .Skip(input.SkipCount) + .Take(input.MaxResultCount); + + //Execute the query and get a list + var queryResult = await AsyncExecuter.ToListAsync(query); + + //Convert the query result to a list of BookDto objects + var bookDtos = queryResult.Select(x => + { + var bookDto = ObjectMapper.Map(x.book); + bookDto.AuthorName = x.author.Name; + return bookDto; + }).ToList(); + + //Get the total count with another query + var totalCount = await Repository.GetCountAsync(); + + return new PagedResultDto( + totalCount, + bookDtos + ); + } + + public async Task> GetAuthorLookupAsync() + { + var authors = await _authorRepository.GetListAsync(); + + return new ListResultDto( + ObjectMapper.Map, List>(authors) + ); + } + } +} +``` + +Let's see the changes we've done: + +* Added `[Authorize(BookStorePermissions.Books.Default)]` to authorize the methods we've newly added/overrode (remember, authorize attribute is valid for all the methods of the class when it is declared for a class). +* Injected `IAuthorRepository` to query from the authors. +* Overrode the `GetAsync` method of the base `CrudAppService`, which returns a single `BookDto` object with the given `id`. + * Used a simple LINQ expression to join books and authors and query them together for the given book id. + * Used `AsyncExecuter.FirstOrDefaultAsync(...)` to execute the query and get a result. `AsyncExecuter` was previously used in the `AuthorAppService`. Check the [repository documentation](../Repositories.md) to understand why we've used it. + * Throws an `EntityNotFoundException` which results an `HTTP 404` (not found) result if requested book was not present in the database. + * Finally, created a `BookDto` object using the `ObjectMapper`, then assigning the `AuthorName` manually. +* Overrode the `GetListAsync` method of the base `CrudAppService`, which returns a list of books. The logic is similar to the previous method, so you can easily understand the code. +* Created a new method: `GetAuthorLookupAsync`. This simple gets all the authors. The UI uses this method to fill a dropdown list and select and author while creating/editing books. + + + + + +### Object to Object Mapping Configuration + From 0fea42978265e4eb3274e9d0acf43a82384eb33e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 22 Jul 2020 19:17:24 +0300 Subject: [PATCH 33/77] Update Part-10.md --- docs/en/Tutorials/Part-10.md | 125 +++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md index 6b934bacb8..621c39a4d3 100644 --- a/docs/en/Tutorials/Part-10.md +++ b/docs/en/Tutorials/Part-10.md @@ -443,9 +443,134 @@ Let's see the changes we've done: * Overrode the `GetListAsync` method of the base `CrudAppService`, which returns a list of books. The logic is similar to the previous method, so you can easily understand the code. * Created a new method: `GetAuthorLookupAsync`. This simple gets all the authors. The UI uses this method to fill a dropdown list and select and author while creating/editing books. +## Unit Tests +Some of the unit tests will fail since we made some changed on the `AuthorAppService`. Open the `BookAppService_Tests` in the `Books` folder of the `Acme.BookStore.Application.Tests` project and change the content as the following: +```csharp +using System; +using System.Linq; +using System.Threading.Tasks; +using Acme.BookStore.Authors; +using Shouldly; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Validation; +using Xunit; + +namespace Acme.BookStore.Books +{ + public class BookAppService_Tests : BookStoreApplicationTestBase + { + private readonly IBookAppService _bookAppService; + private readonly IAuthorAppService _authorAppService; + + public BookAppService_Tests() + { + _bookAppService = GetRequiredService(); + _authorAppService = GetRequiredService(); + } + + [Fact] + public async Task Should_Get_List_Of_Books() + { + //Act + var result = await _bookAppService.GetListAsync( + new PagedAndSortedResultRequestDto() + ); + + //Assert + result.TotalCount.ShouldBeGreaterThan(0); + result.Items.ShouldContain(b => b.Name == "1984" && + b.AuthorName == "George Orwell"); + } + + [Fact] + public async Task Should_Create_A_Valid_Book() + { + var authors = await _authorAppService.GetListAsync(new GetAuthorListDto()); + var firstAuthor = authors.Items.First(); + + //Act + var result = await _bookAppService.CreateAsync( + new CreateUpdateBookDto + { + AuthorId = firstAuthor.Id, + Name = "New test book 42", + Price = 10, + PublishDate = System.DateTime.Now, + Type = BookType.ScienceFiction + } + ); + + //Assert + result.Id.ShouldNotBe(Guid.Empty); + result.Name.ShouldBe("New test book 42"); + } + + [Fact] + public async Task Should_Not_Create_A_Book_Without_Name() + { + var exception = await Assert.ThrowsAsync(async () => + { + await _bookAppService.CreateAsync( + new CreateUpdateBookDto + { + Name = "", + Price = 10, + PublishDate = DateTime.Now, + Type = BookType.ScienceFiction + } + ); + }); + + exception.ValidationErrors + .ShouldContain(err => err.MemberNames.Any(m => m == "Name")); + } + } +} +``` + +* Changed the assertion condition in the `Should_Get_List_Of_Books` from `b => b.Name == "1984"` to `b => b.Name == "1984" && b.AuthorName == "George Orwell"` to check if the author name was filled. +* Changed the `Should_Create_A_Valid_Book` method to set the `AuthorId` while creating a new book, since it is required anymore. + +## The User Interface +### The Book List + +Book list page change is trivial. Open the `Pages/Books/Index.js` in the `Acme.BookStore.Web` project and add the following column definition between the `name` and `type` columns: + +````js +... +{ + title: l('Name'), + data: "name" +}, + +// ADDED the NEW AUTHOR NAME COLUMN +{ + title: l('Author'), + data: "authorName" +}, + +{ + title: l('Type'), + data: "type", + render: function (data) { + return l('Enum:BookType:' + data); + } +}, +... +```` + +Create Modal + +TODO + +Edit Modal + +TODO ### Object to Object Mapping Configuration +TODO + From daa9eb4cc3fba35251210dfbd2875e5894d05e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 22 Jul 2020 19:43:46 +0300 Subject: [PATCH 34/77] Finish the part-10 --- docs/en/Tutorials/Part-10.md | 211 ++++++++++++++++++++++++++++++++++- 1 file changed, 206 insertions(+), 5 deletions(-) diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md index 621c39a4d3..3fc2e153bf 100644 --- a/docs/en/Tutorials/Part-10.md +++ b/docs/en/Tutorials/Part-10.md @@ -443,6 +443,14 @@ Let's see the changes we've done: * Overrode the `GetListAsync` method of the base `CrudAppService`, which returns a list of books. The logic is similar to the previous method, so you can easily understand the code. * Created a new method: `GetAuthorLookupAsync`. This simple gets all the authors. The UI uses this method to fill a dropdown list and select and author while creating/editing books. +### Object to Object Mapping Configuration + +Introduced the `AuthorLookupDto` class and used object mapping inside the `GetAuthorLookupAsync` method. So, we need to add a new mapping definition inside the `BookStoreApplicationAutoMapperProfile.cs` file of the `Acme.BookStore.Application` project: + +````csharp +CreateMap(); +```` + ## Unit Tests Some of the unit tests will fail since we made some changed on the `AuthorAppService`. Open the `BookAppService_Tests` in the `Books` folder of the `Acme.BookStore.Application.Tests` project and change the content as the following: @@ -535,6 +543,8 @@ namespace Acme.BookStore.Books ## The User Interface +{{if UI=="MVC"}} + ### The Book List Book list page change is trivial. Open the `Pages/Books/Index.js` in the `Acme.BookStore.Web` project and add the following column definition between the `name` and `type` columns: @@ -562,15 +572,206 @@ Book list page change is trivial. Open the `Pages/Books/Index.js` in the `Acme.B ... ```` -Create Modal +### Create Modal + +Open the `Pages/Books/CreateModal.cshtml.cs` in the `Acme.BookStore.Web` project and change the file content as shown below: + +```csharp +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Acme.BookStore.Books; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +namespace Acme.BookStore.Web.Pages.Books +{ + public class CreateModalModel : BookStorePageModel + { + [BindProperty] + public CreateBookViewModel Book { get; set; } + + public List Authors { get; set; } + + private readonly IBookAppService _bookAppService; + + public CreateModalModel( + IBookAppService bookAppService) + { + _bookAppService = bookAppService; + } + + public async Task OnGetAsync() + { + Book = new CreateBookViewModel(); + + var authorLookup = await _bookAppService.GetAuthorLookupAsync(); + Authors = authorLookup.Items + .Select(x => new SelectListItem(x.Name, x.Id.ToString())) + .ToList(); + } + + public async Task OnPostAsync() + { + await _bookAppService.CreateAsync( + ObjectMapper.Map(Book) + ); + return NoContent(); + } + + public class CreateBookViewModel + { + [SelectItems(nameof(Authors))] + [DisplayName("Author")] + public Guid AuthorId { get; set; } + + [Required] + [StringLength(128)] + public string Name { get; set; } + + [Required] + public BookType Type { get; set; } = BookType.Undefined; + + [Required] + [DataType(DataType.Date)] + public DateTime PublishDate { get; set; } = DateTime.Now; + + [Required] + public float Price { get; set; } + } + } +} +``` + +* Changed type of the `Book` property from `CreateUpdateBookDto` to the new `CreateBookViewModel` class defined in this file. The main motivation of this change to customize the model class based on the User Interface (UI) requirements. We didn't want to use UI-related `[SelectItems(nameof(Authors))]` and `[DisplayName("Author")]` attributes inside the `CreateUpdateBookDto` class. +* Added `Authors` property that is filled inside the `OnGetAsync` method using the `IBookAppService.GetAuthorLookupAsync` method defined before. +* Changed the `OnPostAsync` method to map `CreateBookViewModel` object to a `CreateUpdateBookDto` object since `IBookAppService.CreateAsync` expects a parameter of this type. -TODO +### Edit Modal -Edit Modal +Open the `Pages/Books/EditModal.cshtml.cs` in the `Acme.BookStore.Web` project and change the file content as shown below: -TODO +```csharp +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Acme.BookStore.Books; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +namespace Acme.BookStore.Web.Pages.Books +{ + public class EditModalModel : BookStorePageModel + { + [BindProperty] + public EditBookViewModel Book { get; set; } + + public List Authors { get; set; } + + private readonly IBookAppService _bookAppService; + + public EditModalModel(IBookAppService bookAppService) + { + _bookAppService = bookAppService; + } + + public async Task OnGetAsync(Guid id) + { + var bookDto = await _bookAppService.GetAsync(id); + Book = ObjectMapper.Map(bookDto); + + var authorLookup = await _bookAppService.GetAuthorLookupAsync(); + Authors = authorLookup.Items + .Select(x => new SelectListItem(x.Name, x.Id.ToString())) + .ToList(); + } + + public async Task OnPostAsync() + { + await _bookAppService.UpdateAsync( + Book.Id, + ObjectMapper.Map(Book) + ); + + return NoContent(); + } + + public class EditBookViewModel + { + [HiddenInput] + public Guid Id { get; set; } + + [SelectItems(nameof(Authors))] + [DisplayName("Author")] + public Guid AuthorId { get; set; } + + [Required] + [StringLength(128)] + public string Name { get; set; } + + [Required] + public BookType Type { get; set; } = BookType.Undefined; + + [Required] + [DataType(DataType.Date)] + public DateTime PublishDate { get; set; } = DateTime.Now; + + [Required] + public float Price { get; set; } + } + } +} +``` + +* Changed type of the `Book` property from `CreateUpdateBookDto` to the new `EditBookViewModel` class defined in this file, just like done before for the create modal above. +* Moved the `Id` property inside the new `EditBookViewModel` class. +* Added `Authors` property that is filled inside the `OnGetAsync` method using the `IBookAppService.GetAuthorLookupAsync` method. +* Changed the `OnPostAsync` method to map `EditBookViewModel` object to a `CreateUpdateBookDto` object since `IBookAppService.UpdateAsync` expects a parameter of this type. + +These changes require a small change in the `EditModal.cshtml`. Remove the `` tag since we no longer need to it (since moved it to the `EditBookViewModel`). The final content of the `EditModal.cshtml` should be following: + +````html +@page +@using Acme.BookStore.Localization +@using Acme.BookStore.Web.Pages.Books +@using Microsoft.Extensions.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model EditModalModel +@inject IStringLocalizer L +@{ + Layout = null; +} + + + + + + + + + +```` ### Object to Object Mapping Configuration -TODO +The changes above requires to define some object to object mappings. Open the `BookStoreWebAutoMapperProfile.cs` in the `Acme.BookStore.Web` project and add the following mapping definitions inside the constructor: + +```csharp +CreateMap(); +CreateMap(); +CreateMap(); +``` + +{{else if UI=="NG"}} + +*TODO: Preparing for the Angular UI...* +{{end}} \ No newline at end of file From b0db5bddafa51a3ed3b62bfa289155c812ecd5dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 22 Jul 2020 19:46:02 +0300 Subject: [PATCH 35/77] Update docs-nav.json --- docs/en/docs-nav.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index a6ab09dfee..ab1632d1c0 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -47,6 +47,26 @@ { "text": "5: Authorization", "path": "Tutorials/Part-5.md" + }, + { + "text": "6: Authors: Domain layer", + "path": "Tutorials/Part-6.md" + }, + { + "text": "7: Authors: Database Integration", + "path": "Tutorials/Part-7.md" + }, + { + "text": "8: Authors: Application Layer", + "path": "Tutorials/Part-8.md" + }, + { + "text": "9: Authors: User Interface", + "path": "Tutorials/Part-9.md" + }, + { + "text": "10: Book to Author Relation", + "path": "Tutorials/Part-10.md" } ] } From 333e31e1cba338e849daae035c9fc27eaa61600c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 22 Jul 2020 19:53:57 +0300 Subject: [PATCH 36/77] Added scrennshots and notes. --- docs/en/Tutorials/Part-10.md | 10 +++++++++- docs/en/Tutorials/Part-7.md | 4 ++-- .../bookstore-added-author-to-book-list.png | Bin 0 -> 80015 bytes .../bookstore-added-authors-to-modals.png | Bin 0 -> 37342 bytes 4 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 docs/en/Tutorials/images/bookstore-added-author-to-book-list.png create mode 100644 docs/en/Tutorials/images/bookstore-added-authors-to-modals.png diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md index 3fc2e153bf..ef8129d23b 100644 --- a/docs/en/Tutorials/Part-10.md +++ b/docs/en/Tutorials/Part-10.md @@ -64,7 +64,7 @@ Open the `Books/Book.cs` in the `Acme.BookStore.Domain` project and add the foll public Guid AuthorId { get; set; } ```` -## Database Migration +## Database & Data Migration Added a new, required `AuthorId` property to the `Book` entity. But, what about the existing books on the database? They currently don't have `AuthorId`s and this will be a problem when we try to run the application. @@ -572,6 +572,10 @@ Book list page change is trivial. Open the `Pages/Books/Index.js` in the `Acme.B ... ```` +When you run the application, you can see the *Author* column on the table: + +![bookstore-added-author-to-book-list](images/bookstore-added-author-to-book-list.png) + ### Create Modal Open the `Pages/Books/CreateModal.cshtml.cs` in the `Acme.BookStore.Web` project and change the file content as shown below: @@ -770,6 +774,10 @@ CreateMap(); CreateMap(); ``` +You can run the application and try to create a new book or update an existing book. You will see a drop down list on the create/update form to select the author of the book: + +![bookstore-added-authors-to-modals](images/bookstore-added-authors-to-modals.png) + {{else if UI=="NG"}} *TODO: Preparing for the Angular UI...* diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md index dcfe2453ad..30df597c0a 100644 --- a/docs/en/Tutorials/Part-7.md +++ b/docs/en/Tutorials/Part-7.md @@ -98,7 +98,7 @@ This will create a new migration class. Then run the `Update-Database` command t {{else if DB=="Mongo"}} -TODO, for MongoDB +*TODO: MongoDB part is being prepared...* {{end}} @@ -162,7 +162,7 @@ namespace Acme.BookStore.Authors {{else if DB=="Mongo"}} -TODO, for MongoDB +*TODO: MongoDB part is being prepared...* {{end}} diff --git a/docs/en/Tutorials/images/bookstore-added-author-to-book-list.png b/docs/en/Tutorials/images/bookstore-added-author-to-book-list.png new file mode 100644 index 0000000000000000000000000000000000000000..f318ddfd31883d52f6d109a097fd0ec28f46e29f GIT binary patch literal 80015 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�-TR3MYaXI-JY!@5;8fH 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 literal 0 HcmV?d00001 diff --git a/docs/en/Tutorials/images/bookstore-added-authors-to-modals.png b/docs/en/Tutorials/images/bookstore-added-authors-to-modals.png new file mode 100644 index 0000000000000000000000000000000000000000..5fe40467947f97c2ae9048073a8d40cfc58a8ba0 GIT binary patch literal 37342 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 Date: Wed, 22 Jul 2020 21:40:31 +0300 Subject: [PATCH 37/77] Part-7 & 8 completed for mongodb. --- docs/en/Tutorials/Part-6.md | 21 +++++------ docs/en/Tutorials/Part-7.md | 73 +++++++++++++++++++++++++++++++++++-- docs/en/Tutorials/Part-8.md | 8 ++-- 3 files changed, 84 insertions(+), 18 deletions(-) diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index 6ee16bb1b4..413ff48504 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -56,12 +56,11 @@ In the previous parts, we've used the ABP infrastructure to easily build some se * Used the [CrudAppService](../Application-Services.md) base class instead of manually developing an application service for standard create, read, update and delete operations. * Used [generic repositories](../Repositories.md) to completely automate the database layer. -* Used [conventional API controllers](../API/Auto-API-Controllers.md) instead of manually writing API controllers. For the "Authors" part; -* We will **do most of the things manually** to show how you can do it in case of need. -* We will implement **Domain Driven Design (DDD) best practices**. +* We will **do some of the things manually** to show how you can do it in case of need. +* We will implement some **Domain Driven Design (DDD) best practices**. > **The development will be done layer by layer to concentrate on an individual layer in one time. In a real project, you will develop your application feature by feature (vertical) as done in the previous parts. In this way, you will experience both approaches.** @@ -122,10 +121,10 @@ namespace Acme.BookStore.Authors * `private set` for the `Name` property restricts to set this property from out of this class. There are two ways of setting the name (in both cases, we validate the name): * In the constructor, while creating a new author. * Using the `ChangeName` method to update the name later. -* The `constructor` and the `ChangeName` method is `internal` to force to use these methods only in the domain layer, using the `AuthorManager` that will be explained below. -* `Check` class is an ABP Framework utility class to help you while checking method arguments (it throws exception on an invalid case). +* The `constructor` and the `ChangeName` method is `internal` to force to use these methods only in the domain layer, using the `AuthorManager` that will be explained later. +* `Check` class is an ABP Framework utility class to help you while checking method arguments (it throws `ArgumentException` on an invalid case). -`AuthorConsts` is a simple class that is located under the `Authors` namespace of the `Acme.BookStore.Domain.Shared` project: +`AuthorConsts` is a simple class that is located under the `Authors` namespace (folder) of the `Acme.BookStore.Domain.Shared` project: ````csharp namespace Acme.BookStore.Authors @@ -137,7 +136,7 @@ namespace Acme.BookStore.Authors } ```` -Created this class inside the `Acme.BookStore.Domain.Shared` project since we will re-use it on the Data Transfer Objects (DTOs) later. +Created this class inside the `Acme.BookStore.Domain.Shared` project since we will re-use it on the [Data Transfer Objects](../Data-Transfer-Objects.md) (DTOs) later. ## AuthorManager: The Domain Service @@ -203,9 +202,9 @@ namespace Acme.BookStore.Authors * `AuthorManager` forces to create an author and change name of an author in a controlled way. The application layer (will be introduced later) will use these methods. -> **DDD tip**: Do not introduce domain service methods unless they are needed and they perform core business rules. For this case, we needed to this service to be able to force the unique name constraint. +> **DDD tip**: Do not introduce domain service methods unless they are really needed and perform some core business rules. For this case, we needed to this service to be able to force the unique name constraint. -Both methods checks if there is already an author with the given name and throws a special business exception, `AuthorAlreadyExistsException`, defined as shown below: +Both methods checks if there is already an author with the given name and throws a special business exception, `AuthorAlreadyExistsException`, defined in the `Acme.BookStore.Domain` project as shown below: ````csharp using Volo.Abp; @@ -223,7 +222,7 @@ namespace Acme.BookStore.Authors } ```` -`BusinessException` is a special exception type that. It is a good way to throw domain related exceptions. It is automatically handled by the ABP Framework and can be easily localized. `WithData` method is used to provide additional data to the exception object that will later be used on the localization message or for some other purpose. +`BusinessException` is a special exception type. It is a good practice to throw domain related exceptions when needed. It is automatically handled by the ABP Framework and can be easily localized. `WithData(...)` method is used to provide additional data to the exception object that will later be used on the localization message or for some other purpose. Open the `BookStoreDomainErrorCodes` in the `Acme.BookStore.Domain.Shared` project and change as shown below: @@ -247,7 +246,7 @@ Whenever you throw an `AuthorAlreadyExistsException`, the end use will see a nic ## IAuthorRepository -`AuthorManager` inject the `IAuthorRepository`, so we need to define it. Create this new interface in the `Authors` folder (namespace) of the `Acme.BookStore.Domain` project: +`AuthorManager` injects the `IAuthorRepository`, so we need to define it. Create this new interface in the `Authors` folder (namespace) of the `Acme.BookStore.Domain` project: ````charp using System; diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md index 30df597c0a..ead535720a 100644 --- a/docs/en/Tutorials/Part-7.md +++ b/docs/en/Tutorials/Part-7.md @@ -54,10 +54,10 @@ This tutorials has multiple versions based on your **UI** and **Database** prefe This part explains how to configure the database integration for the `Author` entity introduced in the previous part. -## DB Context - {{if DB=="EF"}} +## DB Context + Open the `BookStoreDbContext` in the `Acme.BookStore.EntityFrameworkCore` project and add the following `DbSet` property: ````csharp @@ -98,7 +98,13 @@ This will create a new migration class. Then run the `Update-Database` command t {{else if DB=="Mongo"}} -*TODO: MongoDB part is being prepared...* +## DB Context + +Open the `BookStoreMongoDbContext` in the `MongoDb` folder of the `Acme.BookStore.MongoDB` project and add the following property to the class: + +````csharp +public IMongoCollection Authors => Collection(); +```` {{end}} @@ -160,9 +166,68 @@ namespace Acme.BookStore.Authors * `WhereIf` is a shortcut extension method of the ABP Framework. It adds the `Where` condition only if the first condition meets (it filters by name, only if the filter was provided). You could do the same yourself, but these type of shortcut methods makes our life easier. * `sorting` can be a string like `Name`, `Name ASC` or `Name DESC`. It is possible by using the [System.Linq.Dynamic.Core](https://www.nuget.org/packages/System.Linq.Dynamic.Core) NuGet package. +> See the [EF Core Integration document](../Entity-Framework-Core.md) for more information on the EF Core based repositories. + {{else if DB=="Mongo"}} -*TODO: MongoDB part is being prepared...* +Create a new class, named `MongoDbAuthorRepository` inside the `Acme.BookStore.MongoDB` project (in the `Authors` folder) and paste the following code: + +```csharp +using System; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Collections.Generic; +using System.Threading.Tasks; +using Acme.BookStore.MongoDB; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; + +namespace Acme.BookStore.Authors +{ + public class MongoDbAuthorRepository + : MongoDbRepository, + IAuthorRepository + { + public MongoDbAuthorRepository( + IMongoDbContextProvider dbContextProvider + ) : base(dbContextProvider) + { + } + + public async Task FindByNameAsync(string name) + { + return await GetMongoQueryable() + .FirstOrDefaultAsync(author => author.Name == name); + } + + public async Task> GetListAsync( + int skipCount, + int maxResultCount, + string sorting, + string filter = null) + { + return await GetMongoQueryable() + .WhereIf>( + !filter.IsNullOrWhiteSpace(), + author => author.Name.Contains(filter) + ) + .OrderBy(sorting) + .As>() + .Skip(skipCount) + .Take(maxResultCount) + .ToListAsync(); + } + } +} +``` + +* Inherited from the `MongoDbAuthorRepository`, so it inherits the standard repository method implementations. +* `WhereIf` is a shortcut extension method of the ABP Framework. It adds the `Where` condition only if the first condition meets (it filters by name, only if the filter was provided). You could do the same yourself, but these type of shortcut methods makes our life easier. +* `sorting` can be a string like `Name`, `Name ASC` or `Name DESC`. It is possible by using the [System.Linq.Dynamic.Core](https://www.nuget.org/packages/System.Linq.Dynamic.Core) NuGet package. + +> See the [MongoDB Integration document](../MongoDB.md) for more information on the MongoDB based repositories. {{end}} diff --git a/docs/en/Tutorials/Part-8.md b/docs/en/Tutorials/Part-8.md index 89a8c58e01..0930ca88d8 100644 --- a/docs/en/Tutorials/Part-8.md +++ b/docs/en/Tutorials/Part-8.md @@ -86,7 +86,7 @@ namespace Acme.BookStore.Authors * `PagedResultDto` is a pre-defined DTO class in the ABP Framework. It has an `Items` collection and a `TotalCount` property to return a paged result. * Preferred to return an `AuthorDto` (for the newly created author) from the `CreateAsync` method, while it is not used by this application - just to show a different usage. -This interface is using the DTOs defined below. +This interface is using the DTOs defined below (create them for your project). ### AuthorDto @@ -174,7 +174,7 @@ namespace Acme.BookStore.Authors } ```` -We could share (re-use) the same DTO among the create and the update operations. While you can do it, we prefer to create different DTOs for these operations since we see they generally be different by the time. So, code duplication is reasonable here compared to a tightly coupled design. +> We could share (re-use) the same DTO among the create and the update operations. While you can do it, we prefer to create different DTOs for these operations since we see they generally be different by the time. So, code duplication is reasonable here compared to a tightly coupled design. ## AuthorAppService @@ -499,7 +499,8 @@ using Shouldly; using Xunit; namespace Acme.BookStore.Authors -{ +{ {{if DB=="Mongo"}} + [Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}} public class AuthorAppService_Tests : BookStoreApplicationTestBase { private readonly IAuthorAppService _authorAppService; @@ -527,6 +528,7 @@ namespace Acme.BookStore.Authors result.TotalCount.ShouldBeGreaterThanOrEqualTo(1); result.Items.ShouldContain(author => author.Name == "George Orwell"); + result.Items.ShouldNotContain(author => author.Name == "Douglas Adams"); } [Fact] From f85d4bac496d7b2e7dc806087ea37520aba20bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 22 Jul 2020 23:12:22 +0300 Subject: [PATCH 38/77] Update Part-9.md --- docs/en/Tutorials/Part-9.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Part-9.md b/docs/en/Tutorials/Part-9.md index 147de367c4..370ee2341e 100644 --- a/docs/en/Tutorials/Part-9.md +++ b/docs/en/Tutorials/Part-9.md @@ -509,7 +509,7 @@ That's all! You can run the application and try to edit an author. {{else if UI == "NG"}} -TODO: Angular UI is being prepared... +*TODO: Angular UI is being prepared...* {{end}} From acf161bd2134dfe9d26fa38d3404432788cf0768 Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Thu, 23 Jul 2020 14:37:06 +0800 Subject: [PATCH 39/77] Filter permission provider in PermissionDataSeedContributor. Resolve #4841 --- .../PermissionDataSeedContributor.cs | 1 + .../PermissionDataSeedContributor_Tests.cs | 32 +++++++++++++++++++ .../TestPermissionDefinitionProvider.cs | 4 ++- 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDataSeedContributor_Tests.cs diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs index 906192fd4f..ac8274d445 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs @@ -30,6 +30,7 @@ namespace Volo.Abp.PermissionManagement var permissionNames = PermissionDefinitionManager .GetPermissions() .Where(p => p.MultiTenancySide.HasFlag(multiTenancySide)) + .Where(p => !p.Providers.Any() || p.Providers.Contains(RolePermissionValueProvider.ProviderName)) .Select(p => p.Name) .ToArray(); diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDataSeedContributor_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDataSeedContributor_Tests.cs new file mode 100644 index 0000000000..a888beb5f9 --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDataSeedContributor_Tests.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Data; +using Xunit; + +namespace Volo.Abp.PermissionManagement +{ + public class PermissionDataSeedContributor_Tests : PermissionTestBase + { + private readonly PermissionDataSeedContributor _permissionDataSeedContributor; + private readonly IPermissionGrantRepository _grantpermissionGrantRepository; + + public PermissionDataSeedContributor_Tests() + { + _permissionDataSeedContributor = GetRequiredService(); + _grantpermissionGrantRepository = GetRequiredService(); + } + + [Fact] + public async Task SeedAsync() + { + (await _grantpermissionGrantRepository.FindAsync("MyPermission1", RolePermissionValueProvider.ProviderName, "admin")).ShouldBeNull(); + (await _grantpermissionGrantRepository.FindAsync("MyPermission4", RolePermissionValueProvider.ProviderName, "admin")).ShouldBeNull(); + + await _permissionDataSeedContributor.SeedAsync(new DataSeedContext(null)); + + (await _grantpermissionGrantRepository.FindAsync("MyPermission1", RolePermissionValueProvider.ProviderName, "admin")).ShouldNotBeNull(); + (await _grantpermissionGrantRepository.FindAsync("MyPermission4", RolePermissionValueProvider.ProviderName, "admin")).ShouldBeNull(); + } + } +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestPermissionDefinitionProvider.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestPermissionDefinitionProvider.cs index 584c4dd2be..c6de42ac3b 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestPermissionDefinitionProvider.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestPermissionDefinitionProvider.cs @@ -16,6 +16,8 @@ namespace Volo.Abp.PermissionManagement myPermission2.AddChild("MyPermission2.ChildPermission1"); testGroup.AddPermission("MyPermission3", multiTenancySide: MultiTenancySides.Host); + + testGroup.AddPermission("MyPermission4", multiTenancySide: MultiTenancySides.Host).WithProviders(UserPermissionValueProvider.ProviderName); } } -} \ No newline at end of file +} From cad7288fef130de66ebf3b7ba4381013cf4d3e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 23 Jul 2020 11:15:19 +0300 Subject: [PATCH 40/77] Added "The Book List Page" for the angular UI --- docs/en/Tutorials/Part-9.md | 174 +++++++++++++++++- .../images/bookstore-angular-authors-page.png | Bin 0 -> 41853 bytes ...bookstore-angular-service-proxy-author.png | Bin 0 -> 25528 bytes 3 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 docs/en/Tutorials/images/bookstore-angular-authors-page.png create mode 100644 docs/en/Tutorials/images/bookstore-angular-service-proxy-author.png diff --git a/docs/en/Tutorials/Part-9.md b/docs/en/Tutorials/Part-9.md index 370ee2341e..2164526645 100644 --- a/docs/en/Tutorials/Part-9.md +++ b/docs/en/Tutorials/Part-9.md @@ -509,7 +509,179 @@ That's all! You can run the application and try to edit an author. {{else if UI == "NG"}} -*TODO: Angular UI is being prepared...* +## The Book List Page + +Run the following command line to create a new module, named `BookModule` in the root folder of the angular application: + +```bash +yarn ng generate module author --module app --routing --route authors +``` + +This command should produce the following output: + +```bash +> yarn ng generate module author --module app --routing --route authors + +yarn run v1.19.1 +$ ng generate module author --module app --routing --route authors +CREATE src/app/author/author-routing.module.ts (344 bytes) +CREATE src/app/author/author.module.ts (349 bytes) +CREATE src/app/author/author.component.html (21 bytes) +CREATE src/app/author/author.component.spec.ts (628 bytes) +CREATE src/app/author/author.component.ts (276 bytes) +CREATE src/app/author/author.component.scss (0 bytes) +UPDATE src/app/app-routing.module.ts (1396 bytes) +Done in 2.22s. +``` + +### AuthorModule + +Open the `/src/app/author/author.module.ts` and replace the content as shown below: + +```js +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; +import { AuthorRoutingModule } from './author-routing.module'; +import { AuthorComponent } from './author.component'; + +@NgModule({ + declarations: [AuthorComponent], + imports: [SharedModule, AuthorRoutingModule], +}) +export class AuthorModule {} +``` + +- Added the `SharedModule`. `SharedModule` exports some common modules needed to create user interfaces. +- `SharedModule` already exports the `CommonModule`, so we've removed the `CommonModule`. + +### Menu Definition + +Open the `src/app/route.provider.ts` file and add the following menu definition: + +````js +{ + path: '/authors', + name: '::Menu:Authors', + parentName: '::Menu:BookStore', + layout: eLayoutType.application, + requiredPolicy: 'BookStore.Authors', +} +```` + +The final `configureRoutes` function declaration should be following: + +```js +function configureRoutes(routes: RoutesService) { + return () => { + routes.add([ + { + path: '/', + name: '::Menu:Home', + iconClass: 'fas fa-home', + order: 1, + layout: eLayoutType.application, + }, + { + path: '/book-store', + name: '::Menu:BookStore', + iconClass: 'fas fa-book', + order: 2, + layout: eLayoutType.application, + }, + { + path: '/books', + name: '::Menu:Books', + parentName: '::Menu:BookStore', + layout: eLayoutType.application, + requiredPolicy: 'BookStore.Books', + }, + { + path: '/authors', + name: '::Menu:Authors', + parentName: '::Menu:BookStore', + layout: eLayoutType.application, + requiredPolicy: 'BookStore.Authors', + }, + ]); + }; +} +``` + +### Service Proxy Generation + +[ABP CLI](https://docs.abp.io/en/abp/latest/CLI) provides `generate-proxy` command that generates client proxies for your HTTP APIs to make easy to consume your HTTP APIs from the client side. Before running `generate-proxy` command, your host must be up and running. + +Run the following command in the `angular` folder: + +```bash +abp generate-proxy +``` + +This command generates the service proxy for the author service and the related model (DTO) classes: + +![bookstore-angular-service-proxy-author](images/bookstore-angular-service-proxy-author.png) + +### AuthorComponent + +Open the `/src/app/author/author.component.ts` file and replace the content as below: + +```js +import { Component, OnInit } from '@angular/core'; +import { ListService, PagedResultDto } from '@abp/ng.core'; +import { AuthorDto } from './models'; +import { AuthorService } from './services'; + +@Component({ + selector: 'app-author', + templateUrl: './author.component.html', + styleUrls: ['./author.component.scss'], + providers: [ListService], +}) +export class AuthorComponent implements OnInit { + author = { items: [], totalCount: 0 } as PagedResultDto; + + constructor(public readonly list: ListService, private authorService: AuthorService) {} + + ngOnInit(): void { + const authorStreamCreator = (query) => this.authorService.getListByInput(query); + + this.list.hookToQuery(authorStreamCreator).subscribe((response) => { + this.author = response; + }); + } +} +``` + +- We imported and injected the generated `AuthorService`. +- We are using the [ListService](https://docs.abp.io/en/abp/latest/UI/Angular/List-Service), a utility service of the ABP Framework which provides easy pagination, sorting and searching. + +Open the `/src/app/author/author.component.html` and replace the content as below: + +### Localizations + +This page uses some localization keys we need to declare. Open the `en.json` file under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project and add the following entries: + +````json +"Menu:Authors": "Authors", +"Authors": "Authors", +"AuthorDeletionConfirmationMessage": "Are you sure to delete the author '{0}'?", +"BirthDate": "Birth date", +"NewAuthor": "New author" +```` + +Notice that we've added more keys. They will be used in the next sections. + +### Run the Application + +Run and login to the application. **You can not see the menu item since you don't have permission yet.** Go to the `identity/roles` page, click to the *Actions* button and select the *Permissions* action for the **admin role**: + +![bookstore-author-permissions](images/bookstore-author-permissions.png) + +As you see, the admin role has no *Author Management* permissions yet. Click to the checkboxes and save the modal to grant the necessary permissions. You will see the *Authors* menu item under the *Book Store* in the main menu, after **refreshing the page**: + +![bookstore-authors-page](images/bookstore-angular-authors-page.png) + +> **Tip**: If you run the `.DbMigrator` console application after defining a new permission, it automatically grants these new permissions to the admin role and you don't need to manually grant the permissions yourself. {{end}} diff --git a/docs/en/Tutorials/images/bookstore-angular-authors-page.png b/docs/en/Tutorials/images/bookstore-angular-authors-page.png new file mode 100644 index 0000000000000000000000000000000000000000..806ac3a49500cb10d5791fbf16b709a573d1be9a GIT binary patch literal 41853 zcmeFYbyS;A6gEi9uced%rC2FeLV@D$+7e3fqG`|;cXzMgR-m}GIKhfTuom~A!GpU5 zmq5sd_WO3v_uuY0d-i;P>>LhfGH>$E%$+;;K6B?jVeeGs2_HUth=YSesPGo3frE2D z1qbJD`-8jKmXCKZ6gW7~aTI{Bv^>*y=WTq+PSF^2%($v|0vP2&!R@YIbtjHI?j?Tg z*S8|FB2OQOX~^hZfb(kQN_9&qh_#g@Yo@#lb#^`llmhPNy;$>oct>^-HbNtSpPbtk z+*Vd9erBC7s4sl>ZO~kpCC*l;DKlnnYIkVJ*TdZ1eO+>)U2ale+z>mI+j8RR2Qc_o z#lf+Aorw3Z4#caJ_*Z)__HFIXzv{>*4&r~cP$E;We>EA`=l!_Nf_uyy?*W zmy++6!d+vrLgwPRT?*HrmNQ;be$whQ3s3*lX^5h>q54Jw{-%gsPkqJKS)k+$$CCmm zn-E>hho84Uk}>sKT5W+^DWoa@t&iVu`vOMu?gYB?cE=3Skl5(BT9{3jEw4Z^LAm(H z8l1d{0DZiLY5gZDchCIc$Am{sLjgp;FE!(tS981WPjCt9YwJC@U2&*^hpE@G;XtFq z*Jnq0DDQc&muWvF$q?ZWrB)pE<7nTQuYB&Km2IGDV*D(ttX%jJXhObGSQ7m@lhHX3 zcewbpMlChbN|T&~WH9x6LXOovYGThA(Nt@V>!g)%Q9h<7ocC{Hb|Rk>Z<282RLS1+ zYOLK`Grf}rpy0iCLq_Oe$r#8zq z{E<%FtEId9pYPN^e$#Yoqn$~zw_tX7-gKsO6}C493lkVr3uObwUqSZzPoa~ ztnN(JCQ%<)_l&jdJ6bjtj1PU8D*Ec{&P$v@FNeIZBwM)Rcol_R>oyVggGV%-1P;3J zRVth9Jz^>Ul^Sz7ASy=pz;b(?g~-DqOf2QJ%!uRgzE{_@Y?|TC-tncjO2My3S?A4i z4GM3@EF7B7@y3>c9fST4e|#Cgz!%L)75MBT-lwRqs~d+DP#W2dFGwj&^|v<(*+cSG!oT{lYu(+cXI)YU z%`&dAIN@ZyIl5MyxnxT(6H(DTfo>B`UKAI(F@sAWORhRKQch|b20Mziy2`=*V5+Tb z9GNSp#}U@HEw!PJ)Oy`ZlSspomMU|20sv3}8hjkK%;9hmS8GnHjd`uCd8lww;a{>z#GFWXYYhgSLYKj_Q_%iSGvxqfBnQ<$0G2(5Y( zqo6^~DKzLDDCKlF#l)mE74Rm>|I_7@J2;OMyk!zuG?vCY*Bf?bb%TO7Ka9KZJ^Vpk z2r^Y2vUbKVolMw8=xa?*O0f!$<+D8zAGV#Yu|vWMy$`-`x`zvzEpf7zs!mWvh#4$8 zPLm3dOqh~}WmBgi*3*NlDwBx6S*wN8OA8h6jRk0^Yrm^6*y$Vl{Mf=g+*%F#+G$(R zEz5(ahqNNUD^(8Ykw7*XKKP^n0?)mpL8&6y$DonDxr2*CM^o=zvNq<@axhk#VBjsR@U$*LII}M)IJ{@UM;l{9)XD2W{>TNI?N$+KKSBW}o0dLQ9 z$*;v4>=^oZ`CO^d@cX0wXpSu#1A*H%Li4SQDcQ{2ddv1CUIh~k*C$_^$CmGA@#M|b zd2IN#U?9TE54H{8nf?xKh@gO}*6C?sc%Ge}LaQoEELUvQ%EjrNd1G1{UGi0cCtq4K zyt>&cVE(4lv|uM@IpEN~o)o$ze`k(wN#0{2Ipdl1@Z^hf#GbR(egLu#RuWa2X&8tY z;^VWh%@+J|npP+4-2iZLH?b|SJe=kqkDE`6fR}oxrY273QuE1$A=$Mh%VHo1C3UE@ zN-6S;DVvautr(aawGkaFX;b39)2lmXUK?%>OSYsWA!+}{>{C+)sI9`5uQqcd#4Wxu zF>h`??KD+sIt%WwVS(u$&$X+CgsqlT(R(dLhdB+R`l5NQoEVw8p2a3euIybQeadM! zqN&cCjy-#xJ7@-(=%#A}zZctk`6Ft5blmOtnj-0&y)h*MHU~i;{yH>CVNpq+GgJYc zufI(M!Js~Lxq+(UMv!Xkq;k~1pC2}#D)nnC{BJM$lGOmos|37#PZn4G2S3t^6iA&l zaOI&qbAs}<_S_!9G<`ZXFVu##i5+I3eo>7IFmLO*DX_}`BJ&j5GK?FisktjOxT;qD zsC}=b_~m>+q0mdUM^E|B56DxfSlx^BUjmGumO;b)#3QzW!10UN6?rU9fz3T?*9JLR z2B;sD0D%LCIo(mAf?fq=?T#4`35{{dEV00)sn+b`*(uailTD`^+qm6Rm+J$P-a}mN z8YiVq=$N{WmZyZNruYOO2dALaP|1B;vH7h+RAH&?+L=nG6#Ez?^yz*o`f@fV#4moV zkWwAU+f4*giV4mr+0x}>;b@ZlqgnDp{g|twApZr+d*wUyOjiUDDF-omWFREi z1QY&R(nT3M4`BlYo4~@OqNIH@8X0R2=Xl#Jq7q|M;EOj$ckgwM*~^2|-cUuW2^0ge z#TC^e-pb|Kd{q%FO64?UYeR_a+9;{0+03N||J<1fc^xxIEApe3(f_2Lqyl08l5igE zVyg7hf|%x;1gqqrkL_WL*5ecMgIz0?v*JJlGm}zIMoG>;#%BpVTZLlpeSd5vwTb&x zk^KH575y|=9a+LQ<@tMw9x*9fVTyqAXab-4blLiO5FXMJi@mNzF&#y}+rnZIZciqU z-4BMIVSdW+2WjTkAA{<^uD*Uao}R=aW#Pid533ngy7D`$Ajr3i5UJ{d>6psUP)pe^ zv)qmp>Xo-MQ8pk#S$#+S5P3Yb>6s%4?6D;S!O= zwwy(lkl?2l1sTEKrEwp0H;Y-0edYGmJ1JBdb0>ivy^(_Y&S3qYv-`q!f$Zu#MtZ+< zN`AW*G2@|`xKXwO20*wd1MhTxXJ{y^l*d+8c*{YU`Bl8z{&4_QywhU>lEuc#UNm+K zG@Lb-Ww5XUSDw@VRBmPco2<5gMX%rKAsCd3HD9zoi3`;48IhYx#nGnT6c&mN$USmU zGpb{h$88M=TysfN=@0DBX~zHvP#g5DS=BCNH&=H z{i%fxu;V+=PQ_X{oxTE3;MtfDOXq$0Y$oi-sIZ1MJ4&kYmR z1^?;7HJHg&k0R7BEa!{KPA;ji(oxdiimvHoh~aE^HqXU-fghgT$*h)HWZPf-YjD86 z@(Gz-gbH0T+w4)QdxM+8(Med;$FB_8q1l#B3Z!_}W_r*IO}q$oy9!V>pGv{_oMwoT znYLVqBb@G`KWE_=xrM}cf;O*pq6N^ z+&$u>8sAs|Ze~PDow=;xf=nK@l{T}VYR!Uv{H!6gk=(C%px zFJvlb$bJbirx)+nnjT%8%je}EdvceEEZkbPr%yXUFepwmXUmRQPLRcPt*|dOioqlD zu`hm2xpQ`QvR&AUisA=Q%*RJhg?+LEgpc>q!j0hrDq;Xz?alcKAy(npb!mH)c{x^q z@bP6qJy`jOpFHt;6#eu8Sv8tn;;kA6g=0Cy=RLIQ=@QTQ2|J2GvMQ<8YK4b6%qoMu zmNXNCJV14_Do#nCTK8><#0A&dOum=lkQ7;joJwkH+D9vA$kye&mCj7<4g+4<*|qYC zN~QqkMU(o-avbxJx~>Gq26|6YI(E7$fpZ74{297#*M`>=WZY2B6bx`rEa*yabVie&+t~|^1bT5xZ4(}C@3=P~9H3u~yuTjU$TZ|bK;A0v z4&=dTQcuUOe}k|-yp0y&xNY0Jy%y6vS3NWn$m$`98m=Q%0pycJWHs8rIJv96?}{Cb ztVN7TxVX}LS@km7r0uguYpY2ooa~y!o0tGQ$kO~U^n4rpumF?GqhYuE!mZcI8;qC} zT(v}t`fxfQzK)l5iE|*d;2rE%lfHQS&INR}%aYZ|Zu23sV=7+~wZk|jTcSWh;-=-T zADLb{SG`>4P%gp2xXuMm-`=@j;AF5x-;C)f<)(SyxezF-hF(WhBtBIivu_Z#&>l;8 z^g|B|!7~%)azBgxLYX`-Ku3TDwJ|Ba^A$<5I{RbjV#sJ7^?3!eV<9v)>x@?xW?jvm*43VQ*o| zl`q4wD?J9W4p#?N!p=vNO9uW^QLnBa|9J0?+rl%=7xLQbOP`(@0BOIa$0nF-lH2&M zC(qOb&3gpA9(hv5!s5QiH-6{rYsYHt`zk=Np*ER5;V^x1*b+pn9{E;bJ_!Jr`9@O2 z3YylEi&9CGn4xmyGNa{p^8K72`qbN?lL0n!Hr}V8raG(~o+#l3@tI$bJD6OC^OBB9 zl(Nz;Emz4D5Y*E04!*5}WLt(!yR2j+$cGapiGN1uB%gc#6ev9+t)>fJ8#oxZ<-5D_ zA(~#w7-*ahNw#I3HqmFOZDEyCCAV0+`4QwkA3fgIVDkN*m$3*DQgl3D%RG6$QAz9a zta^-zNRsQ7_CN(=ZW9=1Uf2t|(X`+w4k03^FLVq$$BBF9pz`ah(KmCoZ>UtXUU)w( zv@r2wiizg3mRtq1xth)Zq~7L?($nZ!rl`=!u|{fMlM3kW`gZ}Q51^6YNa{5ENvx%! z7tXgfi@5sY7ReIGPm_1$*WIpEq7Y_P5zYY6$i%2N2$Tjs_NJNTX)IPa1a4@DDE6~$ zw~&#zTz_TFF;)qGmLxEv3!P-JyLb{be)#1Q=<<7m@ck0&N_?NV#9eb38in)VRa9Du z!b-26k&QK~KKFfm_Iu5$I3-*n*Tj+OBJ4rnX<2yt6>(xFTnhVt1|VAO+$jQsK)dZfnf+^?dPv^&&w7B73@!KUW9EPCX(Q=c^+C2LpLS@r2X z-=RuNtgf?@#``D~l|9`ZUG{$BkSNKg4ZHWT$Khr3T?w9?>8gF`7ivSF>!1t<9n^^r zW%Pl5C4A4@@&-Lv?4+|c-wO0P-D9$1CUHSEWj*S_Bw$vpem6@eZw|I9BAQRH%7xP~ zTczCBZ-u>P(%C?&Ey5c?iJFjD;+8!Eig|fM-6xg!dt0S3oZ}fVX)*DfS;;iR z!-!!UV*6R}GBkIKwkk2XWJ}bAQ;yo*-eB!vjrPKTdWAXG zacK)Q7@XzhxFTnsfrfTni?iRyYur_%ZOYS#3@1$Un`n_Le~I9*-WN$@gP7k&T<#jO z&3zPtDZ;-qg9FVzTevmYxR;#Je>xpwnicLn0jKD6+U_x-T?A)e23M`W{d0D-*kyCL zl}TzqJ{@a!Ng!heyK!eO__nf82+u#yTxSie*X0AS%`c%bG4C+=VFyW|ehwoYRAb5YCk!w=9Xl%08FhD@3O1vb{_wRay5r6_$jKIK$pVXmu6X*XA$$M+$49~ynCH~vtt6XNCk4b5QP zxVhFSp|Xm_&{eZO!8vT3oaAoMg5SaXn|a(3>Cn*7?2Daa79WRgc*1`RikFqS)kB;B zh`G&DrZJi3K<$ZW-2e?`fWl%`FFu0YnMbQ+{UwKZUPP#|%F`{cH!3yBQxYlwD~(x!*O`fd!MIcEm@I+QFJSzdjDaN8dHm z1`P|3nX3akv^{tnvkJwRZ~H7|eU)@yNbSqqx2saamg4-N?G98?yUId#l4vug5kipG zq6OL!t?zhgxARNGp{4jcNQ97tM2@%aLIWJp}+D ztVKBy;xS(u>LKp;{HS)$#0&rcMP+&Jem`&cB%X}tP@m*0@=Mwvt^%wS1?cdf=!uMr zJnW}o*dDHlnmy3ZWVhB+WjWDg`okhkE|^LGkSTvH$Cny-_Im=J6_!gegsVpR9Uinm z7M#Pk$=OY7zrgA{tc1FQWcAexAbM^4%mHIE(rPu6r?;~8vr9#w8kH?*MY669ZNYaGv} z8#P?$RxxMmYKHVfh7}ywM=g>f5AJyp2W=*Ezt={L%6Y-Z=a|m9k80#VC>9uMt#NCb z`}*R>ZLjEuFzft!1CN`|nuF~}=$INkq)#Q3lEh}0BX%-PZHa6C(78u@Ne@)OHW^aS zx};vvD5nfSmu>;XXZ5{tG8DMyo8;0`GJe2)?mxB^`fjJks(ZAme|ivI8sv3ZhOen% zVsZlEv{Ta4wGUsjBcpiv>a=>nLnm13WUG_QBF>xw8&WH>JJM)I?A9t2?GHV!3Z0!O z(d0~8+UpnqXs`fqOPFDMlQWM8$E^J}*x`#oZ*l^ooWlfls%Dn=gd!C1x4(3?zTSRa zHyOV|;B1>ZP|;BG2=qczM54OB8Z=v=f*V8I*oBv*rgrp#+Wb4jG{}-YpgE3%LF8IYFs?;HU zdhF6<6RmUZe<+)%#+mYQE40G@I>rG*9CdmUH#H6OU=FL zMfKfPG{>iQ8#IfRVm#Ag++;%MC(6qOg@lIki?E)th&hk{&4?6MlV`#3YksjELJ~PG zLGX)fJgfN3qtiQFz~G#V=2~rbcm{+2pJqI#croQ%ec%fPo5OdS`|z<+TIv@iweX_u zRISc*0h?%D9p?*6IRfD?kLE|3_Ei)1>DbNHXFSIW%wnY+Yi{_LXgj6mynZ0lCCJ(h zsIIlb$}GZ}v&GZ)I1yR6auv0C>z>F30lSG(aAuC@l1l&QOlltE{pAq*jHeb)cndhZ zP`%+@Lb+k)u(G8x&Kbl}WPiFUN!8m(pwUkW-hJ$Zjf7v1Dy5gfZ6)BbIJ@^79t!z(ska~Sa_ik#5U zm++HW2NhzyrlF_S*({fH>Q@apXBR))+kN-uNB8o?*@4&WxrhgiOuhwRM2&Gc*1S13 zwq=5X=3TAzA=#m!hHja-ks`OwFJYRmvWo>wre0bEX0#Tn8)Z!chmnx*Zntwy1u#9< zch70LENOI*hZL3^rAxh0*8AKfMj|@D#H&zJL;>r~5Z&sUl?6T4EL8x#onP?9cy}Kc z35(T;tF=Fv%=&!^BDF~{Y|43?aeF~GVGK()SDVO)hUk>B$>P^EhKDA{Gb=0D2@z>b z^;8W)S3e-E0GQJ|!w+R=j%JsrQ?#UG65~G7EI{@8snRU9$*VZc(m9#Nv_S@5bi!Ig zdbvdUcGoF9-JZg7S8A(m4mRzMAwKch)h;-NTx8^}tRxO5KB!>`i-WhVW=%m`vEYup z$EA@*dsO|*fT~dh{)7bl>U)Ee>l$36N^HJLI%VMSr(rZL68{K@*&KA&DIz5y={}o8 z8u1*jZ=9tc)i!94?O${@+d0@$dn@<7t8Mfxenm~WY&Jj5L3_Sw2db2D*dRO-Q=h!& z6-w6Zo5W(TE~qCD^$<}Do}GQv8J9lZ6~oT>ra+)Jzhy)+9=3*=jK9NcX!B~vZtgTZKWBA8)msvYZ!`Nq2G18}G9|I7vWy2Y3$Ii6c2 zN>#y(1-PQt5TboVg;_glazXzPwM6ku_Kpnk*z08%scoXwa$O_GdUz2vAls6{r>lCd zfwb-X3QkaSZULC{V+882c2y8+T@R#=mR!9l1UyM{_31hL8Jp8M59xgYfcTxoiOzoR zF0;!CcZ<8qh00IankAAA6$s;f34Nt z_%pkbVy(e6ASs5VeZRFzpfa!dalvsHTyf~R6MrrSb(pwfWAg^Rjm!@F+QOZ%Yo)a# z{^_h`A-6VQjB3cvtm_u5cjc+3X|!6|OkBeIMj~u7@UfSK@y~Ybf=Q!iv7y~((w;8u z%9-~3Z6M?${iAyojBfJZSe|5UT5<1)TpQjjJuWY%;@@Dg%GTXg&{(qjvNGRb+-7%P z*vwEgnLugF!Tn8Xw}`b@0ee!YVK!57Dx3B;JjFrfGrgD4Ha>=#aI_8s+r}=V|J^ouTyj+C5+6l&-6%kIGvKF)i(NBh5%C<; zkFvxlBuMRX!ga``tCk&6COHBmTMqq|iiJOYVBIeU>z?V~qfPx}c(ppZ_+}U7U8!wh~9vWTb)DSNaUkD$+^8}LSE-1?%5xO~~(>-jL)haswCLQvUmqI&Q z9(f54;SMitDc<&WDD7_*m0<6nN#j`i>0R(_dGG0v!NxS%gsLQ1Q;u-Eb)eiKA52Oz zVlXjiG~RzK{B|hvc6FjsMO2ZIFAAfqcU&3axi8nJK59H;Lx-Ka7ZwN$t=higo{rTW zm3(EmvdLbi`SY^`$w4@6$#Om?H zf|K(S(0MFU-^DDWjx%_guXs)P$_MV!*mh#`{uDvmqVt=#)(X~qH8!BrPxr>(skE-v zant|zON!^tnuuk-4@bS#iNk_QDvME5CHl#xiUKiP z-_Oi|;Nr6n78a5r674tE6)WODQ#LMn=D3ooBm?Xh?(@@V(>D3!Mh#SZ`*4&UmQKYf zA}>m<#zF+8No%7OXbQta{UM`i0ir~cVsgOodi9(%;`$~8@@Dh4cWbz7REiVu(>}pzwhNjw!d=$j8%ISiKM_=;sC)~m*bp=N&X+Bf=>4jCeFjN8g?caGnF~JgzTmxj%80)M=5Sj zpnKXI@Ptb-Qe}OS=Afh8S0IS+)e0@*GMbP`7*@99^T(XFJJ;s z=WV|>s7_f4QA-gY*}wdeca;V@dRQudyVkUcE0;vU<0-E)-&&Ul_Z1WJ4ka6fv51ln zrY_A(pDwe(-~uelkET+Srug%R(rBnnfG37>A*QNXDg5T|e~Uz7+coZczu76ZyY2(+ z+#UftnG8m4>6SeCU&RahDQ@GyAbj011K5Zep+UFsZqm!|J+uRN^kt%xJg=VAI7_A7&B(T>t{C)uAwKN`02|Cqff4<){m${4*fWDXr*2|}msQUlQ_hAM zl{3@#s6T|+q9m)UGHL%g^&Ci^5YTz!@t9L$z6T&>h)Ah&hq}rjd^TxhgWMRbh%}4&sQ3! zy3ih(*siE*U80~~ssMJ0tL)(EFp6CmEla>)`C`;|TY}{T^wW`V(#?p?#jg%;XZ*aEdHZMyaZ;hfd;s4}}u(3`4{pdeCU) z=V9Z?H7{Z^5+sBy$0jK3Z1Sqeo87U!TiXZAsS8}KrgYUdWmDAFl*@8eaUyJOgq|lD zf+~G1eowo51u>3TYsy8zGN~P&VguqM;%Q1iAUA9we<&s0QisgtwYTbP?=fPCxZ?S0 zQmtR($yeGU_LIT%^ksbP$@{_n`zn_&5?g6jRoH9cdWc>b5m9q=Q+apJ z9<@L2G-xok``CYcQ~9Hki>p2RrM3Qcb-mWYE@3nD4gKa8V^P?FQTDf6$1!Ze3Xcq` zv-s-yXBx%8E+D@Y{bjpa+2(gcNZn}VQpwzsfZDMDf#tcc30b_k0yTA-kWMO%A0eo7 ze`xTIYj>5|5QKKxI32)(DfGCyxWXte0hA0YhkZ zT$e&^wGEJW@w%DxDZ-^HdcK~bI`dVovUbfLnGp-uZuVnB^4q^jgl~@K1(6bNKB~tg zPAdgcsI#1DgmAso%;WcNL)u753h=H+(9<^CwMvyN&*F6zU;EQ|m(;;IsjFTP@-1&z z=+i7)Pj9n>ozj`>lUg>|6I2ZJoLg=15ecg<^YvyCIsJ9&n|d6v*WB|B&hyc6&yr?c zh+!@>A2y>?8TH_$qL@!4ZQS(|0TZatePT#wKLR0ac2m30LowIzX^M#`PNDI7UPke` zhp%FZ1h)kq)#X4e+fg;0gF9(iT)O9onR#uZ=H5id#%(rttO-4osdAa-R;n!rSCy8` zOq>vU8=NlD+UV?+SjrO8G`qU^J7rjO)UWnx^6Nb?3_phNOKB`QHsG@BUDi&{Xs3N~ z{vEz29&W9F^O@>XJ7R+N{gvwP{PTp_twOftomejD`plt0bEFsdx>r|WsRvx_ZmVm!`NVkg(}mX4 z8ZTl_UqIne2GjY(WMb~+5yIQTPQfZ+Y<3)gy(9Np*poQg={zs7t(7+8GB`{ZE+r*F zYbk&gGS2_w6=ik$QP*6o-n_JwmPl)fg3p13ITiTB-r6BHgVY41q(2}c0uFrB7lnuX z{JZB_qX;i{POIFwVBOk+N&)fGD?}Cb=|<&;kzowkAmpsk&j@QJUhQ?nHg#B=*!iU} zQgsWN);^tD8KiQP2#&#Qje&8E5O@it*y@%CcvM8A<8 z)7MJa%~rF^*dE0M*-A4Xq44wdIx~A)(M2|^ z23c7g9DrQ2abIMZ6C`xR7?$}v=%@H?t~=MenZVVro>h=kl|9(Wwi`AdE*k=C{P8|8 zM(nvLD+0n6YzR`<-T&}F14qU*Dy3oAR%=pAdz{9@Q_M*dZ;JFue7x_<&!jRVJ!NEi z{c!BRtocB?=gI-Y0D!TG(zz|`4YhD)U^!tEfNRw3UxGZP1e*Oa;CpgJLPSEj-{5)r z$4$(I&)!Je=g(?GwPwl=KPGbw|If5~6cGCFXE-=BU;lSY#t(^Lyk!z(k2bh$0I-YE z{6~(`fNTqZgA-_ZODvEPf4p>OQ~xIC;ZN*@ED3I5 z!BpwqbMFQzOjCB{c1~QS2grXB?go1m48)QTs&}((%4q4g_;7HB$1cA#+3@qzd2QY( zU7g3+df}WfTMT9T9Tjazaj>0TE2U$}47vw3pFkaQ&_`IRgXPV0Zhl5|=Njw`y)MkC zM~Bm_yx`#Pf*NOKGFuC{+RfddTE-6QG11D@U7X=30pBKJrrx_tS#N52aJ(zz>7cDf zt^S$au7mhEIBg7A-ox{f-~C(ZRGL?5gCrb}0ug(oH)xl9iz^J%5SA3daH|8u%)JU> zv0Qfp4Wn1EgRmsOB}x3erybtFCd#V8bjR`21F@lZg4nqU$lnTSXDCD5qUKSkD=W^4 zvra$GkvMj$eE%yOWB4JY7iUwI#svpQhTwmtTF5*YkGL~T>d1lRn!Ng7AN|#w|M!R; zjw4&GSz)eJI5?Y5L-#EImeA(qqqmd~Wssn&IvDC?Ypg&mE8;E?`VO0*kJnI-y&71@ z=G1cV^hdaYar9+S7q{G!z;CzXe1IjF$N>L-PP~=dCcjQU8aP0m-Xd zHi_<=TR}a*@*V)6Z#O}8B=&#cW9(ai|J_R0a=J5}1}n8nLw`=cby&n3?s-Xb@FBIZ z{M0GZMc^<6f22qdqFipbjnZ#jNXWas?g-Gu+$2)*qYRli= ze~=plaF(9Xi%rGPO=MkPbcE45NY<~Y1sK%sMNxAh`||E?e!888Og~q&E0!-KgTi%a za?dG$W{9>3QbU}fJqVHzr-rDR%Ott#54TIw^kD;A5e2Hyeh%+?LQSjO2itUy2B zK0JAOJGc`G^c8WKE6Lv$EM?=idCZ{5Bfqr{40@$=+&m z5Oa8Rcbo8*oirbDD@wWz&!wx*Y>wotiSn)EEVNti!N@WF`-+t+|1SjtSE{P0526(- zrFa`LDK*!Qe!~)~>u4TEll{@U+KgRc&*jfSWBm8~5A6bcKkR?s@plvOoOzyw_8O_q zYa|@|zG39^qLZ4%zt-Iebt>&;b3U!4na`cf>pYcb5g~m2@w`{{s^@mfGoLHCvD&4u zt+wxTWwC%ct@uN*;PWC!p{5DNsKyDk2$>Z)?Dg>XI_3%J^T=Nuk;60|5g!MLR3Ai9 z-5l~0aqIb>3)NX2nII+6dmUX?1oSsU=L>#zOXneWepgT>rd(8x8lu*Y@DXvkr zXUCTT24YQCxU=!aN$A6`kD!>0q`TP2ScB^vDYulUeq`HPalaqTuXeweJgaZf2XSQ} zgH5DMioWdZkI4(IxB~68bvUwsFpf)lEZ@IhA+FHrfefx{{!Mzou7RiIJax(aW18H^ z3yZ%4xpS)v!1U38&Eb!kt$&zf)h6fS`F-Iq9eQF*0v}&kqr6<#ocqeJbqD%%Nol23 zt(#avp6Gb^3+5swC*Qh3iWw3EfgG>-{6Q*!8C|Lt?u=1cK^ z;g7D)7=@EpSLrGB#W%|82x8(lZ#yRW>p9fMxSfW$jp`kKJoVWR*el#9@~+bZ2J6pA zpNMl&3n&6r(7Ajn6FxUKbDLlap+zDBJbqS8h_jtg*UiDZ6> zb=#$50%i^(ekN=Wfn|SZLssv4(9;j>BakMd8qN?stYF}?{ng9A_wD2pfF4Q2zjx>? zYg*_rgM-$f^S;{R=$tzKZ6`hZL@0Mh*mT#!=;FgUJj*PsV&l`~!<}}O2iQ0NAh@-h z!?+XlH#_bm>b}~0rtjgO`+>F1*9AxGq*+r(2)fu}%1El+OKGRwI&RWw)bbU>{bI?M zmHF4POAB7iDu2I--z?52YQ3zf=d*hArc z0%?j{Mq(hB^GNGX)Y~Mix1ih54-EcW98PWEY&E3mV05z+F&P^=1hu_D#nP`DmVl`;=azE4b>AjP-d@ySnKO3J2Zk=oGrDn&tMxdC+qvQNRx zsl%$Vqwb*LmU6sUsS1A7=XY7Nk*`nj?yEHSvu4-xWV23(xkbIH-id|A{ZFrDsj6&e zv0jDC*?ey~67T#aI)sQy!g72O<#$E=J|>SIwVf6vgToiohJP@qC16igCWOfori4`+&4?}ZMZCR07cA4lL8EK zdDdPTA1t{avC=q+KC3w=I1AXe|5(dVDG`gcV}{b0QqtwHV=i=w>}wA*FP!0j9+L`m%&|1Kvp@ur9&;g&UE{(e zEJ7i(va}p_LmOdGNum1LDUD)7J8h1oKYuP|VJ`H6TH0uMU5nhee~jBe!+h*87f|pz zW#;(M>tp&XVFBNXnur}J7_(!9v9`3l=n`)E$d`hJhm&pux4s%%0Bds%E)arY|Ykjzxtn(t8m#E@;gpjATxRncZ2fvU&Sy0umhWHwEv4P zuz*Ev5&w_Gr}vXvZ5+tr@|GtgVmnwaDz?a!Z&0zfk%qc0X+T}~0k3iO{x;*Bc+Zo5 z=B?%U_v!xu=XYuOEhTnOZyqm{w}?$TmVK(2xs#4vBLi3UtO%}~nMr`bV=M|6yfrqK zA;G~em`h{;qrpR*W*~O0k0u(*TdXfoos{QWF6y}vScsO5V`qB@$FexsR?xg4)@WWduL}`yA&7E?1zp*D1Z-MFhz~>k7I#3s}PQ?~#oB=?w=m zqPZAErIIOn`X^*<{?{U^iiM~IBdvz;nWyfCezf}c)y-3HKWe@6%vM``|NU{~g7mM& z4Mr2FV@0=rf@gmtqFb1IyY1dT!?E~zWOirG?{FzN0}&!?F-WuJXIV0PqVAU){@=g1 zxxVFw9jV%TdxV_-bVG3xRNnW-@JdZJW$|D!{B2Rs^RVzLIJ;hBM7DntHYaT6+hHz7 z<_RK^j`D?C|Gpz5f3kgZB!#$!O7}Hx&3pv#p}Ib``W=1`4c(nOS)v>(I3u|1`iE-kaOz4kuS**}#SGOY7^Woh;M{-yJk9JzXJO?)weM zP#m2oS7#95Hnk-O%)MEyAQV5Y#jNF+Wa*>}0)fg-XFp}jGyM9a_{->?R%-r!9E4ep zm&TkW!LVPdGHyYAn$#JhC3^=^J4X)qV7Te&MAkD}jcbWpP>^X-hjc%5!cWlie=~u~ zI1C;2otH_i`B}F5Ee~jCMg7lQ04y^`wtK;UEg~iW004+jI!KG32H(W88T{h+@jpe= zw_Z7|d~58QR&z%m#u^}7t1LUkJqU?Ydi@T2v$Fhl4^PQey0^$Z+&VvPhvTLp{IB~` zTCWFJ__=KCrcb*wp=KlW#gZ727{BfEiuVD=6SBtv73_ugbCyajJS{rM#7%;Y0@e;{ z{iXg5HND($uIp?HioM7U0nI0`m0AeB+{rKYB_(TaUC|0XmusW!_OICt`Si+=aV5Tv zQtB!|)^*>*c>`vj(pnAvHhI$QCEehUI5Oe%AlyXU^cWj`qb$7HbXeaIAi!>NbtKZO zZu)*FBJ=$GE#AfUZMB{;%ds%zq^Bh%i8Z13^)%j(`XcUB z1X^ftfSt<$qnmlQLu{JV5GSK8jp#esPax{sA!^e4=%PYKq(7=+r+m9Sw9)&xpBUq8 zP;+j!@}*AbdNGw*`@v2OC+3ge4L%S{OI&2BQ)#Mf#lkNIETi*Hc5^zoLwIHHK?kPGLx*M-Lj zf6hj;_Zw#;>9gXZg>cuuyXr)uxyH~CJ)+{(61WYaenV{QyHvve-W7}epILF88|Qt&%=5Dy>6d6!R>{=GjK{JsX}b6Mp@$ScXnD_}#ZCEp zv4fx8&L>&3ZlZ)eFJwhAmX1j?>op)eI7}~n`pyJ8IF>s5YWz>e1s+zc^d@;C2--X3IjN^$`P->}yi1Vq# z8^kXLkcWo7t~c_nW_$rxn)j?qIHolx>%I`)jptR$^2A6wVpoq}wVRuTLVe%XB!JjmMFn2xJw14|^ zt6LjQj05=kAnT#-zSXT|k`dU-03#@Uj)hQkYUzRumNZlTIR&aGl2WINh>?AEkVs!$ zd21~=s5$x7WbP$K)J06 zy5>nMUS9|kOV(_VO@781&bu-E%zMcXY0D)|bvSVkr+L)kUU6MGM{=t7 zWnl`2UE}6YOXXWiY^(}LCT5Jfswut^v9N=&m{hBL^TLq!M0kHlGj#n%2&zJjnk$fhGB$~|Lvum))9x-a2g`L z{_ty*+6|2X0I;E2!tk1VP0tpywDtTw76>P2?sv|-QN7qF-6?K)0EV{G_g&5V9W(3m zzl00DJWEw@LHd}Xf0|38?mddRg2_7EST6UxpY2(=w4uE5TM(Dl^F>=?Xfod$J=@AM z!p{5o!&5Ap=|D_$>t;b>5C3dS{w&weHdK}wwFt+T{I>Vw(~?Vv|K!}&AGjclqlXXg zg|~6~{MGV#d8>UdM6KZgZs3pxbZyWNP-kA?FAHpm6}#!WaW+3KYp0bOK5O*V>MB%c zO^OKBPZVP_883`f&*388Hat}x;a|w8KKTL*HeE;+w+^M@_O$8E4QeHbo|2mNHG&HY z&z+uZ!%&31&8K(-l_5Qc6ELoZk6m*MwDxpD;XRZghL`(Msm)uh>P2&_0B%` z{(0@)_Wrzk-kG~KcDdr1V|<~1y?|ud2qic-;nC)n*sh`D{9}MED9f96D;d**NYkIb@zwsI2xQZ#>*x z9lbnWy%9+krH@ao*^s23+xo8y{}R5=z(D`!Z+~;~oOqWVZ8E5uqY@H0!W&wp9r7jN zpw=b1&<`mkb+UyaId?{1^*;FXLCZu1ZX;R@s$%wXADU*L?ftkH{Q%2>7$0cZpOmU)8-wvX$Z7MA~kOM7kI?a!$9? zf8?ymKndlz9skQ0%*!)o{LR$$jNg+aN#{~4Zqt{yOZqLWsWs9cVd18_9KH3`?9?CT zr5+QSbLkCa)r;jbSxdk8vBvfz!2<`UcKj6Auhv>O3w@{=F<(7S3VZ49dzL|2%(Q>w z(kWZxhV}`?E&}6mbA>+boy@{ znPwY|YV)Mi^A+@Rdh0#Lu?$kQ6PeXNH>^H4WbppjO5=DqB*10kHJAC&&e^6u!e^G$6!+C95Andfpof}yC5)E0Jy6iL>XF`xB1vT`M`qa$x+>PeB+ zNNz1|UZTbFd!Cu3ycUw9Ls$j_{> zaXc3c98Hh^VoY_4c-E?!^cLZs)>vs5%BXTB=}Q2I&zUfL+e+%yzRCkWdml@Xa#nUm z_rB8E8po1wLq~dusffJ6?|Cj+CTl#|;$d55X|qlaC5_S(v7q@PS*UrkHgT!{;MdCG z_|yYrS{jo1z_zW06?<3=eQ48{y3ZG$w-NLBu}s=(CD=OT>n1XtMgP!TO!^WnyX8P< z=$bRXz03!OBf6|(U^{*a7|+tOzo0+foiJN6UHwuMn<}mKU8_4yLAVE+(TTc> z*^1inHcQ%?6`$>K`~|6#f^DBd6VDc&YC91)+Ec$udjG@TQsnxvPn_p67RMfMO&-1K z-1bn|oneq15vos`Dqg0i&X2^7o}vrnaLYHYCg)aPs%C1W6$h>dUP&%)`J>lkdU!~) zkJemEt&mRYTa;?&=C8GNlUZBMw_Mx!l$>}r{pRQUYSbKIiQpzxi(ts_>&p}Db`}m=X%D7b73fG7ZdWy_8`NRW+QQT^t4PZ{O1-)E2J5cE zZQ$ai_7<~tuzQs8j|;g>71ojD_Dfx`JnJ~)9&8-5ny99x8^+;rH$Bnd^zT|SlI-6y zvA`|A07Ot8CI_dc=5*4uin||7{}tcK>qM)o}Pju zbqNnI(d{m!iWW^)2+Qy&D0O;&6e}U0yE&1^K@Hs*6hSRz)~DXG{YZ%nUUr@TGT4T` zhZbWi>y`X@snGPF89$jl7^MD1W;-`|ekz)0kldxaIy4p_xnlL`n2|8In}zbVUDX{% zu9W451(uf-y|xE?Zdk4f2TxM!!gLd~=xBUw#(DdN?G7MxjnymoeW zZEClTjRN}%QgUxq^&^JjfsCCWD)RyldrUjHzOhrDSRt<&d`mZ&+3H}vk288lFpxgUR*R@qDsDfV6YEaGrzn=a?^(^mj)$k9{9&+EuFW;Kdz!sQxQ?o!tLDF5aaq?Je8X zv&ZY!PUPTn{!c8Fa#C|)VZX4N&{$8GTUoy(DR<&#?;^dY&{93keHR|q4u(?~dMb~Y z{?L}9{keB({xJKj{UNz*VS$>qW<^?SJ|T^{P|uL1A7z@7AAY83Lt{=Hy>f50BOFItS^Zn9__xz>J_8r0H3y}q;;-)udW7@hWd-&aF z;${TN4K~)Uv%RZ2Wa;Fgphag(6Q8N&{LsA{mYvb7`2k_`HR}@6>JAed9re`O&a80% zt(>~)stfL#O{*h}V>Sjff4Lnkoy@W%6-YyOoCzLs3y$ z_tBJFr9n;1$Cs-Y()=|$#g@M43HP^dR}^GUvpdB8?Ms=5xA(dk-HxeJv-%e%W_lfK zS!#^xR<2e`!l5puHb*2QviOvicr?Tosfu0I9{K}G?448!pWGfcW*(1M{iAJf-u=4I zRxIu&n!~*Rq@xQrb)?(Pf0McThp5B&*#oOxkNt(DXvh4m5)bXpneOT4*x6dw!u!2x zMf0BdICr7ohGwQi5%Z1cR*@B-11IQMH+rJp{(ShGj`zhfFE-!w_z`_m1D4q?he}KG z{1@Lx()O;Cj0c8?jNDJL3$Y7vD=f*cT2^Z(Z&fd4=LJE!$K$=#oA>e2KW*Dj-?H-g zl(SKeLDIPQ)xFl=L32L=rZ-uZH^p@$^mBCdHgO`bw{HBrAUT)*gGKAlxrA!wPY>cA4JG)Z5SRvZM?T}LY-JPLom%F*)IY~5&RLVUX(%SShi*m84g2|6g!u(}n-RlbysQi@eMU^0mb z|NO9`ynMIR4^e@i$2UJCv`FIW|Mw|;&;Na}++VB&TlMR5`uBN!|Hr)We#ptaPX~E6 zeb)COJwmZ!M{se(bzh87HLjoPJAZno-=^=4&o;tuT?eBX<8Pu&`s~9j9I|qRhQ@Jt z-kCgKC=!mz+{T`po&73QOTa;ghSebCUUhY-@V@Pjq~2cV3{(n}iP=hX|Jke1P~im` zJ{}02OlyW0BBaI^Yb66v?UntT{ch2=u@1N+r>>-?;n5D zGG-fCG|UD;`aYB3ayCA^Uv9-)Gr5I%OPPwQDlPlV&Quu?<01(6|(Fe zx40q>*I~gk2!i;#`HSgB^IDDPWHLlKsI}&nQuK!nO30P%;P!Ox=Y_)I7%0G;rBAN`MEuu#v&aZ}F0%F&jO>yRT71v4d0n-ck($DKn`-iqs zN4I>ZZOTd*hy*`>{g<@J`uNsfR+7nPNA)-{cHBer)#0>oR^!%q$yb^LC z%*XLtC)q{#z0<6H8eGadbZ2>9@<7vPrq~LAyIpC@BQ3N%2%bNiYVQrsmVzR zgNm1)`(XIrpOwvyUK^FwR9C+mb~eSRHsVHCptBc!)caOjPEJl+k?_~AHEbAR!*>)$ znGUik#;V{@ubs5Ce6~WVX=x5qU5RzZ2?+^t4w0 zy1KfWni`AtOp8g)(dg9FGlGH{+1WoD8X77p;&Li6c)yVJUst?ZvV7QC#ZmKgvz8H}Tr_mH0%GsOR|jK5eT^jP_E0vA2JHWi0vR$&*sFM4Q!hJzEy0 z7>o~tJAL|eY;0_`PUXWh|8fc!u|p2TH{%Z>HTH9Ut8zp?5r@XQF>Wp<99MMOYINJv7WAU|J>kMC~J z=fAnNGV|Tno$uV~kCF=>pP1OX^FYtgkdf(1;YJHFl06*q+6AuXgoRsLThARn{Mpfh zNgp#KD7Y|K9g2JF%ySSH6cps=m%Db2Y&%lFM?tJE)u7Ugx(`xpsq#hQdc^Qfztp$h zKR-Vob8Tc`Pz{-{xcK6M1(KAMlrO8eGuGc~6;5(~{+68wzQ-zaJd#o*zj*iV&y!_i zUF5uQ;_4iz3|3ZB0$xc=v-w(JN<%Y!;Ll34!;hqXPE5Ehj5dAv@L_3bsi&u>ZBV8o z*Uqdn*Dfe12=iglmR?>{(^W_!Sz1nJTlaBsaqXeanc!?q%O_UzHp(t0H2|N3=nrbRm)i^z$++xwzlnPxcS+k<)aEIV^EF1^1ZE6d5l z6LH2s)?$n?I4FoZ-vBebBJ~)<7#bR~!hJsHV+s>E=78z}8FXU-RnZ3Ya#=FzUJ1efxG)LW1hW=Kk2nhkS3K;^j%@T!*QoCQ}l1 zc_@HGK{w}KaeI4vQPI2f^!Xnt`i0b$F|%ay;OJ=F^%Jskat@On_|^t|JV%7%JLh9q zW;ms1@b*XVGD8rTMvvkCKZO#Zi$fto+u+Q3pFj5OSzVl9W@0k6u;6BSEo7njpApr5 z{0ArVwWG(5EsV8HbvkszbMovb+Ayhzb-2u`X6K#VZr{GGr1T;!Z4MUn!BWB0bQF#r zm!6)PNo|Ov_V@I>xqbpKgID-TQXQ#lPQ1Ly_>?Bq`eJWJnAgg}n6UG_v5k$*NFN@z z>;1OP#btH0Ru*tEARu7IpVJMx@cpe{oSl4ndwRYXk~WHpiiU@ceJ&~MGt0=x^c5t8 zhwB9M=m;=fyLIcWH9x%R{(Tx{Wo5X);2>s)(_?ye_QgFBe8jL*hNg}~C2ouE0N-bZ{?1_B z?VIxttE#IpMgiXQwAMp4;Y`f&O{1sOl4#iXhg%OgZ0%CzgwW8?bY(3&#N>kq54N_pzJC4s(W6KB+ti+J)!j9{*4)^rQbENi zcCx<-47}k*$AY{ESU$FZS4+{F7|I07A-}4HM9qG(+(U3WC96Ml3*Fr|sO!jYQjhE2 z%cvdHGODVpOSu**L{B}5c;R{RsaCqNVtb?J6%7pyYwHv;*$wx!YpYpUFwphf+#HNz zZg%zzKR^C^$BrFix}M31K5yO}mXstR5;b)P2JL{VXuSQ7lTTBw-PoHqZ;%5@zkO5D z%TiBn;W&0|qAf!%Q8f!DiZG0+u&R4^(b?JAP4aSEqSQ*xyLazCe7Hc1(fN(;NKH3s z_?TI|3bYXNQYqHf&TiMXZLQhX`kI;{=`(5;J{WDCZTS8SO@vAA>M%hG-GM08cyZ(s zL~sv=a8ZZMjEsz|tl*&Oys6^E#KiUW^d>HfvB%F#8El2&V~*4EaSE_vd{ZtdQmsH$ZUmG9~HuCb-6 zrY6ZEykNA^)DCa~gNh1vCR3Yn3s&8hcTVG;Zd@jZBamJ;D;K;kA8mGLSE6QyX-krh zs%nt6l}vPl!JRt?gaf(+BPO&|RaN_#TuY8+m^M|ElpGfo6%`hyY#OT@TM)MDs;aEy zKjgn+sF}F<_Y}0P`&?7(CWtK8uRmw#ogJub>RulR*89L}aq%KC>JJ95m}3#?Sw!vg z@BS=G(J!s2tc-fdYOZ-`-LQy7FzU}_2}{W3{uH0 zMtAO9_8zbsYfgm6ERY`vj0tgYL?efs4otV~)YN2}LRf@}JCk*jTlBK;va_?lb?#eR zbB&czQBm1@@Sw(khRMPUq^n%}2~Ll*u1j~FopW`Q87ZtUBP85%OPrs2ds}s6TMtz1 zJ4sc3QptVcKY9V;n~-nh6%=Z*wpA8rX(`=hW6s6|JW3*Myn1z@?;&g#5eCE7$g&K7 z_)r=i^8WpMKR*Q}C8Z;h$UBy0&)E?{MG)?^wZrfV#2R~hdps1E{D#8=r7_GYfR%R4 zRs`wg*|TS=iJDH1jt3Syux`e$ritrr-w~`T+u6G}S(x=2KZ3)+j8>JEE%kcywq;s~ zy01AhG6rvXdB@b0>A-ggWssN&Q`UxIj=3vY}&L*Bs}TOo6p_dq{bM< zCdSK_GzigSW1>biVO?aG9qdZV%9RxruCA^ner$T}o9`n33kwTlg%QbssEO%tc1{&S zK5)!d;cQx(>Ce2@5-g0#=VNMWePjC1+K7qq@sHO+o3pI+KxBPTsBU%BG{K+MR0DL85!im?ky9)c*-_U9nL^HlKdg>(7bD`B=9gND$Y5 z*W3T`!x#S>EW^iYozK!X06`$W{+UM7S-6z>kzHh;K0~^*U%*c1&J>- zD#vv*iQ11NZ@L34zj*OtkY}2{AJOT^CG2+IDdsS#ZPS8v4DontHjn+&p0mK%jq#>O#m$8T^}Ra5{mGK<PeVI@pi^1B+jSibygd0HzpX#Zq*Zio&r5?w` zoM2;Pdn8rYL6oT<@AZCr0*Vv3w54vazdQippit|I6m5nk8EFyF1mIaD#3Z9_^Hw^< zMn>EaHvd>vaf~jkcr)@6lY=5>Zm2f7>o7OBMfrAX^O!@$76L>GA#&8(W4=B*$JfEvwAOj`3V3MB*4A$@;&xHU8+ zq@=i5e04kxDY04ygrxBwy1Kf6$%=}K$SfJ+#U=r)1GC;o00EAu|zfD=T{3G0<6($_X4z&nEAGcS;+kE zy?ZYTmI3zjS10oTMgVu`78fo1uVIBGnGnnm$ws9Dz$TLy+X#AUY%I^ZujGh~uYiMM zHBoqqh=`mx5rYB{U>lWcwGNtpjX6;j0Fz4CkE`RxEi7PvHcQjDP%k0t2-}G>`4|pV z1nywuK}F6+TATTBCBW1SCCiFNnqkz34<4SLH4)-@r`bYYy*i7E67%eL;Ph<(O~K4_ zHiK6k9hWM33lIJA$Dh^H*H7$4Hcl>B8N~$paMY@*sJsjeY;A96W@T+DYj0nD?l!b=c!Zg-@W7F z<_4G4i8>9OLCLpo9S*x#0HTyxL~Ms)ing}4U{ZW`HJUE?ACbXw{I+>BDe{H6xw&eR zb`J74W~wP==}At`vL1Cd7&wo-He2ZvrgH1{?b)@tS}+Bq;-~vCQAu5YfnEV>!<*Ju z7Wi4oAQylgSr`}?SXgv)b$M7CM{ENF1F>*+$x46x@rM$595jXPM4O+L&z|k<>(fjWI&)^$N4RU6LQ#uV`RciAb^0qk>Pa89<2+nkVW;&= zloiBpxLFJ`QQieM+rK_%^U=@GFTHUFmW4%ouM{gKEiK4&&En#yZ7!@NK{G>E^LT4% z=@7uIq|@vN`DlL2kE-!N(P9P_dty+_UWep9dZeDgt$qj7SJAeMXPM*D;y*(W* z?epgEu_-At(T$mFbDNvZmxADVh+I*}qVybpK0+xR+X{dm#H+`8=nxl+-;*b?sb_2- zS{(#NeXSIGNWwMS%c~TVjiO*-3uwPmKQ{Vp+eQGD1GDCx*YVy0>WbM2C3#+S<@4X0 zOB6OmNz^|($d7P_3Vf*QHSUNy7rD`>>i6hTanYk49P(j5P`>4!B28qp7*>ZEylQPb zfJ&OpZQegGFHfK+iKsT~jKL_y8F@E1H^Ze-sd`!^$HWW+D}qGH&`G2N+~GcT3WSv4 z7$k$~VRozq=n?0|aqzvstT(=X!O~=PySofly++K~v3+}ppjnWQPc=w`NVmn-jkO`k zUG2sRZ{GBPpMXhNf_^|{e#wtDJS=Rj;#Be>N%ysap_0;4KPJJ^4eHcJLSmv()vMhw zkv)6(T^7gPRu>Pph>@sk;1o?W+79v?{%gyYj~_pRwi`u|rI6i0aKJeK`s+R{DJseU z^$B3Anf927xVRfun}7qAm7xMAN`>z0;0P86L#=eP@4nq6C%4C<|$Qp@p7JV(q#mv1VbQ7Z8Vi*(4)gyigiQ1SJPA(9qJ_ zx^=6XEIoA3qY?a=g5Du-{qvbnShe zN@T*Dh7r_TD9*8_K7Q>#ehA!@$v_#@(b0kR2z~WRAWjwTQ2I3>_c(iX9Jq()x^YdT zyah|WK-owak(hV^dX!+`Evf62ZoQP-k5K<(HbxhA@7uSp+jXuQ{*Hy>H>x=VK+W!< z8oxdg9SO5^?`}ZyP)pQY=nqg}QoZ@a+uLO*+`)ZqrW6bS0`$nj3MMH+%;{cJ91cBS zPoMuw{e7J_*?FOW-YY}XLR(uq%d&H)a8tY*FSzHjvNFZ@mkvs@@}4}oZO0C|%a_%W zSAf_*B*kea>f)u6)EU2(W^>~l7-z7q^GN}LM)3H_Eub2>)IaqQn?pdgAmDMV@IB|OS>=q^uqKzIXNBl1M4Rz>Nwp4N(V!V<9!>i z!bxl;GORiv&A~1xXa}|eNQG6%{FQ9fnRLWyQHM#072`qNTBEubC;iHcilm~HELu~K zojzUH+1cqhYwqm4@@NO+Gq0^dfr0z>?oH4o!}b-Tly)6Ddv~_KT-f~Kn=F088Pn{P&~0$LP7#7#mmXLvOE+ymKPM($N`RW9u(p6 z-<_mrQIBogb37lFB1OCtG5)nt#OcX@tu8I?JivGO$PsDH!DMjl92}*f`%6nppWo3? zR|j+4LHUUyc?Fb-tD74O3ro}ya6xc(RE~*f9G2FW=eE+b5>O-hTBu=mLR8cM68#Dy zbxW3EUtg`nYJ%-~6Y-H-Uva2g)Hx9m`EZe@yROqcUvQ0#jMQ^>`s82}{$Y>1Ha5&> z@Ad!-_>lU>7FK`@Q1{_|O-rYmii;ueoGejT)z;D3x{C?)eH1v{z910rNGu40jQt1t zuOPFCiHQ{;VIccn*>Qh(VFmdT@i%TosS+uR_|7k15L*FBy5TN1wc@arTzFVplAfYs ziqqU6GOIO6Vbq7$Tu^AfIrn@(FsGuT0%4Z@+vD9w;wXCHnkNYB8ndT!sJ$I3r>g$!*J(Ob8KYhx!9Z|v<124|Ae#!(d zCVkiB)-8a^u+UJj&$nLD=`R`L`8ck@Kd)6LT2*fW5GeUNk2w0l{{==~q8E%w^!Bbq zv_iUSDANML^oxv!bs%b8x_I$m5JP!oC2Z~oLODjtIxVvOQ4t|wRNs3JX6we3P?;Rc zUOfbz)YMd92cH9{d!vGNLj+9f8x$lv@zF*RP9@nbZig{c5$Z*~JZZ}GJK!CEk z`VVk#X8GBd-Y+8nsU@hNwi&F#_HzC&`%%%pc;+HSx})^RQ9FSqQ#&v#NCpTJ3qjzR z?%e|cJ9rhyaUtrB8FC$ft_f;qv9WJGn>aj>DaXghu~?9UCMp6?VOh>}IRbZ>gN-Tm zWzovMdrL*75t-4=&5iBDm^SE~eftJ}{)Eb5;o1S>sHH_1Y7yv#ni_f984#<3{r!!L z)r~(^6Y>l6F0lNL}7v1wGu_d8?|IzHy?00^N%eob)GeInS+WOq@$lipYgey!!R`=H4oV6HLOy^MiHV4;Od!h*1giCm z5}V8$eZt;dxx6X_J!Km^G)q_?IJz5Ag~v{V+s?>HoXXBenAyK?Uj`tg?Ae7Ls6_~` z__aQE595Z9K3-m4!NF>%DW0*-IlH)!kQdCF6IubuBE(&s=Z9w?|4A%Qqw%2ne7u;g*fi<`y zl!FJ=1)mAobXR3Vf=1d$S)Re+;iYnp$f{6bEBFPlz6*-K{oI=Kzz>Z=nu(BjvHJ%C z9t`2r)5!BMVfg6Pt5tBYT_dkln&s$(CQDj(p#bwVX1~n1}Ul? zwchOPy^+Vd$&yGs;8i#O_8Z1_wE7|nU_ZXzCtgTvY~s!WVq&h)6P5Jh3@U?ocz6in z4lWm0`wkE*4-B7=kB>l6&3DOoca6S>0AK_W>$(h(Z@+NEhXnaF*P9?_%L6zFT`iwW z&cGn*()-vh^|#m0iip%;axh{n9I7J%JR%JEsMXZgO1iDs{`J@Ap-2)Bx1tf3u&~3I z2fqU!URF?mMCXDmi8kaqj!(*Y$0jG-QPPj(KC)J^vD_2oHrY6mTCOkR{`A`u;0lEQBl z%_0S2)DoDXV{$=aJ=!Q!4x5d;&G3!b=0e`1E+ms!BwUwJFGo>hp#NiNp#3Nl?P_i` zZJ;?}YwhqT(4(~Mn0T;#Hb^i;HDCx)Eyr=@-OO&(!R+7u-n=!=L&G#fGvtxx)>s>D z19wY=Xh1|3BcwK28<9A3fm>Wq(S?LE0Sus^$5AZ-_@FGHfQX_`{qza)4@y=^$9 zJEif`(>}>L{;d6g9DnUYsUq#UbGH=2?#a`qtv}!2RDWr|ASW+xZFM!MK|Z=!Sy|cM zzCMzd(#B!nt;Ie+Njhest07H_TVuzDCntHT6|cuP!H8|_?IT1TcnIpyZ(C}b3bP=?X_JS+mRN3dU7S!vFc@luWV@%G*$MX;+S`^G**kRfYhP~K0UdN5!* zW)>DYQw`)C9kXwZ{XoSC7$|2(*Bn21$@DYfaZEXRdGsjD5qRN2AZX(C2Q;w0qM{J{ zY7C5wKYF(n@X z4`{dR`}z72TbT3`&?_o|oqDn99O~FxJRA?iymZsoV)MmIm%6g8rQ1#m@$>U@b6*pY zaGLFR=yHm#c*+M*FqJs za_rj`rKHh*XaC8qYgi1BAQoq>u{Z?B1^_OqmAU{_^!9jzrTba0+fej2#|&jneqCl%JJ4TxEZyC*$Tqd0xDKcL*Td@la%xD z=`9_EPy3@n1YSTn%MYDRL}ZH;Epiq|r0YGbi&~QQww*gwPV7aGN<>6Nnql?IQlB3w z>%o{kVi(L=mR_6rN_4{`FRx_nocga{ze2|habdTk&9fUb>^Q^0VT4Kt4p$#np(Gb5 zn*k0EQn!>TfQchGAP8aYd?kP*D5mmf3}70PbZFYCKv7v$_0rw4;bS#o_UghIrBPYI z4vt;&uWA5G5ba*{Y+V^5b(1#ML4>Kc`X+)_lMCX4-qwoTQQ)=;;Q_*9 zgv(?OviOahkI~UX5CnuR+HC*nQGrdPoF3~d^#f|yF9iS)5*pf~?usaY$?HPxhR}PN zgGW1i8d+xzsd$$K()$8dUsFS)_}e!$Dkw!MUD@O1GC!=Sb>S7RkPs2m#uI&~pWOKI z;|It;QJ~yE|LlFj5y5x*^ucrYJ^&I5cf0OkVId>@qo^ue8{mcKAmx+X%>H9-d;kir zU;mEWHx{u9;F)4vhqRW3vJ_L1<{P14Q)euI`!6UcpdV<}1+f)<_4T{!CqOG{$#`9- zJ$?JzW7LcwaXDUFw4!mvRN*7zuXl_LwzH3_v;Kaq zA0;Z3mNnD~L)D=z9T~`5W$kb+)RdBjRj=R+LJr(0Yy9NfavHndj@rMW-#s=U{@_F4 zAVj*h4s@c@l<7i?bayW(EF}GE-8%d)jjs3T(inmoh0(%1&3}m|cc$PeV57oCg?~2K z5p4r9K^$u6>j9dpMqAR@g6Bhoz7=?~e1sSfkOCTc1oVkES3ggTglHo|B?_t;k9gdi z+OZ7R$I^qu5g4~rg|Phm`C*S&m@oiQV7ZXIOL!qJv8&^5>}rr&VG8h-C%(Qxe~Jal z3c?3K7r`Vr1D|CR8)`90I_q>UdjD>cq~ElPL$3aLhW9^?m-;`ukNDTk{J+#g_$(%W zV+}#4$NwbD{OY;;gO!PB?)`PmP7gn;bb0v)s0-qHBl4fm{CDdq5-=9OMOJHoq<_?yq`rSNnEPC)k7beF0QTVDjE4>-AhmKfp_JN~kCvQhO^g2Y=~GVp zs<_JXmbLYDJ*R=dslpBF=^S+4A4a?Zq~r`71#MKg5p9;y25v{V%kgV>w_cRewO4$% zkTr--naCR|Dgsa{ykC(3M|=DEL25}+Y2U${4{zIq96$ze1tP4) zBX5*qA3S`xF&kW{lJf?oTv?eq(Eqn5dv0(Bg4@T}n&mM;PF8u1w$pnz6cuOYY9&b^ zGnClmM(+g%1sz9&B9xq|u3+)72#lEK%7ybPII|&{RoGJ7Cb-qZRzwHPx>2N?;o2_L zbC)gm-A&WH38cde~s zC%z%PL*&2Yo~xPIg@RID{S{F^JDlR=MCHY>f4`x=e&4{rF%Q7&NJJrwpOH)bCStbn zGRXY|^_1I)+DMS#3LqB%=7Q_pK$V8%V`e6M&An6p z0_;#GDl6ysm>6R)wYP5FLg7;Y9RPN)Z~MhLRqPyNy@fgvB_WbsQ-Fd55)iw`chd}j zKE0fK1E9#doQCkr?P;S%;K9a`kxoZysi@du3}`^pD{$4IRn!m4B5hD%Pzb6Z^8Om- z!V&>DAWvj~{qh_tK;a2o*A8|XJhc(1X+y)0En_G^0OETIIy0?SVyrPXfI%u&#TVU| zkP{K`gXF8q%QZkEB8eisMS?6B8nj3L(B?CY>jFm(VpkPp$DOk0Qw6J*;DLnn1aE1a*K4@*4CL{bEWUq{mMAZQx z9sTj61ms09dkZ2Iu)J)bcEPhFSm7_Euy~Xfqt)QVkabFYS*PCxq$%?Xr(hBaX$eSP@#utFFY<_Ln_r6{FGo}Q)xAzM@op!5xZJx3Hc zB<6V6V$KMjfCjsV4<3NqcZbP}RG{aMEF0C9YT!LHqM2yFJUak0zR_GOL87|eym=G; zubUj~8`Ct3<|u{NXD+rm=>q}7iO%H1qeA3v-TDckCc=3%c2rbKrCZq@_FP|Zu-lM|D zU?R{A1o0NFCV^RD;o+dY`Olp3V1VHP#u4rYs|_>+4-BjYVaHDFV3TPEn&lz&badq3 zq5#nJKjKPupPie7^-lc!SqG{& zV83Yff#(W=wL1TB3j~dVg=Q^u{8!Z0Cf_4^l&uQa7p+LE_^C2OG;PoGflU3sk%01V z-MA6_@}-KVW*ge{`pS!&K%8}zmU007%F-9ewt^-9l$ItZFE2D1%ea64aSjf&5g$Kx z>|mq|b#1w$Lt9TzZ=@3Zj~-Z=`lyXUa-LsOuEb7B>fbcF>^!@7^U(JmyLRk+jPM(- zQOD`GFY2Z05=UPb8)SBF+7fT4bFFjOI!#Nw?c|vA_6(UyPJN)x#0(DK48zhhO2dbL zXMg*M!QYE^`x6fazUSR+YE!(P|N8BZ8Sms4Qito*y8CaWr3E)h`Z+&@A#H6jP?ZS8 zopvocK-T%#FyL>4I;?Th>is335D`1{@=jo#7`ZhY>+A1*WFvwKSSETVf#G^NUW-*{ zGqW+$289$H7)X3Zu%7#J4zo)%$`n7?<*YZmhf1X=XhNiPubl_)%{s0uPKd();X@8* z^Y}czgV5I1bw^;X3Bm>;Mph%eu-722h_a{r&OMYtI;0i^VOF>Kx(k2Kpl@m?n}nFh zU9>K2EJtoE;{dG`I85PcUZI@DR4RgZDXR-X9bvA~*(opn9BumRJzFK0f=SE4n>KHG z`SRrl;RoB;Va>~sP>KAE@?M!SD?NQ}+Dme6#*ZWh)Qy~PWN7HgKm=<|&6R2^_qp*) zO*N?FlvZoB?V4^xQxjpwO3IANwXxy5R#6BMWC40Ijg2pNTO&szYBBTO{0woGnVDI+ z@Uo4~9P&~LNMDpbjonoa1#{uu)Ws}lRP&JEm9`F|a++hK{$wk_p=1ccBH)=TH!k2r zGNQy>uVY)UJK`ty!ucqnm!bq;KzY`AuacW1`JH$M(XggVUa8$!tHoZ1?~4nlibxx) z-I2&{`1G2A@1i*Dum^%T+;_6OP}0s3r%>saHYe*vQuWay2+EAOA9{X^h2gp*M~=M5 zHj94VlUkaZ_DKEENFa2T5(kb+dOzB9-}p4|R24Q=ASd^sTX3=|KK|f5;uFya2Uww2 zxYDF@W(WKHc&iPPMdWgoQFDTZvX&OJl(DI)EXzkBsHDWm3N-w6So>yYTWtGJ_P-*kpE}uM3z8u1j{QXn*}s zaAoq-e1^^J(-#3=*q8Gx=E}5NhIRzMrX zI<~tl9ShbzbF0V$nzaSu`otIqX%IF}H1xa821J@?tz{b~-x#19|)!tMFHGl660Gm_mV60^EEPo6YcEvu`qPrY6I6nk{m zu+`PXL#a)u&80VP&a;qyX*f=Kzc znU@I$P3I__`5Yz{Iqd2yzPE4Q+(cr89qSah=9IG1EDcZZ>S_heT$|oY!Hcy8*SFInwb6wjYHmM^L^v;>S_y0MR}#X!dob$9FoiB zlGHv)bo}$Ef4Vt8TsOj&fgKZ|A2+%v1;VIw;Pv65KkdIjYi4yBPieQzv%n+}g$Md* zP{2Tb)lpMpl)9YgGYmfgKcb^Ukf!b8M_9z2ZGkh?w5>0$n6-hiv2q*J8rgyHMnNfn zL*o#mEpuGrY_1L2xpZ2B*+)zT>59pSZ}X2!;jXq*9zk_>rj1Z-agJsrnjyF~QtyDAOi?#S{Sk2d zn(R886u_%;<=>Hxy-#?k(l}C@d%t%SJ4fNmqu!Te->##$D+`8LW1xXaG-DqrS9vxQ2n8tw!_&W+P8=2y1>Jr z*4Y68kfyhizPCo}+$%o@DP9K!-jq8f`pGA`9ZVizi(Byy)PuXPpVL++8GlBJOZEdZ)wTz;P zMm6L*NQbKvRwQEA7>7dM7(5X!$YI-$W<)x6ZMTIc#Imy&c}24boPu;RZ>^i6&IPy- z7iWr#SN1g>_&O6hda%$+sCK^VXx!IZ@P_0nbtRS_C`Cmp3iB8LiF+qFhA;cTx zKx`r~l_t~e0_BZBi3S>?Val{us|_syx*#;s;)4#5#YPIiGd(-k~X^kXf}*I2@pYp`85 zF0Ks=+kRyQcVp}j*0P$8c1Cp3Nz8wj4xgY};{;jp>Cc;4sKZ3WR4bSb0VLI({*fJhuyU)_BgE)8*y_;gH1 zK-TF^G(49F2f|Yj9JjJM_OzKLuOseK)JO|z$h{V#0h0iS#DBT8X~N*q+Ol8DakiWT zdi5(^8W$j0%ojJb`}Guu4r>`4etX1EEEUc>Zvf@F7M_Yif1_W4dQ>M>MEUx4K6I}{ zQs<+QiL{+n@DjXH{E;h-(de5(OT;Staru_^jT8nU1SIMeQh_k-On+Lea3f^p1C5jB-?JrZ z=cG|#db9#f zAjjPt*H>Gx@ezRu6o-yOr`^z<_zw7!>-v~pB)Uq^y=X(}+PBzag6b5!5D!Ev$!nNBSJeL&SxtzC>Zq$QV+dyMZ93Oz?(cZm zde!I%^d7JY3Su+g@SL&_`uq;t(U@WOXxR}~e|)+%vL2d@U64fY8zrwh{Ive1^XI?7 zo1yGxp_d0HUbq}uc!e~6O}KNuDcOCY9q8Ria<#S5z|PJNYy_&!2sErA?JhT1nmfP` zP-st~#vo$IDpHSnk%+QiK${dSS6h8F5;eSYXYby3_ znaXPboWW3UyjY!MK78nh0idnjoNIr3zSA81%(7s4P;@f;y*K4|nwlB{{1Lf5MLhwo z1e9r}x7vomcP~C*w#@#Cw(K>+g#pTr1`q@-b~ep;GF!d^Oh$rb%l|P(pzt-h*8P!3O6@*Y+($RJzey{k^SEL7#XqO z>OF?pY7{wv8%7m@F-k0#D*(F+ue&rgHHk0vJRC`@tY=#?A}tzW2Yk>;n$I#G8~u?{ z*di^q_(LDrhqRIwxo(d94-uD^y}!TzYN*gekd|c*0C~H_23nZMXo&)%Uz^8+hWm#tlITeTm>9bsU4ZDX3^gdM?%uCd(XqCb)W~XYw7O}PI zudw^El_R1wckOv{hubJ4vw>m#E7q7Q&isTO1b?)3b zkaWu(LlP|uTQAb|{Pz9*vhh|9?yx;BlyN%tqklZHTyd=_gYpG#bdrAjz7Wm*6PmfU zBem{}X@v5NHv1tLE}FmQKh!KSiQO@#bQTFavh7RGpOvM(g`T=0m%}tPY)YtA5|fg6 zPMkP^5`39;2hCrl>G^&hxw*M|YrTx+cV_k5W6afQ@DxhZ!|ldL28r%e;5^teg}ovBVF0bh+lzkZbnogL=EH>yA|V3aNh2N z{E^n558_3CWv$TE6ny`6`cKcHyS`8~(H%esz=P}E`Co?y-8cT{`NK@baQ zE&lh6V!r+-OPZ2WQPWjYN=?XJcJ)J}9L;^3SWNF*U^4bkgBcR1rf=>@6B^ds^(r!;>spoo9MVmN7V@`f5(@ zXlnh%ebLK~cpHtxZ&j6|`YB7#UL164omxBTUshSE`{_+W!WoUQs>%6l3mH-Sn#%L} z3$<+A44f`nDE@5q*+-0N$73faBhH``RW4R(-W&m~Z*w!d7Hx@-wB-QS{afAMp0+wX zDnEDj#qS2}9t@m}lG+EQXv=%OrfOV_NMy<+Ir!>+`G~M*(eBOKow0l=gxXD-Z;9QBf3jXM^`iVP0}(|W6D;7xD}qunq8}^YKpqMXD4gM65bc5 z?s+nVNrYeQ|P5rn!cb+idtP7`avl$dU_9mHUI6IJ}L*dON&px-@hZ zBh^&qyQ(R-odUdPrR*+Rte@;YR4Z3t#B@{4|7}oP8>1817Um=O$%CJ2Ot#70RLzX6dWrW-*d_abHI3%mrQDGyYYls*%8$Zox3IvT9(hGgPmfVbH(~CIQ+3mTH)jY( zt#kGXvZ$_&BWW|C`yc)d1!T!+MKE^~Z+_v@PHEjdZ~fLMjxq5`q9%IzE4ib&&mCsl z+B4NuJf2_86d!z7P%wG5gy^xrFF`mN#pGnZOUS%?@77us-Lq<64yhQcyn8|sT(X{{ zsi~us#{9w2+0@bTqVf^>1C_pBzCj($EgKubqw$rM8ARB=zav60(ws9kAD-U^)CONNZv5@ zpv7Q^LFo?Z6TGK<7?0^^EVQDZ*il#UT|$Db^((P+2k94@0u3@!^6$jNjDOT4Qp|n& zt=v=QDrzrPBiBsp)*n5Q@BV|AizkT}=ho$)5S3V-%*TPXtxo)tyyfvHc$2wwR8-kg z`<$EtjKsBHIo7@`RM)Lr=MM=E-~?O+o&rf#RZZR8>_0^#g1JHjPbp?4 zyz4zgeSmMIaiv`_mT*o8m2jSl=w$b}@Mqn%$X^+ajTI*+uzP%w_O$Qj9>2l@t-PHq#Ql0RLrg^8eY5NegG@qXviQ!Y0|} zmPIV|efozg|C0MXWwOC1vMb(UilGQ7yYt`Afew1)@KBvHQWDgH@{0j`<)kT{zgnOq zc6Nb%oVH{blj!_03)QIw)bKy@>8I7nUoRPIf$r*i=v+OXK}l;j=Ho2DCf@88=?6K-+JC5pTrdwPYICQc;saOwjUv literal 0 HcmV?d00001 diff --git a/docs/en/Tutorials/images/bookstore-angular-service-proxy-author.png b/docs/en/Tutorials/images/bookstore-angular-service-proxy-author.png new file mode 100644 index 0000000000000000000000000000000000000000..ac231ef7a8c0cad46859fa03fd0dc87ae6bf1c9a GIT binary patch literal 25528 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

5~?p*Dd4H7 zAcylzq3I04IaNJLMmSS4+%r5O998`|z6taEiq`YF@KPtt%&n|wOKxUvrmI52kiZ@q zl%Djl?u%1)r<=59w{~cw@~9gk31YSh4DUgnre}^Z<2a*S&8tv!Xvc^b@{Rtbici11 zf>j7wL4Y3Ge8V_RFltOW*cU2R$QD-;9amfG1uC;vz*iZRx%(_$AtlHB>(m`7@X}Px zqLm2n>jrOcOr}b{iKo#_iiOh3TN-xubBME)33%_^RBWQDBE9;tyN;G<5Xs)j>%OvT zDd(EX=&@ZR0+qxaUpHD!kowvXBd+vxRzaesa8ymMmxq)Tjm`LT|SBC+HK;X1vpIVVyb3h zrF+>*U_%P>#T=aiJNu{+ z0?ijSoCE^HMnRpy|GAvq(Ttsnpz$LCcK9-3I0|(9?|xyMsteOA{m+6+w)Ce4##AptRT;$_DTJ(2Tuh$cOUuTa#ED8xbz0 zV9EBy;G}y^zTd*hDOGk~XrEQ=CRcg;7>5`7N4zGW*>|YSrzs(3Q^4GDn)m05&=F&_ zfNsFZ@r>`ctwR(yO61=0gW<4$KA)%kHo(W7HTo%dTNWX}q)JSK?^@?HPTcYnkGh&>SbR_EJtZaptGpoN@jgg; z$A-?3<#H4G)NmzQ)v?O-n7_B~BWF!A!<|5~)V8T6f?-32PQ-7u`a^+C!WhNj+* zCB%cqcCx!S{7nasTZ9h5=4-cz1>{-MujYFqcdinrkf|AD+``uLYm} zrI7a#%^(5}k)ixE-&V>K2GYrQb~In7(13gNJRSQBq~ggs9b9xt?MeGa(|G2~_j_LC z<`@)TIF!x4j4>?KV*TSV97TND>Q0?HxYE)7fSg_)vC8SJ{(9rZ#g|9_z{9#tTO;+Q zK2IiYS{_bt+;hG?!*4!5nDrkCc%8L1ji{$2r(RS#LTp6zB1Pp3^5KEo$P_Xoy65NJ zT)rztK>QDO8`vt#x5AhY=r9hsv0HP3Ap)geht>lMkT2a>8uUzO=f?V@ogNNWJXdld z!7EwG)Sp$_z3aI}R9*~-53oOZ{?6RK%k*hb$pQdHkkD3PqnHf|XQL=taRnP_j4AqE zvgVl0Z-NZ8biLJVW`Raua!%jpCwKG zPkmdWwtia^Gk*>KuP@*>G68FJj9$q3EbVZE!e3T)ZbHEam=7)hgrW{R)J32v{c>f> zUG|;~t$&EarAgzD*QA;gk|(weip)nVPOf#iL#}%gWzoGqKZmHt!VGhtIOR?R96>PM zG6aCKxdrMiVzgH&@}1gP{S9*Fy~hI~u0ntk72|-irFOvqZa`g@KfngG)}?q>YD|V! z*$&;m zecDCAcMW2ihexY1B>i7zWy0cR8KZTjKr6?LnE`JS#jX6 z**k;V%OXtp{iBiX8D-#cy2w?yy5g&)D4{3I{Mu7nN)E$8VaVtkhls6^))?)?V{}b! zt9`H4ePSH{x)VGD`qq}GsM6``_1K-(P`x(g+;JyFu*kE2LwCO>cwO*#FvXIvYlk0= z-J){}EQP+3$jUN8rxIgU$}%x2_tb|@+?&lHMU73q>f0jx)|1(6?HIwyRL;{v@7H%4 zm+FHOvq%6ip_nFOfe2|SDQlT1YE{pAD`=VOi4DbMfX%sy(#ZhlleMWbDR}$9Q_71*8XET@QWBh#3;4x1Z1=-}7Bl2LUBwbhpNKjks&BrJpiYOp5 zgciX04UyI}+^b%tI~8q{CgtQBXfLto;;hu)n##Ba9%dqW96k|o=(4!s&rS4?EhGdO z@g_#ZdI}g)z%Wb*LBYDh_VQ-$%ap<#8?B|{6WuFvimP%bM>U(aN(}bHx%(%X_!+k@ zM3ot}ZqJK-3G-g&&)P>Xo?N@{*N$Dj7Cx6a!5kPUp7B2uCFZHzDn1F-&<{81X7^D3r}$QQ<%cP zXDva6LxlKwewM2lTX$X)U0D{qVxy_bmOxX);f7 z>3f|$OBe7t@vORE1VnYM$arXs4J{!jn(1XUZ~yP4mxL4V)9aY;wzHpQl(bHFQdV{u z@%%2}pTyd{Y>a-lq5`7t2iGvk?>@1Xh?u+^4k=37zn?iuQI|m0=K7{NvbLP|M&uWq zabI}|5uW*on0@W%&4Zj1`8?3=Gi3k|8~KUVA0)r(!)A^((xK?RpXiYSm3=pp@*yH@ zQMKFy@iZl%8`I0h1}b=f!g???w_?8iuD9)bRoF@>tNhD46>vP+y?dcv7 zjqZv~EA)3<<^$&r>+fT#Z{lF@HS@LV4b^ooOYdxBy`}wvuAkrFXs~Ej6;$F{sQ&)ejDh5H>wSeUi%xn>EZ8qRl_5-dz%{s}*15)qSGBVgRqHjekArW9r06f+(!c_K(gc(*zd>SK4xB)5Po~ z6%|w+1#>Ctg%2P{buXyCTne);r_)GRRiY68!fy+ZhfwXwuVULRC^hK~iGU}EcKpac zC+i7z$m9sbH~8zamjc`S(e}sMXMShpM&+iy7~(`L8(k9J!ym44lw<29789b?TV7 z2Y`=VwLaK#l9{}I2-3*ByI!Mn`7pc9bQ#sQt5tfqQpAxkcG1Wuc^{m!1*MQ?PMv z%jlM&%m&piuVrXw$j=cqKY34Nw0U4jhr?5xVHrwc+lnBl$u2lOfsUXhmOX17-n;X> z5-y7|L&zdm8u2!r2+-hNoEy>6jLkUjK>SI=py)_F48D2u6`n%1nm779gx8bPNvx}K zCYGM$`l8Kc_qg6+!BT*)r`O-+pzY`(e#EAfgrvb5b#2-o0LHiS@nF2Np)nP+a)e|% zor3zGJ%45ltFR5c)b()} zrySG71AxFRl=ItsR9y3{R9$UJ1WKOI zcZ?}2$McmO`=0G@jNuC<*ogMsOwRh;NwT?RVQ!q{%$1ywJPEquDQz^s@7K0~7tC=i zFh}x2v=6!!fpRV*%@?bagYff9XX9&u%=|^?PZ6vJ8XLxmufK#i@3VSTWcXbx4Efx; zZbra8nPH*2-_77{`j7RN3+Z2fXj5ZDS&=x#g+y<4Ul11$`ioYD6xOKwMGyxXkVIy4 zO4Il*A`^G~`1!z(#yDw56P%ChkAbVDUBdwYDFvvLDh8B2??93@h_-UG@aA}MaF=68 zJ6E2ZWm&An?fndtK4lZa6CFEF@W>j!i2T-e*xuwq2 ztuS0rcHZ^%2=Pq}=i6_JU6TkXgYYl;Lbyuv5Xfs{Y@g%&iJP;BbC3{ zJ(fLZAo7rPl}>-nWir>d-z5!O1r0?s5PM zObYi>5YZ?5!Bs4YwB^R*+%?j5O*SN**BHnAgpWKO4uTRjagZ z6KCxFd4nU+yKiGwWKmq=GpTVDnBP2*J~CQyJ@p~7VK|8cWQ3m0@}~`2?{`u-rOX8gPxhv&`|q%ZrRgLj z|Ej;JlPgoKJ&&KWKn)C{uxR3Cdn$vavFVdvtZuC0Vl)*jp7`*cS}BnN8)2m#@k zA38;pS}#8prRDN=SXI)k^mm?tHC}PD?j+i?#w&x^0D;Cbac#RTlm4a=}RuP@+XzOx$ez8y@hVJMnS$Z>SM=wB5LZ|ntC9`cLnfd z=G!yd@iQ=4X0*gr5dm2RS;_AWIeI3?hYM^C^d(22jM8F8fy_94W}Eg;7ma=%O_#~s zd5Jc=rY5&M&R2(WIMstVguq(*_JKh`X+;Uc?;JoTjkJMxT0L<(froMiM^rkc8lBcXeh!eOSJHSS$ltJ-yx)oiqhs(&zY3uO4O7ZtSA zx0I@kHu4-!kQ(7fUn$L`m%g6my;!W2x3{u#YZ`^5w~LK&qxdMyW$E%yPQHkm>|_BE z-mOQo%OEeuqd=3Vs~vKHmQq|=`SqoXhN+}fD{@l%YV)cbBmbh zNG3|<1Tg0?6c;z661zNA(B%=GYIJQVJTgD=7eTbIIi*e62uRB45EA9Lt%@&Ao!|T8pao)rNA!*rvFd;%v+gx~OnfEwEgFIS0x}qrw45 z{Mz=nYIdc;TEBDQI;NM0Q@U}q_~12N9v+YWb#geFArYM>rvClu?A;vh%>;h5L&TtF2L&?)`pO++Kpkl_AFUkZVOq0czmGM%im(YPR-%rYjyWqAL{e03Eo)})8RFhuR=P|R@)DXv7{?A% zd4mvyPGo^z=qva(j+b6iL&$irPINsqpH=mD z-FAKys;4`J2Cs0f9>!v|t%7n~kZ5}y&@!jx$FrmoJF7bdjz7#`;~q?P!%xRN9z+s+ z2*Ui&Udb4@NY5Ik^g~98?ZHZnr&Xjv;UCIdzXBJnGJo4`XT~Xy1u*9C#oCw&dRp}S3@ePr%~u^5?d=a}G#;*{$k}@?C#I#K zcO{ihemu_d=+vVUyDW&dFjW85>9v`V z(B;0cyj#7k|HPO7jy?#Lh;_gkCUZt6wmYdSFV4yS8TtPK@&BeG0odOg5Ydme;+TKn zY}|Vyq~^TnxquM5+Yxt*|9uiFLHAp^(i<^#~Re`*EEDBj_E zZ=soQ^L$$vn)q%DUO6d7T;ex3I$Zu!?z=e9=yvKq&#-1zm+E^bTz)*7PFzRb=PzGu?l_X2I@}B1?VaPK?82Pg(qFr~)kf(@l*?6Mzo3FcnDVWy$U-8woRAsbU-|x!+ z2^9a@d$f0m7SCwC?B9j`DEGQq3?&#vixHQX!WTbYQrYboJTMW^+F(Y8KjlOun81 z00Mz9hpR*v)gjzIbv?laAR)s zmg@={V35ZprbbUTsDQ)B68%;`F_Na#M;cu)2bso}}N@!zO&i2P+JKiKulkB8_b z+D(Zl&R9AYcemW++}$m{i*7Gc2Sf#m8oP{7jt8ENG7*?ji^Th_&xM&MAGiR&k{7>t zCjG`nTg4#hVdQ9Pcb(e)$FlBRSm9h*cumTkl}g9;h|t;}uuQ18O-m{jhuHg1V2zGl z#-S7PXP3334J4wDs*}@&nu0f$8Bd{n8k9jx77m4hdlSPNTfJh_273RI392K|taBEV-hpe8_y@z3jih$>9r`330w z(~C8OM4~zLn=4V4mBs<^mcU<4K4$IKgw*>#i%%x6tye9VQ}t;VY~@1E*)GT$1!|PW z1z+R{41y3$6lgN|-1ofB5xS8p(NLh?inS$pXfUYT7hjv%B61#myF(#c*Xf3Ltgi-8 zwDaHoGE`E{wN)2bF;XMvLu8+K)73aZY({U&pnnjQ=jw<&?aZqsugGj{TP+&J?6x%N z&w9Ye!Liz1-D!oSCf_Ji%VEy8+rORCs&p`1&i*@>ONtm9zZl7H*H6 zzAkLAgdHvMv@tAbkvn6d8P*bFpncDpB*cV2=iT^E3VKp<@Vvc3ktt|wx*ZXLx__U- z`Ibz`&0uGCsQ!4T&4&2W`X-5X%1mas^=1f?9x~Q4pQ!V#XbDVPgdThoOt{|q20|zYL>p*o-3YH?TnfRcH(O0C~3VpS8=pG$*$Q6zmoaX zNL?dkb;84IWc1AgvL6_p@SsK%_R;rYZM3MWLQ|b|$*E3UPkVR$12*3*&)N8nbI5nA jU89$ZgR`Z-FK{Y*@8@qjT;^Yo;s7Kea^mHp2LAs8CeJf> literal 0 HcmV?d00001 From 647bee3e2f32f4b66bc3a8e07637bd8d22f07806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 23 Jul 2020 12:00:42 +0300 Subject: [PATCH 41/77] Finished the author management --- docs/en/Tutorials/Part-9.md | 184 +++++++++++++++++- .../images/bookstore-angular-authors-page.png | Bin 41853 -> 47512 bytes 2 files changed, 174 insertions(+), 10 deletions(-) diff --git a/docs/en/Tutorials/Part-9.md b/docs/en/Tutorials/Part-9.md index 2164526645..32149f1485 100644 --- a/docs/en/Tutorials/Part-9.md +++ b/docs/en/Tutorials/Part-9.md @@ -509,9 +509,9 @@ That's all! You can run the application and try to edit an author. {{else if UI == "NG"}} -## The Book List Page +## The Author List Page, Create & Delete Authors -Run the following command line to create a new module, named `BookModule` in the root folder of the angular application: +Run the following command line to create a new module, named `AuthorModule` in the root folder of the angular application: ```bash yarn ng generate module author --module app --routing --route authors @@ -543,16 +543,18 @@ import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; import { AuthorRoutingModule } from './author-routing.module'; import { AuthorComponent } from './author.component'; +import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap'; @NgModule({ declarations: [AuthorComponent], - imports: [SharedModule, AuthorRoutingModule], + imports: [SharedModule, AuthorRoutingModule, NgbDatepickerModule], }) export class AuthorModule {} ``` - Added the `SharedModule`. `SharedModule` exports some common modules needed to create user interfaces. - `SharedModule` already exports the `CommonModule`, so we've removed the `CommonModule`. +- Added `NgbDatepickerModule` that will be used later on the author create and edit forms. ### Menu Definition @@ -630,17 +632,31 @@ import { Component, OnInit } from '@angular/core'; import { ListService, PagedResultDto } from '@abp/ng.core'; import { AuthorDto } from './models'; import { AuthorService } from './services'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; +import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared'; @Component({ selector: 'app-author', templateUrl: './author.component.html', styleUrls: ['./author.component.scss'], - providers: [ListService], + providers: [ListService, { provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], }) export class AuthorComponent implements OnInit { author = { items: [], totalCount: 0 } as PagedResultDto; - constructor(public readonly list: ListService, private authorService: AuthorService) {} + isModalOpen = false; + + form: FormGroup; + + selectedAuthor = new AuthorDto(); + + constructor( + public readonly list: ListService, + private authorService: AuthorService, + private fb: FormBuilder, + private confirmation: ConfirmationService + ) {} ngOnInit(): void { const authorStreamCreator = (query) => this.authorService.getListByInput(query); @@ -649,14 +665,162 @@ export class AuthorComponent implements OnInit { this.author = response; }); } + + createAuthor() { + this.selectedAuthor = new AuthorDto(); + this.buildForm(); + this.isModalOpen = true; + } + + editAuthor(id: string) { + this.authorService.getById(id).subscribe((author) => { + this.selectedAuthor = author; + this.buildForm(); + this.isModalOpen = true; + }); + } + + buildForm() { + this.form = this.fb.group({ + name: [this.selectedAuthor.name || '', Validators.required], + birthDate: [ + this.selectedAuthor.birthDate ? new Date(this.selectedAuthor.birthDate) : null, + Validators.required, + ], + }); + } + + save() { + if (this.form.invalid) { + return; + } + + if (this.selectedAuthor.id) { + this.authorService + .updateByIdAndInput(this.form.value, this.selectedAuthor.id) + .subscribe(() => { + this.isModalOpen = false; + this.form.reset(); + this.list.get(); + }); + } else { + this.authorService.createByInput(this.form.value).subscribe(() => { + this.isModalOpen = false; + this.form.reset(); + this.list.get(); + }); + } + } + + delete(id: string) { + this.confirmation.warn('::AreYouSureToDelete', '::AreYouSure') + .subscribe((status) => { + if (status === Confirmation.Status.confirm) { + this.authorService.deleteById(id).subscribe(() => this.list.get()); + } + }); + } } ``` -- We imported and injected the generated `AuthorService`. -- We are using the [ListService](https://docs.abp.io/en/abp/latest/UI/Angular/List-Service), a utility service of the ABP Framework which provides easy pagination, sorting and searching. - Open the `/src/app/author/author.component.html` and replace the content as below: +````html +
+
+
+
+
+ {{ '::Menu:Authors' | abpLocalization }} +
+
+
+
+ +
+
+
+
+
+ + + +
+ +
+ + +
+
+
+
+ + + + {{ row.birthDate | date }} + + +
+
+
+ + + +

{{ (selectedAuthor.id ? '::Edit' : '::NewAuthor') | abpLocalization }}

+
+ + +
+
+ * + +
+ +
+ * + +
+
+
+ + + + + + +
+```` + ### Localizations This page uses some localization keys we need to declare. Open the `en.json` file under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project and add the following entries: @@ -669,8 +833,6 @@ This page uses some localization keys we need to declare. Open the `en.json` fil "NewAuthor": "New author" ```` -Notice that we've added more keys. They will be used in the next sections. - ### Run the Application Run and login to the application. **You can not see the menu item since you don't have permission yet.** Go to the `identity/roles` page, click to the *Actions* button and select the *Permissions* action for the **admin role**: @@ -681,6 +843,8 @@ As you see, the admin role has no *Author Management* permissions yet. Click to ![bookstore-authors-page](images/bookstore-angular-authors-page.png) +That's all! This is a fully working CRUD page, you can create, edit and delete authors. + > **Tip**: If you run the `.DbMigrator` console application after defining a new permission, it automatically grants these new permissions to the admin role and you don't need to manually grant the permissions yourself. {{end}} diff --git a/docs/en/Tutorials/images/bookstore-angular-authors-page.png b/docs/en/Tutorials/images/bookstore-angular-authors-page.png index 806ac3a49500cb10d5791fbf16b709a573d1be9a..bbd865e44b773829e4d3d8d91a5ebc5119d42b11 100644 GIT binary patch literal 47512 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
frux+g)X4|+&_ak@RG}7ptP9n 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 X|9OMH;ZsH8GaRNEXlEQa{p-I0_S=Aq literal 41853 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 From ca08dd77e5bfb5eace07d50c963114e8d577afc8 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Thu, 23 Jul 2020 12:32:34 +0300 Subject: [PATCH 42/77] docs: remove booksType from tutorial part 2 --- docs/en/Tutorials/Part-2.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 690394264c..8eae95648c 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -474,7 +474,7 @@ Open the `/src/app/book/book.component.ts` file and replace the content as below ```js import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { BookDto, BookType } from './models'; +import { BookDto } from './models'; import { BookService } from './services'; @Component({ @@ -486,8 +486,6 @@ import { BookService } from './services'; export class BookComponent implements OnInit { book = { items: [], totalCount: 0 } as PagedResultDto; - booksType = BookType; - constructor(public readonly list: ListService, private bookService: BookService) {} ngOnInit() { From df34cea1d63aac118d0349c8b60f0032bc7a0a58 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Thu, 23 Jul 2020 12:37:27 +0300 Subject: [PATCH 43/77] docs: place bookType in tutorial part 3 --- docs/en/Tutorials/Part-3.md | 38 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index 3b31ec1c88..b8593a5ac5 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -643,7 +643,7 @@ Open `/src/app/book/book.component.ts` and replace the content as below: ```js import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { BookDto, BookType } from './models'; +import { BookDto } from './models'; import { BookService } from './services'; @Component({ @@ -655,8 +655,6 @@ import { BookService } from './services'; export class BookComponent implements OnInit { book = { items: [], totalCount: 0 } as PagedResultDto; - booksType = BookType; - isModalOpen = false; // add this line constructor(public readonly list: ListService, private bookService: BookService) {} @@ -738,7 +736,7 @@ Open `/src/app/book/book.component.ts` and replace the content as below: ```js import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { BookDto, BookType } from './models'; +import { BookDto, BookType } from './models'; // add BookType import { BookService } from './services'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; // add this @@ -751,13 +749,13 @@ import { FormGroup, FormBuilder, Validators } from '@angular/forms'; // add this export class BookComponent implements OnInit { book = { items: [], totalCount: 0 } as PagedResultDto; - booksType = BookType; - form: FormGroup; // add this line - // add bookTypes as a list of enum members - bookTypes = Object.keys(BookType).filter( - (bookType) => typeof this.booksType[bookType] === 'number' + bookType = BookType; // add this line + + // add bookTypes as a list of BookType enum members + bookTypes = Object.keys(this.bookType).filter( + (key) => typeof this.bookType[key] === 'number' ); isModalOpen = false; @@ -808,7 +806,8 @@ export class BookComponent implements OnInit { * Imported `FormGroup`, `FormBuilder` and `Validators` from `@angular/forms`. * Added `form: FormGroup` property. -* Add `bookTypes` as a list of `BookType` enum members. +* Added `bookType` property so that you can reach `BookType` enum members from template. +* Added `bookTypes` property as a list of `BookType` enum members. That will be used in form options. * Injected `FormBuilder` into the constructor. [FormBuilder](https://angular.io/api/forms/FormBuilder) provides convenient methods for generating form controls. It reduces the amount of boilerplate needed to build complex forms. * Added `buildForm` method to the end of the file and executed the `buildForm()` in the `createBook` method. * Added `save` method. @@ -832,7 +831,7 @@ Open `/src/app/book/book.component.html` and replace ` Type *
@@ -917,13 +916,12 @@ import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap export class BookComponent implements OnInit { book = { items: [], totalCount: 0 } as PagedResultDto; - booksType = BookType; - form: FormGroup; - // <== added bookTypes array ==> - bookTypes = Object.keys(BookType).filter( - (bookType) => typeof this.booksType[bookType] === 'number' + bookType = BookType; + + bookTypes = Object.keys(this.bookType).filter( + (key) => typeof this.bookType[key] === 'number' ); isModalOpen = false; @@ -998,14 +996,14 @@ import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap export class BookComponent implements OnInit { book = { items: [], totalCount: 0 } as PagedResultDto; - booksType = BookType; + selectedBook = new BookDto(); // declare selectedBook form: FormGroup; - selectedBook = new BookDto(); // declare selectedBook + bookType = BookType; - bookTypes = Object.keys(BookType).filter( - (bookType) => typeof this.booksType[bookType] === 'number' + bookTypes = Object.keys(this.bookType).filter( + (key) => typeof this.bookType[key] === 'number' ); isModalOpen = false; From 383474bff72fb8cc8f199c2b37f126e9a004a40b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 23 Jul 2020 13:31:15 +0300 Subject: [PATCH 44/77] Update Part-10.md --- docs/en/Tutorials/Part-10.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md index ef8129d23b..a074de5b5a 100644 --- a/docs/en/Tutorials/Part-10.md +++ b/docs/en/Tutorials/Part-10.md @@ -74,7 +74,7 @@ This is a typical migration problem and the decision depends on your case; * You can do it programmatically on data migration or seed phase. * You can manually handle it on the database. -We prefer to drop the database (run the `Drop-Database` in the *Package Manager Console*) since this is just an example project and data loss is not important. Since this topic is not related to the ABP Framework, we don't go deeper for all scenarios. +We prefer to delete the database {{if DB=="EF"}}(run the `Drop-Database` in the *Package Manager Console*){{end}} since this is just an example project and data loss is not important. Since this topic is not related to the ABP Framework, we don't go deeper for all the scenarios. {{if DB=="EF"}} From 4ea3896ff91f331768033f2daccf01c287963aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 23 Jul 2020 13:37:18 +0300 Subject: [PATCH 45/77] Update Part-2.md --- docs/en/Tutorials/Part-2.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 8db6986ae6..7ca9ae5f44 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -462,12 +462,9 @@ For more information, see the [RoutesService document](https://docs.abp.io/en/ab Run the following command in the `angular` folder: ```bash -abp generate-proxy --apiUrl https://localhost:XXXXX +abp generate-proxy ``` -* XXXXX should be replaced with the backend port of your application. -* If you don't specify the `--apiUrl` parameter, it will try to get the URL from the `src/environments/environment.ts` file. - The generated files looks like below: ![Generated files](./images/generated-proxies-2.png) From 4d0fda4f821415b026e4eb2f82e0c25264dd182d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 23 Jul 2020 14:15:27 +0300 Subject: [PATCH 46/77] Update Part-10.md --- docs/en/Tutorials/Part-10.md | 131 ++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md index a074de5b5a..f499a2ae8a 100644 --- a/docs/en/Tutorials/Part-10.md +++ b/docs/en/Tutorials/Part-10.md @@ -326,6 +326,8 @@ This new method will be used from the UI to get a list of authors and fill a dro Open the `BookAppService` interface in the `Books` folder of the `Acme.BookStore.Application` project and replace the file content with the following code: +{{if DB=="EF"}} + ```csharp using System; using System.Collections.Generic; @@ -443,6 +445,132 @@ Let's see the changes we've done: * Overrode the `GetListAsync` method of the base `CrudAppService`, which returns a list of books. The logic is similar to the previous method, so you can easily understand the code. * Created a new method: `GetAuthorLookupAsync`. This simple gets all the authors. The UI uses this method to fill a dropdown list and select and author while creating/editing books. +{{else if DB=="Mongo"}} + +```csharp +using System; +using System.Collections.Generic; +using System.Linq.Dynamic.Core; +using System.Linq; +using System.Threading.Tasks; +using Acme.BookStore.Authors; +using Acme.BookStore.Permissions; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore.Books +{ + [Authorize(BookStorePermissions.Books.Default)] + public class BookAppService : + CrudAppService< + Book, //The Book entity + BookDto, //Used to show books + Guid, //Primary key of the book entity + PagedAndSortedResultRequestDto, //Used for paging/sorting + CreateUpdateBookDto>, //Used to create/update a book + IBookAppService //implement the IBookAppService + { + private readonly IAuthorRepository _authorRepository; + + public BookAppService( + IRepository repository, + IAuthorRepository authorRepository) + : base(repository) + { + _authorRepository = authorRepository; + GetPolicyName = BookStorePermissions.Books.Default; + GetListPolicyName = BookStorePermissions.Books.Default; + CreatePolicyName = BookStorePermissions.Books.Create; + UpdatePolicyName = BookStorePermissions.Books.Edit; + DeletePolicyName = BookStorePermissions.Books.Create; + } + + public override async Task GetAsync(Guid id) + { + var book = await Repository.GetAsync(id); + var bookDto = ObjectMapper.Map(book); + + var author = await _authorRepository.GetAsync(book.AuthorId); + bookDto.AuthorName = author.Name; + + return bookDto; + } + + public override async Task> + GetListAsync(PagedAndSortedResultRequestDto input) + { + //Set a default sorting, if not provided + if (input.Sorting.IsNullOrWhiteSpace()) + { + input.Sorting = nameof(Book.Name); + } + + //Get the books + var books = await AsyncExecuter.ToListAsync( + Repository + .OrderBy(input.Sorting) + .Skip(input.SkipCount) + .Take(input.MaxResultCount) + ); + + //Convert to DTOs + var bookDtos = ObjectMapper.Map, List>(books); + + //Get a lookup dictionary for the related authors + var authorDictionary = await GetAuthorDictionaryAsync(books); + + //Set AuthorName for the DTOs + bookDtos.ForEach(bookDto => bookDto.AuthorName = + authorDictionary[bookDto.AuthorId].Name); + + //Get the total count with another query (required for the paging) + var totalCount = await Repository.GetCountAsync(); + + return new PagedResultDto( + totalCount, + bookDtos + ); + } + + public async Task> GetAuthorLookupAsync() + { + var authors = await _authorRepository.GetListAsync(); + + return new ListResultDto( + ObjectMapper.Map, List>(authors) + ); + } + + private async Task> + GetAuthorDictionaryAsync(List books) + { + var authorIds = books + .Select(b => b.AuthorId) + .Distinct() + .ToArray(); + + var authors = await AsyncExecuter.ToListAsync( + _authorRepository.Where(a => authorIds.Contains(a.Id)) + ); + + return authors.ToDictionary(x => x.Id, x => x); + } + } +} +``` + +Let's see the changes we've done: + +* Added `[Authorize(BookStorePermissions.Books.Default)]` to authorize the methods we've newly added/overrode (remember, authorize attribute is valid for all the methods of the class when it is declared for a class). +* Injected `IAuthorRepository` to query from the authors. +* Overrode the `GetAsync` method of the base `CrudAppService`, which returns a single `BookDto` object with the given `id`. +* Overrode the `GetListAsync` method of the base `CrudAppService`, which returns a list of books. This code separately queries the authors from database and sets the name of the authors in the application code. Instead, you could create a custom repository method and perform a join query or take the power of the MongoDB API to get the books and their authors in a single query, which would be more performant. +* Created a new method: `GetAuthorLookupAsync`. This simple gets all the authors. The UI uses this method to fill a dropdown list and select and author while creating/editing books. + +{{end}} + ### Object to Object Mapping Configuration Introduced the `AuthorLookupDto` class and used object mapping inside the `GetAuthorLookupAsync` method. So, we need to add a new mapping definition inside the `BookStoreApplicationAutoMapperProfile.cs` file of the `Acme.BookStore.Application` project: @@ -466,7 +594,8 @@ using Volo.Abp.Validation; using Xunit; namespace Acme.BookStore.Books -{ +{ {{if DB=="Mongo"}} + [Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}} public class BookAppService_Tests : BookStoreApplicationTestBase { private readonly IBookAppService _bookAppService; From 4e2e81a3853b06166bec291caf664086545dd7e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 23 Jul 2020 14:48:35 +0300 Subject: [PATCH 47/77] Update part-10 for the angular UI --- docs/en/Tutorials/Part-10.md | 25 +++++++++++++++++- ...tore-added-author-to-book-list-angular.png | Bin 0 -> 52134 bytes 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 docs/en/Tutorials/images/bookstore-added-author-to-book-list-angular.png diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md index f499a2ae8a..e522f676a9 100644 --- a/docs/en/Tutorials/Part-10.md +++ b/docs/en/Tutorials/Part-10.md @@ -909,6 +909,29 @@ You can run the application and try to create a new book or update an existing b {{else if UI=="NG"}} -*TODO: Preparing for the Angular UI...* +### Service Proxy Generation + +Since the book service has changed (added a new action, changed DTOs), we need to refresh the service proxies. Run the following command in the root folder of the angular application: + +````bash +abp generate-proxy +```` + +### Show Author Name on the Books Table + +Open the `/src/book/book.component.html` and add the following column to the `ngx-datatable`, just after the `name` column. + +````html + +```` + +This should add an *Author* column to the books page: + +![bookstore-added-author-to-book-list-angular](images/bookstore-added-author-to-book-list-angular.png) + +TODO... {{end}} \ No newline at end of file diff --git a/docs/en/Tutorials/images/bookstore-added-author-to-book-list-angular.png b/docs/en/Tutorials/images/bookstore-added-author-to-book-list-angular.png new file mode 100644 index 0000000000000000000000000000000000000000..9fba94d5bf2b6600483fa4fda61dda70daa6550a GIT binary patch literal 52134 zcmdR$Wl&>H7oZtocyYJE26uwH41@dN4uiY94-69A-3E7e9~^?aJHa7Ha9h52zWujV z`)8|mw=1cfdvkMg`lh?j>F)C+^t-$SGQwvB7#J92DM?W!7?_XSFfi}-|9Ss*CJo&L z_U#4UUQ*Ku1_lxHKhJkCX&HDhFkfJ#M1@t{Gfvjbya~ZAoabJqjh1c&YpReAx8cTB z8*8JmcbFgG1LI=!cy=jbGBVIMaBl87#Qj=h5xY;2IlKW zVcGwf2?O)vaUtP!485^U+WqKlti3%ZkMTG~FI;7WQR3FE5qh_F9ICMoO_|!}drZrajg5;X zz0w$nOcHwn67y6kbU&pt5f%B?Sz4a6EuNMEQ3M z7e(U*M@~@}6efCaBW|cLzd{yX&EfSz0gCA9KCk4_)=Y9JZ51NVy7G2k$yECeoZ%l5 z*jqAyK$U*_%Fg|Pqj{m{jg3d~s;}tpHZKj0K?k5#)qZ>j$51?!V2u)nI^z=QG6GJA zeeW7xrV?8zmml?QEnddH+8U|+e!Uz)H(MRZ5*f)=2awDN870sJR~{2h&2K|7#eo`$ zT(onPYvNP0R$$h;^N;e$ zBW8MBZ2D(5ukr1oM-&JI4;u0@&1w*f^5fdeW{-Xd6_1;$3;KShyW2*r6Q>ObP%*0P z<@ITh3EHMw00!u12~7(3O~mC~!r?5`5KM8{vN;I% zsdR_r*$;c_wpFdRBXfMm+PqXucK0~B8V<+QcE!?7P>A2PjDhyK-jjpAxuAE+2lxVjtq2Mad=VW!g41AJoy>dB(47MUi zrBr#6<7~wYrQxA!&|~394lNQ>WLFMc`0@Sp6BM4#T zDdWV);MVKYR6=tL2i`)YeVv(o7q|K*(ArF8osYVXv$dmVuCt@{qOW4IaG1G|yjHH4 zq4nq?DH)m7?N%fFyp^yxtb12Hjk9NF51WH?AMY(3BZ7SL?KJV)fEOQ6S&w-{{!z~m znmu21V^hg{wF#cIafU`j;%~GEET?|^EjHS8GuN7yx@2-G0EwD$Iv-6nL7DZvjaL)1 z}GM2F8oLvTnUJ~q#dqZum4U$yg^ipc;R(l-We zJlI)+j`?0^JCE9WzAnv;B7FWDpP$(>+o3=4-Q5zD2gS zk|j4qo|f39Iev7H<(4PgO0fq5X$0GTS2C2$8EfpOl*Wu)d#(k9e-y4>BJ%%3`8guY zXtCI+=!bZ&ydwQTd3Sa37(=V2W`Q1MgMApRK6PSA?TnGne9dn&roD?LV#d$c##v7`-%YY`bP0845p1zhijK*E5t(nGWPfojP zM0$2eLmUaVXn;@udEeG(bN0Oj^q!zfUp=YNOcSGr^vZr;8*)b6lyy21POI^{WHXWw z&~(w@X9L;ONTQ4za^4E^9Zth0uw7~9FRRy)A#A#=R4}h{3iR1qc)gY#oIF989t}&> zo>%WL-9-|47j5+0XG6wIp0~^QDO`VK>SI&axmCRHj*a@lkjRbS^D#J21c!{Q#rL{U zu1uN@j1i0q`+$1tVddAx=IG}RL&B_Msc51r7Aq(%ZD_t}FA}Y)*5IvKsz)q8Lt<&O zVs%@gtH$kh0WhF+%th_KYno^D}r^7&(=y_d=y_}LVgM0t{@+*?@EsXh7Yb`JDDyBIcAOja7j>68)m(~m33Y+Dy^+Q8^>bgySvG`$k}n}RXnzBzI_3##pEV66QL-?s2ArM zsc@~gUOyE1UG|NPImrcUq}E=xdJ*6EFb;p7w`lMX3HXLhl-IhoL(+AB0eRZY93Ku! z^JjwxFFbcaylbdpmo8ST(mbg|!z@WoHd;MZ*Qh&P(+>x$>Z1bXkL=0ir0Gj5#=@dv zF?##3u^8Ylg%>1y+eAfWogd6d+z-a*T3D%mG_`yqE1NmoZCdr*S;zIg+r8@Ggq%<% zRc*90bOYeq$|_sfR2ji*@ixdS(I}d;76lvLcWnfSM!#%}Mo833jT&t`UdXQjLU+AF z5i2abR1V}>2K3FjtP4KS^h8g?HIH(6ZiPI83~+vHTInSzmtoJTDa z9cj^}WgM|NkL9%jH~viaw2GKAn|rpHJ&u!=T;#W#f&9HJji0jCyMlDkKv?YT$O83!~x=$Hu2 zRB&zSE1o)}WVwrA0$Zl}+q1)Im4t$Wl)4Frz4_tvDl|~z>O7QJK@DG5Nl`U6zA#3n@B5GP+hS;6hmDl& z@E-QuW!899Aha}JgX2O_*>2F^R>bl!uJnmLC=mUrGEeTBsCP!#-wnFx~;7ZAtG~VZ7y>i;J;VS#~{<+N+lAe;$4ulZHFntxCD^kObf><@>n= zW%GI$UmPs1M)I3jTbh`NDOwVauo`sv6t{78<}+FIJ}jTjD>o*5uXB(oXuMeRbG>*^ zuWW*o#K1T_Cn^OT5}12D>%^p1QdJsP?eEu=vQ$HAE2R4TIel=PH%^Bc&zffz6w-?g zF1RYggcZiaBNVi+CdF$Va_W%HD=RG-laW&y!pCqgGPwIXLi&qQR1}F^YM8!+mOg9& zB&rD2ka!%}%e<@$v(;BVJUd`NW7<6)IE)7)c6 z!Ox?mbAklNMEW^6dez~0APlhX*R(a2?IxRXN&Z7+3ifv6b~ucp@-$LN=y|)%RgvQI z#OFF_jUI7+56cIf14)FqKL@F=pJms>?lbviw5{KPr+#TR`JIh$F~&n)PGY)aMvzhoh2Ncb#OpxQI=SX4F;pX!pj3dZZIPR5>myqk0)5JC973YD#xkFRvXv zl${0xef;Ttlj1?xVfTK`j`!`|PWVhLP2SI!N>etLl|sg-tTiPbCLShbdAaM$zLS(_ zz03^cQZzKxvQWs%OpXja!|<49gE&C7dZ05%wGH%4DlE2q#Dv4~&NT5Z`M_kL~m z`$Fdwt*)cIroqysz5A_yUgfRlK}-RYW+`%}XT_K!0+`2A89!8|@;gm^O-T@Te}-9W zgVkADx+*1;b|)FN8t43spmDXs(OqtC&k?wcroGOnPaZ?n!DTHj_v2&VWM|<8T9NDo zV6;lF@H;DCdhnjaS2Y}EHD&D#3Vh27JE9C8lYg>*4tg{<*@?Jp#c`+~K6te~NMsUs zeZh!5-8{?055iVWUG^`W7cLsyfnxUV0W=lUF(I7D5Sz5~@Y;;rxRY(IDAYMOB#&M) z7NikLB~aMvyuX`PgkD*Aq_jq@w}&3#!on1@_u{1cAuC;;0;saAEGk^<4vQhM-G@kbJ_w;?vY+x;b$+Vzx6lb8 z3r}fyofD>3NRgS4t9U70ak+LJ2_M_K+wYAbaq0vd+@o0pgwZwFA9&(qs@QwDD5%cF z7Kqp?YQ{KoI*Boii3lMCw~u%>7A6i-s^CDAuBkKXV1vCm-&&-iVGzB&HgEb-2bQji zmXen8an{lt&ra4FD_#f+d?}Nd{tIS&MxCcgr2O`&rGUBf-7Gv$0qkMf0_e6=Tj=Z(Q-q41S72}h<;HF{fbP1Vb0Y2wIrYs>tmUR6`gJsK<3++fT7K zm$0{x#Aof*=F@jj8$Lg)8Z$H5+_b?6s=W-Olg{1GRKZJ%H4hIHoB6u4-V5q`0zE;o zt!6Wnc-f{M%&9VTHPZCIE3pc=R&U6Uu5LM>xl1FB&>+$ymKUxQosQ?P=GJBeEIaPE zAq$uhlcxe{un-zt2$P15gJACmg#vSOwhEI(aAOq@`Hf0heV0)EZa^0y?^brZUayso zYgX`TQx@wSRwWmIPJ`alrY9U{dsHE2g zBO%|DIg!Dxn$B27&Z8Q>q^cIDxi%sI`@}{SRpo)x>-AV!cthsOEA#-6%{8j(8aL>? z>(}{Q@zN&f^vEGo^|-iS*}0YErgwi8UUhQ9&K=rwlj`t@Waf5BfiNB#+WFE#icm+V zw(7GJS6%ZQ8uDVcFto}{>f_UqY4qkaJ>7@rxNN*nPn&}GK>dygsZJA3Bo8JF5y|W8 z@}k%To%J@VXPa63#LvUoMXPn{fj$Z^-!dOMssL{R>&+U~#Hd#l_Pd`!l=;}$W`i>L z4yL(P;2{l53mXUVlS9nf>J0w6c1~X3&97I&>UtiUwuaVPdXZM%xm)!HKk%q(>h~I_ z)7VZGaVh{+G!!cb<7<(o3Nb*c0dd`zvU!zVEU+^NDz_;YnU;hr3HLUxXqvsa1s4wG z(I)~|q65}*(RdtAmzB$e6VcB#gMB8dSQM$AH>ZN^C%EQ4%$SDY5vSUnPa(R6%58~y zU&Qe%RQ39fHN+0*(q%${lK;SodwwFSv$4aMGPN~O%c$=%x%FSC6{aBTZMHC-X*doK z3m>2&pb^Yt09bfdyQmvQ11K=2h9->|sQnQ&gu@6pMJ8E8kNl5g10Y)`UrfK;;bhlPJ_?~adG_Y{EpQ*!^dqZ|U@FNBb>nb}2o_7bfy?78mz$-#g)FWcU82>X1a0r7jd~Y=n*U@H1sf5L z!QDi!{1Rn9OCEDsNJDl`6C5Lg&b86CBU3vExwwpU!sYWAy>55E*zwm&omE< zmAs^s8PNazBiW;q;;!e9I!E?F;np2Igw^#B51RgS2 z5`hs^SL175T)GWVv_i1ZlQm&qUV(IhTbC2gbp3pu4wIwIW;Nw| zC)v8Z%Y7xlr*0da?G*dVD_t6G-lrtK*SlS)_}|R%oP{b)ZQ|1S9^`!n`=mrPc|!>F zjvBpN_-PCBf%v?vZkQpcr|Y+B+2B4swo0wa_~4^K@p^dtDfq!GRjJIE@3u-cviKr_ z_ge3maA)kz+;YD`vJdba_W-(C$!KGlHQ}F&WJdM%i!7~eOKx$n2jMF^l3?dC`cq%> zB6WLHJ>Y*Kk#u*7I(F`n-1$#ivcY~R4hTrZT^=%^p#y${^yXe~kz zo{yuWr=BlZ75-vnW66rLgo;Q0TuWlWEQ)0MgZ%M9bsZHom6n9JcW?lrYZTI*yo&V0 zAY8;fJ*x|?)=_4p({#XPS|9hdyP1#JT90LI7$X+t7a3DVx~6?3)0B2hpjccH+UB`=dEe-e&i7kA za}kJklU}~Jltc8?_`4T-8WbVEe=;neQgiPvo(tR>a({S%T?p*nPJ47ZmtXSX&r|8g z%;X=98{uKwk&~s@QS7}%k=G#1`ja{is#{@?Sbo~VL|t>$brcs?a@2`XqEZ)?8J@^| zq>2cfD;yMW8U59pgo#aTdsV%pjE@>D!YcKEK&QCs<38`LJE3=vC zd^jo%>-6`Zs=2uG#)iq@ni805>~Jx3&DU4l6`(%*8N}#gRB1E_pS(njg_)>$LE%su zk1=FNLXa)!JL;Scx7zL0uOe*!xiG*&6T5+aiG1x-_{u4A2Dj{mVDIFtwF-Jqz}x|5 zcmW3Xl!vSeE<(^UZAC`wJ`nHLuv4kh%Apcx#6j|K5vMK7hGk=3`||mPV7IHfFZ2u9D9y7s&CB85B| zHJKXamZVmVg-rj`+r}@=4T8Kc`CSG|c)v8xao^;hh~v@*4t$TL)N{?;1 zEkxS2UDiXa+Ipu}9W5b+w?C&Sj4QP|*cB-S^xl&a;vEUjE^^x6xDGj0EtnKonXqyi zJV$bk`3aIU)>-|rMg&?%Bw&7HT6rSz*5LQ(#EEs+<9Ef`6H`RH|M8vwB@bIPRwLB3 zJ6aOUoAjcu5mu2RI5w*l2V!n#f$>xjX$D0oA{h%t(w1ZoD?@Kg^W-%d06`SV?Q&`S7_qNd6@Cf-^VntrSk{Q}-1m0s zfd~~@-upg%qt)jv#UNV$di+*T%N@*y6EE8Jg_XicVN{GJY-@7ZOf^Yz^TQaQKEJ1@ z9aeV#Uh}tWQW%oYhVK4nL}Vnjcyh%4K;uwa_%P+y(R)O~C+~H74|Q%?X%8fc@tu$a z^H3%FMDAZjkw1Kyauv{qHXcA$9i8{o=q%n-;5M`X`?J^|9ctNMSXn)g-~~zlIDu$9 zuw(|!DvWqMRIuL8h~|y}n0YnpBlaR4nUo70%Fz(UiQxOIi;Nayk~6iN3{(9kgs+Cp zreT8w=EtXUPUl_mp5Yzj=!!#p(+iwDjw;y+s(OmG#$dPoy7)VR4lVYG-qaOTP%()FH{oC zCWo1JRys26V3&!`QPU(Lqx@6Iium}~p%KK;Qt#l{alU9YbV};t1csfOiR>C`{)^?2 zYmfA^j@G*}RNuMl^9ceuMAR{%5xXM~55K{R5!QI6aHUY36V{%U_x!O;bZQPK14o2O z3E(LVLH2_*9+{0iVY{E`OXL-NzE^af`7ojg>vnbcR)S8@XbOc8d zsAH0n;wObnoF`z@14sEA*!h{+JaXhT-hZmAqESr8!Q{1W5A04reef|Wrl%q^q@)YV zFnC{CM=bO~;n%9vzi2{vcmACC9#ku2dfl<={@`nDZ!8^ITtBu)WP*9#<=o9tQ3|O6Clao&M0?wmkG1dHR^n`u){@ks)hnMTkqnQkG4teGMzm(G}jJ6RqR ziMOIc!(__J%B1Fp_8YE|kzJY^3GvejD0X%7OAqSvHM)G9L8cd3#n-pU5d>CYbgZmK z(PJRD);k_4+U7;j4u?6{n@bFNfsy)ZPVq*-d zUDnnQ-Dv0*(~j&22-$=5y#YR>+qBVg6QK}Drt5_7+7vC%1>F=k^B$?zY)KN#4~Iba zs_x;W{z8*}{0Fm@XU9;@R65#B1tDZbtN{O?7WE6HimRQC+^SSU`zI6O5j*v)v_`Jl zI{shKhvy#HE4&-;Lq~Ay@4nH}(moW|EN!t#mQIX?Z5_1B(?D=>v6T~RNUADh>ms@* zzlo~do~`+itf)0xugn)nWZMa@w}&ehZanpK@G?QQMznX{E6TX-S2NamgZ*@rZ+erp zqO;k-t+Q>MHfmwxZh8XV(|S)y`+iTGlYKrycTiQKCzI9qjn}PoqWJgdhclIWs;l>B zD8JlZfltSA;H9JoY#!GOhs()c6@&(B_Am+SHu3K*`h>{HzQsXe71MvOO1}~ zMyzC^B9m+@Kg#j8&Zkx)1&qcfB5y-!Z0Qvp$PpN0Stu`GOG+`yszu0Ppb`}BDXZTMW$t5_=IJZ^%hPPrZDqoBt&O|$(S`U$r=1ik|_z;0RQt` zHL4?4&Z?56`;4<6KNCxpYy_jZgHT1#3Ug_~l3yBr&ytE6(SHbMd z%`~;1A4JU*tTOWL=Qs*j3_@mzObZ^G`zA+?lgYRDzTVtXM@Gf9Pe)E&P*oKJXT+Vn z&2LihTu1A5in6AC$J0rt8LQHA*7aoY#^>Q#aVb z-;dd9S=p|xK#==s9};bCtgN$_-ssn5j-Bo2&XUy4uta)G^@@=gxNjL`oPwln&6kw` z@592d{NVvVpW8!SQZj@U&(qZ~Yz__=dKnCEHw_Ge?37G(hl%~JRu>O({bxj>*W(ow zA#|y$k%bD)G-C_y^X09xtzJ07huYSoOP4Y_*$$g$aq4~(zlTn^F4u*IR4sY*%$TgK ztoZb%`4G|#F5Y{m@T;_V7#?$Ev^IZf#x=c_T4m|( zdlZWVCQ{l4&$+qR`$R9cywxhf#~oa)z1m}!rnSUU1djHr?I{lky|ShJ_&y0fo{}O4 zZ4Z{^#6ioGPm2Hrbs}mM>tLIK$>M-)C$&&9f!@XWzM0zE^bBwo~3uWz`)$@w23SNkGYI-4}T%noH_RTsx+ zl>+L4K<~y6ZEm+wnt)=$VOpu&h3So@eO@p%`K2$wq5Ov%?}s+j1pYWUw&n<%9jY;Ygrc5ZLw zG~xc8f^3S1?G!0C&;*}_-zdxVGB?bK&@_IEt|v$A-QM99<9}P`~rmL^1M%o=oh3D2a}r-(Z;Os5?IblpHP|zPd;Z7 zZAE}7983CGj5xKUo3@gR*(00=xOrR(ed}PVHMmQ(2HZQSGmGzjt}%nLxHZ{()2Sn8 zURp3)1LI{VpXxoPkD!R8T?WR}_-L9@CZSF8NQAtWAcmnU?N4w66{6)jw1|(^pYj;- zsN^dXF7$R%rYOV603b?+5eJGun}OJ0auqXrSwQ138kSAY2!geq8jnt6gkZPNkyH}} zpQRz~@T42N%`APA+)3C^mN?*O-F=z55Cu@tFb}&$PW@kd^`d8N} z!Oi$OcvGdU(R-Uddo=@};B7wbLn>VO9i-7&v=v#D{cx2t+(N~dAzQN>%y-uHxEHZl z{Cg1dpKzt1Z{pokH>Vqq{dFBXb!B3@uNM&C$8c32A)jqTKo=|x%mNW*?b9d4V zxHZ%70^3nXW_DA7E4}Vw0Z9eN+=82zuwvUP(wdVt57#@W9Xdh|7F~ES8ZmUdR;LeX z=1Z%+znvU!`6BD&w2nEgho?!>Jw>1-OI>9n-X&D%p$r+ax-Et_s`7+xdU@|cB~Uv( zs=Xdo>3hIA^g6CPVcr|F5qJfW+wC@Ea+xA~s4Be-Fh7BR`ivLW>nqJqCJDTv82EGP z+M_eb3&jt&D?1#l!YuWobrhYo2T)H0Chc6arvJ!iDm9yk=$31Zv&wQG5I`)}))$HR zPdghpE4+KtKomAuGJPT5c>*CfoGvX2vmTP+rJR-eS-f?A6GG7_qwCiymbH!Ro-P>y z8JHojm!9!otuJa_67MZM69Wt+(Rxs>G-Q;GsM07CEZ%(geF<;Wo5qZ! z;odR0WeM`K5e^vdb6RIr6H0tq2g`=-6C((`@J7xDK2PLg)>cDq3l#%Z7XvLqSi+E% z<&a2MV-=X6GGFsXLT9)cOv~uMW{~j+6t%W82wSdQKst7^#2^W%$;)qPD}Y!S!3LGi`2W3r?>F85 zKP1He=5|t;=`(}{Y>@o(@yR=d8UwfJKLEsAIrr~#z)013OYhLQiAXGr5JKahVHHNp zK#^d^`%Z&})T@*A?Jq3yCnSg_%>V1p9qtNwzruXa#*`!1mM33*XXknqx(@50kN@iX zi5hFqed5eppj1M|Yub%4#imw=5U_dsm?%xj`d^wvhYllDv_J{={kyDvT%D~57FDc% zdv1#OVJ7XRf0e*&_DWL4U+@DEdIDr7IH#*<3`Z&czS9itV}p~ImHhDc@c#)|Me+^w zU?N{PS?=`8ULqc3hl z=tl$&P(Q<8l*oG%*5hHsVEDDsKF@Uky?MIeO^;JTE_&72PFDvfA+C$ZtxJFKcSGY+ z_s9TX!sXqf`I6L#yZ%PbKnF7N2P&>%!!WvJ+vRF9MsZ&dj;J8}yx+l^+vkwcnE;j8 zBz=$PYXUYGMYH35y0Fu?)P!i=U!-Bncf%7lOcCT{isx>eXG_|C=Cj+0w9fa5stdYa zs_pL^+Wx&C5F=9m&Wk9h8zZ4^Qmnn8^Lu{emDHki$~KAS1qc$ARo_Tc6+NbMP|WN3 zc-OP3f)M#a#q*+oNzG~v<<>>ingajL%v$Ead5zoi(TLyky+Z9-%WIdQpR!Ft0wozn z|KUq;k>5id?(t&~rnb+0iT?9Z^DR_ z?cDEcg@|Y052Ioz`ML~-u_q1LLA)-vB-RhIV#SJd{T_*|WR!ZMYEECD_TOSV!0Q=q z+FgE?ZAnbljZ-~x(=&8`B1MeJX{ixT$VVh1fO6{oHv1KN$`mhXHe3Yh$fHrDyv#Yw zndjzlI|~2XrhH5m2Q+;CDM=O!kEMM0-8cJtVtUG&=Y^R?`JLW>L|=sYU< zMPq}5QlZj)w@AU7DJomn<{1L zko__O0xQ>>&qDMcLE&SV6R(FPO_lVZ$&A$)Zsx0y!PoscPA8=cj%eRUrxq=I^jNd@ zlHMM+)!Jhrr@?s>r|Y6JHO3x^zYZcfk^Jk+c_LFET*8!#$JJ}@&7TMv-TPgVEU&G5 zvlmE7w$JrAvgN{1xZl$p*X!{`FnpCHMIh2OU(`qZ``XAAwcSA?zeL`G%lULG;E=WX zF-x;p^48dc9+|YJ?~a^{)7$1xe%h>dqb*Q5#_jh#aEi;X%lpsG0Eyp2^Z<*Ab@u|v zy||6%=xRXm;;05ssQ1b^?#8;`tg*}0wu$u*H?{P0Z{Ny}d08T6c@-t;SEEBy&syeP@AUZ~1 z)&_U-R$LvM%4c}^y)FESr)Of{tJG}%t0d*v>eEiRMy%s#YHh=K+h>qXs%L78)xO)= zV^1=P``yAs2g_C;+K;~msgGh4PN>D{dW}tclXb5w&k!IXioS6=z10|d33SG4Wbv5j zB1ke6ZzH>?(#>p-69#^-qw{i`Qdu50D~|`^T1XoawL-E=%YEI%^M#+#b*u|z>Xb-` z>+?;~#^YiU@haE$*Mh$e;t#fiNs%tPS$v5Q7LHh~qUbS&XwL6BW^62ZEj!cNL20al zIO>BrrIv4`%4k!&eY3-{%GL6+cWdtqw`UZx2C41VB)YCktW@K^9@BTdu63<=UvZ9C zPdA@{s{C$4^`B2dC0=&&E)hJd6lB#JxFM5sk+~`znyhO3}Td!!2Q8%#~Kf= ziby5CL<|s59wS?`^?Dsnu!|)NSU8w&+8%!Xci{b#=%)M5#|wK+R}NKIj$XOS* zKFn>%y;K;zs+^GTe8cs;xdrK}-kIE-lvF>?f(Sv^{9ICvkIJ>*lE`v1fQl9EJB?`{ zg9ewc`b1C#9_RhnyJOFyp1U4i@&G5tW_ilCEzbj@iO~8?`X=wA&h#YH(D53f*N4HC zHnWehG|Vq`uP@zKE80|YRC!aAZsvpIuDIy&F&ZVxOnY#D-MBi^$mm!lh}l_dqDT3# z0eZD!vCsj{d{V|`9Lx3Fcw6+NmfJYtt!YvnK*)EmSw$@BFj8x;))a zhN6j|rJmw9Dnqv=7~xj9hMOfZd;~=6mc`Bx%gf?6x9pNWKU+8;p51KF4I1HGyGoDa zxNh56YJmWU!HE_|$rbCh7A|78TDj0WPo(57$79s&N{f}Q-2zkGWu2(GOqNF;{{F(80J? zDSd{Q9+vr}EWkBZg4>0>J-M?f*-=OY)w+<#uTXE}Jzw02xecs9V^l3R1;Q-HH`ERh zFGlBHPtU@r&S!N`%LpJ4I(ug!U9a2?p^+376>3XAoBZAl8Aj)~$s?S@nF4Sr=LA!{ zC&Dj$U6Qhs8(j0~sBsxn7mT;5BijP5Oh@iI)Y3awJ(j*myfCWWOBnPtlXXpgZ zg8^G)CZ~5fYZ~!}_=@47o8632=B_sMkQet0DQlr(6=h>IO!aK`V9-}Cy z;@*j+fiom*HeYS}bMQKLXlPEUJL|Qwdro8DGM1+~TZc>F8!T0D4^2eyqzgOBDkx#5 zq?l2#E3`5^Bw>VtxC;s=_pJ{1o6ugl9>V?c;MZE?e4Vg0k)WA=@xKbJ$`0d?Y53AA z{UY}U?bW2RHzc#i^1IG{>pScEM@avd{XWl$E7#=CV*qO|;N+L!^amEth*Ygf-kMGNT`ShbhdI(b=sHRt9w9k;4Uy(WazM{W zOGk70d7f>d8rTQJDy$FSW9XzJ85ytiv*5#;WosAkr7gX2Zi-6ac_JLDK@tQkj@LUZ z`fxk>Y;|yrOCy-t*_QU;dC=%Ll@bj+xL5n!mQGw;&~|jFd0ayzdC*O8kYo6;MgYwe z+0`gU3g1K+Pe=H-B~~xqAboH8Rme~M!t>rK(o`&#urnS9S#BETWpu^y4HB3P41DpT zG|ABU(9E7$TL69da%f1SEwRLlpgGj7ERTaeh1iRgS9DD`U1nj!d3sXd2{%WjL@1N! z8TYYxO%#zzbOft|r;7e0$CmXIEl21sMk`gi^$fHJEhvhS1MhGN9~4nwnVXW^_3}S+ z0s4$V-zQgeo$y`ek1KY+l=9CHr5YSof}vmE+1UMtH0stwNEgff3+?|zO}uf{V*4sp zis_@wl{j;|5En+7o&NGDw``R4)?0=TzF2KqYabKkkTlI;CPK4Sp^dM7SD~zNQip8m zZx016eNu9=qZ10ZcZvJTRUt_<2Z(L7ZbUu;zb-M%E#OlNiybVt*6ww8-7?+rP?W!a z2}>lYr;aA=B#651G^!gAVz%o(#(bkMhwh&cp1h6=bt=o|v=RCYD>GV)3^IXsybrhU&D2*ksd z+s39m=*OEfkAQ!eu?mHEXHn0~rNN7}M9WvF)&8+GU|m&LSI1{kG{Ms)6eZ6o15a1| z!FwI>`onBI!^w2~f`8(ccEc_8hK@tCLeCInnr_cdX$KWZy$S*gKF;ESH2DpPH^U~wOx+K5ng?Sm#T^40c zI55@B8oYCLoTtyMmR!X*tr)RT(c>~EcLot8i%C z9^LZt4h01=ZHicPZ^SL_XCl0%W1f$9*bpR7i|op}9^*|c^jR2%zQ)4Wds6k&Gp#5< zMRK_Qu61luFX_ws;fdPqVnQD8Gj2>DoM=1vGf-#`?TUtWbK1Gn&=u+yl#0S>z8)VR z)L7&@&KxuD8iwxYS7bu%nOr!Om+mnToitdUFPNl-q$MiYS)zTjq(h_+Af|?R54+5A zp*d!d!g`|(vj-ya047fhE*_wz)?q(6)NjW;Buwu$shmHJGw>`T&yVmop#0Sp7>ybm zQqUz=2%vOc_*Ckov#C-^5%}MKp-2uh7(S$|fov!dvJgM~z+DppD)sMz*MA)L>h9i7 z&WIeoQldwbhJ-gJ##}vKv<^s@W`4!MOyodEuVFZ4DDc2MoYbt%;F2{NKFxb`N#*`$ zfL#5sAxI7YWhOji_zgRCsh^&XtT!fHV~MaO5%z~khr%wxt%h7yvHB$<;b^Gv3MgiM)9 z2qBp=KgYhizrWu<&sxt~&-%UZdjEL+@m=4$z3u(^T%YSa&*MCfOO zTDD3k+agl(-SpvX%b4%+<1fQZTIi|A?@hJ2PybF+r)`=N**v-Z-rZyz>d?8@d$;l5 zK8h}T<-zqj_1Mi4h892Wm0yB#p;r|c`pWMGF6xY{6psd6Hp=5&yHvr|kj22jFy!K; z!7aI@`{4CIyUF^Tm`&JdiIVv-O$Jq8S^<)ye-HM!ORx3*!ZjMXw7uK6MtJ<~fsQ`- z_r)!DDev?Bed_7w%=cV6Svyh=cd(a#PPb z$?kuvny4yeWA6(O;)^PI_aY+gj6F5x@Baho-aA|*#G8Ite)yw7uB^RkZs*RG2kAE3 z7?0OHqp4fFnI*;Zv))mK`QMA2RC)H)-Tj4?9#8p~PuT^|+ucPw1(WY8>`7{w-I_N4 zh-vSCE+_wi8>jqrQtF*6;)A~Ld;dL@WcRUuFKNsF?%uXY@2>oxc_X3ZPY=1TSJMTK zzcIW;z0PyDiUea*y9mWUk73J}l^_FidHKf|S&BrH{vw(!LfgWO>FMZr=6QC`|9kGr z?z8_nZRHrrzqhvK0p)-8G^I40F9Q z+41q!=DFna54UX5*`Ly`rKQ!~)6+BKkl&-LrDdL};rDUK+Imx1-PF`nQ?rXz?Ttp* z)X$&Xyu41ueNwYI$L3?Q_Nn_>zg8MlUUn;s5_5XdI4WW{`oP;;*XVLNk*Q_bzlX}f z(J@^+-?IMM@vg2e^-1}mBtzQ7)YR0R9Q$fNs)&dPamFCO+k!nEY1*}q4~IN|?tDII zdTDjtqa37c?1g)jE$!{O?yHVo6?`6R&Y2o+vjIpwO=BS!S;QGLii=(6MjP$MK3_{| zms;ni^_g`RS5AIb+%u$An{``3q4AZ3E3MC-GLHQZWdrC!qoUjoXPj!t;t9XZ8Txy< z^G@99mt2&)UkcmA#>YP}pbftG?AbGZqmPtnYOXQ#sc~^Pdh#rm`du@mV}7g_^;r&N z88>{2I4wZe7<0~7R8(}dDVlfx!xi_EygWyEe`aTAH)pRN?%nt`R23W?Ov9;E*!@C- zyR3SXStHD$xX)QpPl2rJh=fZ{PR=vG2hs*ub4$x~#iU86JsvK`^xj8bb_%Gxi;z!L zNm6XbPe(@0zTuaqO=9lvh<$TAQ1%;30Q)-I+b18J70;ZFk9? z?1qt|k}=BNBy@Cia&mG!Z4bA05)zanuny0k|FA9WwRAExG?bRUFPBhM63Um|rK7C; zrLFD2ZW5kh%b^!IfxyWrzjgevzrX+E$JiU2^Q}t7#l@cUr}ugpaJl zc@=8mW1yw2jSm+S74`VNTlgd|DqH;UmP%Hg=1j z4trTeg*j9%GBWapp&`k(v-bALxZ;aZN@3DiuZhE#c}rKclb7mQIXO8UrTnoQDg9&{k>$=I^8elz|LA3p4-s^Zht)s>F964&y}S=_g3 z$X;~8rL@uHG+hAJ?8J!^Cr_T#xpBCPPcX7^$eQE8BYfYqBPq!?wUqX8`$si~0kTyK z;$LqICbiZ{#I>0_ymP&0@O)*(C8eE?kx@@w9l_!3Ec_xQ!$F*pmyP)2!xiH!-hU#$ zS6T*NeaQbjzIxfs$AJ5gzXo^MMHc*{R$9mK?EKJw03@bt{V;&ZTb`zk5iw+1S`1Bn7Tsy(%=|20Nv^qGEr6%?)>VcUf6kb@gz$ zgx||%UdzkN zyE8ZXP$m|cW+t!4vZ1jtS1ZkRX;9v+@=+VtxC_wTrzGAm8)u&JJW zCr8I39i9N$)2@p@pFe+IUtgc5olnjrx%RUw+a&wW*LMb&E?vTR3q;7n@|fT6l7dRn zGlh4bqzkxr=1g3Re57DNaj>vQd9=avwl>v5o&M(LL3>duuo$0Avb%0G3R&tLrF@f` zdgQdp<0~BY#eMWbmTi*@*(Qzqcaw~aj3gu^>^;QIfo;sgv*=QaS6`koex#Sd9*ezy zqc#v>;_d6(6e+|@AXH8+Ob?Wo7ZiLQw$e}|W-Q6f<^o)#>ugghwj(e1E4pw*_@*kNlo>AirMedpGNTV0L zt%2pbz~s(v-uC9j3%Zaxq*X!O=d))A$_Mmyblkpwq{`^zIzU<*#N^qkAhg4aG|YI) zQLHlncZuY`?cSEkHu^&}28cJ`Due8KhKZ1dJS>q&z&~?jAxXTlyo7fHCdH2y{C0}7T=PRnD}AJaWBdJx)AmO z`&I>Cat3SNy&}$ z;%Q}$0|OqbQ}|v9#-KAtX~i6WVx7GxQqCM5L}Ccsip`MUQ&m+p%d!1)E>&V)PEJAJ zbbLGucDE{L3NF&n&{@-<&Ec-+1s;7{+`ihXYK6l92^!(?##u;#=cY> zD-7WulpB_1%FW3kE}a$MB^l`$>&gdvhK7cC!f&mwEU>b&BK&2l`5x==ly7dVa&dCz z-R?5X?!sOvOHWS^lG-RJDsll-*+qWDTKGu$pBi32v)Vug{Aa=XVvkqrZKG3~E-s?# zd*$Wiin{e_(|LRhI5|1Vc6wD@zj*mF*NGDrR#rj&{>9CUaTkJ2H1lF(znMy{YHDa` zXlv6`Au7AFO?##m7bZIqu(xu%L)ewV!o!!6`a!+M#s6e~s5Jij7*S4FkbQlrL9Tv4*U=x;n+A)^U3+?ywzu z5540qMsVpCJ02$ULFW4Q?VH$4$!T$MV&scCO@mhR4}s8LL&_O$LiX(_WL{b;;Hmm()icp=H}wb zBO^U}^a#r1EWBT88;^< zvKYS1JlEyVuOXdWrDRo?QayY%=zl8(XR;yRGtzf3N0Z0ve~D3WyhQ{*sN-_WpXqWG=bjEjp) zfsMM;ydl(k+r6WwPpc%WB1_gAWj%gO21~;Jhm}=TQ;x+wQ|TEQOUrKh9?Ps$Hs?Gg z7?lQYQ&#K^Nc!&3H04Mi@F6voTQa&SAP^X$rKN?YTMyIwB-t(-)oko%@4Cb~;qg@S75TuK#_P7A9?w=Vgd zJ?2pLHYH^hm{BuKAhHp^fRaoTA=NZCw~K-?wZ+VjgNy6d*ypIR#_ERQ*=KSIjV1^v z2IOw*14SC#pBoxfI71&kd`RjYJ8(~)Eo5K@(=Fi*Bc~#OMQ7gM+uoa0F#8g8LqI?P zLSby~_yJNNiLtr!1gfA~EX5&zV|=_gBi5)`ho`uYkxV7IsV{Ksd%L>6{$?F}H04!S zS6ATl+qb#HFCD|vAQ0}?4)5T7)H7vy`}PrWXKSQUsyd@ns2SSY4FRTr#*9J8(zPbp zJE~MGaSm7U&dN4;h*4@gIyz|d-GW`uv+PGos;;TQKB_ayx_n7=Y;L(gM5;Gh=g6dE zabY2KZ}wBag01spRn@h%12(k`#4zX?*g#?sGOm9%cw3|31%a8FUExIznxC@641r8i z9zU96P`kV;Ubl{~ST`Nb=bQ*?{yc*ZN=1hPMO z?wq2cB9b@YNkNRLj&YP9lDTzGWS?c{xcxa_@_qe^?p%&rj@&zQ6t$JK=0{5$Qu~f= z73V8e8G~q2PwnxRW(;zQv6n$-c<;>Xf`UINmkg&(+n zzBT`sSHnnmp`9ttj+B>|mywYHA_B};RXxyon6J6&~(;_4W4q^eft0TF4a!=`FyF6B83Cs}ZM7F&c~= zak)1XQI*op-H;d`znf&6k3oS5U&TH*Nqu#7bu<=NIDSq~k3O~44Yt~`y>V>rnz}kW z3yb_q;oub2h57m0eMLxR=!ULwA6j*j3^P9Lb275go%$;J#$fqjbP!3c=)gsz6fLbU z;E+d9XohiFD6pxxxruj14bCmE z*T6I#bH%!fv_P?hX*IHff|`cLsmCH;w|-tIJ*)4_m&y@;a*}OmbqIvm;$rlYT1l;$ z+-Q+k^>lm;M8(9aDl6lalUv7sPEFMm=lT-~9{OBwX@g9S#hC#qqmNJKcBT-5%>elUhQH?ARFRgJ zMzT{^@BEs1gLl6;$+mlef#lg;pY}cS_w)N9bY@HXX_rz-ad9_**Bv`{RG0z}h}nF5 z&}WGxkH_fk?adj=DI}Et_O1Ewhk)_%agXu=lk5N{DM?{AzsbAiW@dgtK{G#o49+@> zMv)tEQU=Sjou(zb5O@U12e`biudjkH)Zn>Q`FdVl9O_zlXy`sBNgYD)OZvo9WGdIL zp(n(OxVjcACzm`kxP??e7ciQVQD%PYmR)h*zJ2?8?VgHN6+{t&IkMdfE~&FEG&3(z3&h|6&Zu z+54u^uG1w}@3?zfQ5Ep-W`5~g+H(lW;@B3E(}(<71q4DH&JvlT!-qx8nvePzeEIT4 z1~POU7&b!gd7;di$16%0oQ-a<6$ViY3jV zLylNkM@MpcObak1DJSozrfxK`J9o|tPywIZQ}=#&7BPx;8viEGh7PRe$#437`5eLE z1LWm@u3o3wO#(8HCp_gJ=0zbv*(M>rVPs-@nwXR7jLfa9m;}t*nq$tLCm{50Sy(7w zStuxkBIH39Ff&(;*j`gr@Wq>1*4h(PHD113~B>3V1 zCZ-w3V$_NP@B8kO`k){H$F6XsW@J!MP++OiDx)m~nUU0LBXVQ-+c*5Cin21X)1{;H z<6ve!m(jUv#xcDZMG?e;S#J0D??x3fy=-5c1_!$O}vt(2$+r7=+Nh`Yl` z7FuX~dwU(7ms)9x7cYX{l8p;C?)voUQyES8q(oNF6cB=F6px6=;gb^qAIx*>M;^`T zIlbx`mY6Z!M$~{L1O*Q@Z7Ybe))aCOWr*ETU|w+TMau&>W>mA)#VO2$MhaQIl8!k_ z?CygkSIjonmccAY8CscSYsR%4mGz!{H83+XG4W;G{!O@^XMv`=OKI7R^YsEMgidoa zWxCKyr;j_Qci#1^8rehpBl|ABzYO7Q*^@~|A&M?D{;<$cg-F4vEOFBUkL>6&rn}AT z2S{!5^)qA$RKGbZ3jdKJbZp3~0P9~#y1UH(XsI}VbbR%D3T+wmq-L6(|2j9`Uvro} zTW(hN?}76FB`y8W$uIvAtNx$(M898hOkl`_qds%FANZ@~6o|sjR&l++cD4MQ0dfdB zLG=}mzpq)lzkmI&Y5adyd@7UtnurnzgO|k3vx2tU)a|9Kb?vG`;YGv0Qmpv zb+nY0`NcUo{FG;&(iXf;zb9XA%{;fdj!I`>U;yF6)!SGT)KRY19r0IcDWMG`?m~GQ z+?M&;;5^&SCCfdz9;26uY($kH1pkm{=vsFlR27#Q3~KSku;>Xe+Vrq)VZ%wqcSVF*=N zOl;jlifVS*4Wff`G7$cmD2j$GL?&nGTh+JsM0XYZr1cs9`BRnS0N7C5sqFmx{G=pb zKYP(AdB;nW-I?3~cnQik2a-bm$%%kj=DAw%#y89!E$*|-?n0O1EqyGXdb)WmY=-YH zt$x&+cV!tjzdpNtG3}_)J8oAj z73dfy3757hM^|yi1m!!cQ~3j+_rf(!7n2?#ug$aQ5wz_6+TC4GB#yv3Xcbr+NK03a zNIa<>PEJjocPZ^Xd&}Is;18)s4hRPJSI%fru*?~qDay&fkWh~FuUt7wx!a>T9bg}q zgA}ZowAwN@>{z@xpCongzyWTZf?pSANTdV$EIlWZlPpUYI>6Y>^?vALib+eGLWi1` zlENI@+uM7A@eT{LlW$B0ntAc@|8T%-v$b}%wgog0LDVz!py*B5i-v@R;HYeUQxlWL z03q{0CfET1{8E;J*aGfcgm@xM-`2G8L7`J9+C>|NqCy;J)6}y;d13sXy z8yOuf`&GHOLXgVGU=hI7vae{QA-uMx#)cOPVE%wdacb(PA!`#Cn+nX#}chjj(P8 zqjQf2Z=t6jV{}88gEqgS6St%9I+Y8WRbr{vPBPjG9Nr9wyAUPG;~{m9Kfh*L^yH02 zdrFFmu7Fb7TrJ3V%n#Nwa^0e*Td*)(h z*KMn^WfN7^He7mL)=$ultDnVY#OFSpra8Rh3dn~cU#3^$cd{TY&VH+@ST6zF2!Sz0 zE$!Rfab}Nsi}`A4~%@BTD+E`mfSu8?jI`Y zba(D#pVPqlFw^GOrLGZc;eL-*ivZcV#U86U=aQuXPtTG1(1vhc3cblQN8f>rKvRMp znX+trtWC$tPGyEcUcdibB+Zvm@A-LQ(sMR$786rb63)K{q4Y6158f*~uL_k?gZq_; zor#f=k&aF;LaiSk(cLX4gU;YM2gk>ThStdi@S*^x>2KcbmXt5mn@LoN1ZT|a%5*>{ z4j2fWV;{O|9=#G_35mYzd@>g<0NNwkuWz{1{|>du9zC1oZ>R)y*s?R*OGDXI1N zfk)8=Oa*}vjiX@73+={$Zorj6a4v?7XOgY^of*0T%|n^X;XFh@Sf-(`Z*?ldvV47! z*f73%qnT|&bro@My}h`v4Es4*CCQwhj$o*nS5Tlq2>$d^Nj46x!t5h= z^#T@%Cp*1{5+g02HjGfgzV1sw@tF?9{23XUYV4%L__y9NY|g~QQ6kHXmJToE+-$8j zw9(Ym9B>C5hT7PFXDkX-8$K0b03Pf6hX^|$Zx9z&$TDTy9}exbFV)@Xy;$$RGCjcj z?c#A+-wA!5ri1ufoYWZ;M|C>M7I@9iq6P@E}UdeveEC4Ig=l#7N(N7$;r1e*i9 z*dDzpghHS^RFCZJIb_L$q~2hZu{d4jp5=`ukk{P*4EgTbN7t6`+c<7-i3S+<1k?+6 z{Z2D}C+wt02f2XEQMwTCvwNu7G}1awXy?gCoEkxAoKe2vA|vx)U}h5=B#23B!&hqU zY+MVZje+%PC){n6z%4Zkiz)VKH`}#8Hj>d&P`;Pv#(aH!^~VQ}_+30o2@k;-8V1B5 z&~=hh^hRs+Ms)xml;b9;O*apZjov~#C*(4l;SUgPOZCc)>YieK5!qFSnQLofQc!hV z#uLIpfrAo5^Zw3ipbQivk`Wvjy>4@jhM7=*rI1Vz1Wv$(iu*_AaCy}6cDX9Q7)C__SYI(c(bOI<|;pV1_aTnlcHn(9tzf2W@7 z3079)>E1#}d0c9#8YRxN&~HFMrnj5Z@al=bYw|N=n0ZJ`&eeC`boM z&qI|pF&Q0MaUZ-u>0cuuA;A+q*_-dNkfJ~9Tmn|DEWF=5mz3oGbq=oZhLEREQFTG? z-#?jzuJa;GMbD>k`^C~m7gyKLRL#>SpPo;HR~sGW`qOvxyY5_RvKylUt&Hajxh|?4 zdol&3%}#Bh^DV8z9PIbQ0%LrAgW*5F=TqJMqHx~#cznr&mKnpX$t>0DyvJ!;LcHfa zovySQH|T5`K(jbT{s^1~^m6zSR*8CDb~elz)Gd(C0%V6lL@2O9=7i9h{BrljF4F~gZa(EouJiWY%aFAJ!ePRY} zIaw9AcHYl0ZOdb^Acv;K(0-vIw~f?HgZJRYix=H}ee2N9%=upsE1^*NGFa5caD9)5 zYXRyC(7do;!D)DSctG5Ir`-;st8ui#YnQsJD&pS}z>A1jGdvc!LgfTd&S~_Vj~zR9 zuQ)h1F3v)*2lxjrmUn<_s7{l(iskPiN&q1Z3g%#7^vXTl?d|8SJXd>6U)>>ENjx`K z2M&WY>=>J~>48H8Y3U<#P|3yL>r3qGP0dj5B5+?;yb&$wGi>xuP1MO&=DUy6b;23>0LbJD>?J9tqRFe}WkVq_}uVS=lRBvMlafnHqHT^j=K&?wvBM_CxRjj5tBO zjHH4!<#QA{19WV(_DAS3j0z_Y2d-Bz*%fv2Eu&h@Qd3fTpmYM|w3`c1?bvp2 z`z~_a26hCq-F_Wx>2Vhq_njm7TV8RO*a#lc+TE{T*Tmgy;d@G2HyR_$WsxD$#7H9k zBKopj*2mEp&u1!^dOU@{-H00>GT>j-o%qS>!>YoY?D8>aPmns1`V_S4U;A>I7j$0# z%o3BBBeyU!0g??n`xtZ*ep)icB)jo13B6Yc`914P<(ml6(l>AXQZlwXm-Zvnu0K8D zV*vV1S4T%)Mk~z`7=^pR66qRf}`{{TaP2O!LG`yyacap=$xu*W1LgM%~e$G@Qd z_an2(7-ebzJvSxX7l!5w{Q=3kAZ8R4GV+lQvp*y{US48nP?)Ixs1&01W&n-MMpzE#y*y@;*j!$iAXnTq!XzXW2qf zrl-2HFC-|RfCfETP3>Pp#L7+X5Q*@~MHc)g>JaW- z2F;^dnw9{XDp>SjLfg?~0qOfzN2+F~VGRKB&sk^Wj#0;A-r6OfXchs11%O++NZz|sMUiCI@4$f5gDe^D%i@f)e$FzsR8+xQauoaGs81M4 znmPta%eS^B*URrQe-U~%!NPua{&=QAeCU4lr^V~@SL=*7WUZ-yl)6l#*MDX=b)@S? zK6%3DFwvHt4);qH-zPXf+IoA}Q0s2yc4K=sNv_Hhf*(UigF6HS<{uK0*E8q5uRn)Z zN>{fJ`2Ys9K1-%kM$pnE9Diyc!}E<~)EN~axFJ2(7}hm3?Ct15PY&QvnEG4>y6F3Z z>D&!?R)79D^E??Or#pDiVezLXK@^1-jneAZ8$mmUzb7UpdY}MFMo*H7EUgvd+`tPm zva&mclogLs!n~uZrUvR8ipar($jvY)5F0^AM-X&_FRE&36+w2mKGi(sxVpX(EjdHQ z1NRtX&;{;8Tn8S3tHuqv{r+ADm0is7HgFCg8zQziX{OJv|L+15ZLisMl1{p;it3KzYh{3$HVR9FGPUz>F6^oSa4G zQrmPK!}nh@&+WecSq}t8i`o6A_pgD9k^Ps^F5pdIJYg-;Ewml6@|cS(3t$VE<_YK3 zD{)3a#G4Y-Qc8i&`z-U3N{aj71B5aLr_YR#BF62B4IiMOR@(k9wUqQ~Uha9e&|QmWl~{$M3AG_R1y? zsXjkLx<}<0EmRO$S+(Z_~P8kyjIdTVUGVz=6P3CsPze$2KQ&0TSz zQMgD*e)8G=OW4~P$)|5(#Ild;oZZ+GHSxNsp=s0DnD8R}^7zVJN$xuLRN87w`M2B$ zO=CzsDAJEtL+A(S>FI9?MlQkPfeZ@PneYe_#qHa@MGh%yDY~UD4tUJFnB30D2u7v> zEpKkjYyulqEIh3UxWp4~YHSSd8m$OacH7fC$38`<=<1dl){qjunOy60?6Igg8sY~= z9gbXpy~z2ewZji<=HCRP76`cDGo?)&4bt9ceQ2M!SJxS)+=V-11Kp;1B=o|>KpvyWyBl{UZYHo6{O zZf>$^6;KHFMX>2G&8bvUwDtArYHGq~Y7~5tA2l_jY~^iXVT1uK1k5W}-b;J~Se*q6 zQI7I28Z{kZ`s$LL+(hv%%X{@7KYlC=ekel#CFW7C4^_mwvMisYAqy7cffM%MKL(>X zo5CK#!t$72=(Ay_&d|(0^)TgR@tMJ%GLmf-p10G{(!OpQD_)&0E_`?&c~Ar`E^Hn^ zdW&A4eW4za`J4xiLjm!F-JrkJm3sev?7BZSH2RMjC03d|H%?PIfbq}CDVh5$27?^T zLy!fB7Mp>nmdB!jmo{GyR~{sU%As}1K}Uc;7(fK=V*mj>S#QH+qx0Za0z75q zo9i=hL#$l3tT+5C)z^8iHpy z>%H~9!n`Wg{OB;txNxhO~ z5|dPlz4BD4s7^4RXZ~iUvLjQ5AlVnL)T9t=Nz?A2lJh8w@NxQB>B|S=^#SobG?z&D zPkZ^)e4#P@IFTrpb+e}N`=UykH@~tL|1%Tg=5HsAmYS=oE>$Nn3gvdyy?(79@S1zO zL~+Qe`S~H;_ya1T5tSp)sBJ1>w@k97mJkTcTFJS&vMnd!QNOVLvS&{-@;2^Jl{4Vc zqb#j7m_n_#LQwk0s`eZ^taf-v)W6qKsEQAbL`mJ2!4JoRF9I;H!b6oAA8&+*KBo3F z2d3VX#*ZIqC0y(WIw8)0tivCNsJK2T36B9Ja|pdFQ)fXOEUdtQ1))7XB}6;*rK`|C z{?kJT59-DTfw)BPXbd0-h16YrWeR`ppvd7C6dv^Em{^Aq1b@>Fi4Nw>#H1uUk!ZzN z;^U6RuuGgLT!k%LNvR27bTy6d9^V2doW?NJ2l)Ac7J*p`N*aV#cEy*!R@Z^XD##y! zU%)Q3`tbNL*h2gf3-=G#dc8Wej#9=aCBb4Q7WH}X`-G3rPWYi!IbpQ#v$V3X7=Ok-i_)Ipi-o6ToG_T00g|+Ix(MEC4q?7KP;Uhv-|xW@anM6BWmfhuwSABf>~=j z{zxLvMsIWT^<1shbn=S>119S}TR=ns>r_@s^JI!NWa(vej?JOTFj6Wo(CsUrD_ms_4vEQn55Dd<*NjSTZwF3QyJ`G35Um7 z2C1ox&o_S|yd95?c9(T>leX(<=2{&e;=Or;RkP2&N%Y0iae>9daWrYMBh(+q6p~!auaSG&da1|kCo*a3Am<#{`_fpK?nXm<9U$wI=BjW##~UQa%w~ z10PuxpFBiz^kIeh1&4hA(dL}RWxmfXqmL}`+_lHM^6+Vsk49Msxzwz?BK_WEXWN1G zgU1ggx#`Ya*4y?8{H|4B(KQ%8_~|e|@aokDGHq>jwOOuK%oSnV?;pYF02Dr0-Gpxw zE*AZc=tG~qj3GpKpu2LPYZSs~QPAkwM#Nsed>MSE4>?2q@GQKuk&Sv8Fd9rka!{<* zb;{J>4sR&jL65A9o&^0H%6?6IKPbR_%RW)yvh=D7kUCvi-aTK(vD(!1Lg@SGhayGz{S96 z7Dp+s(~iQcf=vw#mb(EXE$IVU1e&3_WjGHCMfO5!0VmZD)PP2H@7}%9=(MlLlmLFf z?UQgh9DYD>C$Je$`(&i2Z>-HV0b@U`_T7%M#`&D2+UQi2h#ea%>j%ndwgR~Qgfv7uK` zV)wc)dW8RYM9wL#MWwg=)Kn2Jb@nZ0@A&TR=bv8_Qf)migQQ$sJDz@o?y%Bi!$ZHf zpXP&XfA4tHsK{!;e=H*QTHEMR#vqe;Byxo3xZR5DYS*>Ph_OW1CQr(~ zP=URy-+4FyZ8V%h;SDt08hn`Ufr&D_0h@OmJE9K^rF0C`l!J#3NkXu>xN92?A*3!0 zJRIyB0MD+=zej*IXO>b_-=fa?czeS^kK=6AJ(nBwr^{9?y($J5Tp*L$F{T?NBqFjrTg#+h>asAfx#1M0C}h_B8o*hb`%pT)1>OU~ zZ)Ig+5(YEOd%$LKjzXf%3e%HRo`0%foc~p|xstI7Yt8jQCq zd~ys0bvjiuyWj;HDQ2|{y>)Q*)&T+p>6Y;DLwOo8`yDm)4KKCE~WUQk%u!tl3~ zqoX&$ZX_lo*yMMh0wiAH=(St|12xtdS*vT~1$%Gw=D_Cq0BS%|61$YtW_bBp!kMGH z9kUSZ(1jKPg!-RDx0j7WS2LV=`Mp5(%+lrvujev4*HF2Ur>vI?L|&45e|zOReFxOk z;?J(Q&Pn)XL;<{*a(ci*5T`vdN*C!m)nb*C8Si`#?(Nk9)s1cisqJzv$aall$0_h( z-qJB=jy@+6mxXq`W#7=g5S0;B*^4Y-Be6=fr%lL6XSbU2(?z>X9FJ}?&F@*7>JdUS z2hv>6h=;!+3*K{$ETcW)6U1g1p%8?_u{-YB2_FHoa2S?aVt+vFxIhPw%q`=(p{|t8 z7w@$^BA#FC+As7UD}&>88s*7_r@w3VmT`S+{k>=Y{S~=CWlRnl z?C~m_q{}TF&u*Rf?Q3gG+EjU;l(zS#A!(FZq9xG0CCT}eA2IuK77wg1s}+fOPitMS z*?h&YxNtaUkgeWzwfN0KshF7tcjKQYCh;x)DrQ42-Lvj$n+1F;hFb*|o0%icX3nXa z#zZi=FLms4`VxQO#c#&k?(v7xF4i17XuFN$^MkJ3T=m@0WMggZ)p4;|Do;VfYGfnWejUvkK6!0z?Hf0kFyWQH+69T+%}sze6d3MY>i_Dw_K zLc?E{EHfwz+P$Fes$x>eWgmEV4j*awZY})zl>}toc5??f_TQcDoN^Qr6m+z=f1~;q zjPv!ULH`V$MTCVNDmekK9AGDYG&H`h3-L++#|!ZKtQR(aUu)|e1hoBB4Owg<$(fnK z^zSghVL#2#0By8)NF!|IX~j-zv6Ex~(1*P$C~*P3Wssa>V+kit1c(_}8o!vIpC7yf z-y#YJzV_Z3O1OLJ=`p6E%hL(AAuBuE_*6u@Iiyi2i)DXY9$MQt7O#VG?=_Q!=LVlh zO--$H=u|=DjK@+j81}}9)4s(PN60Urp~o;AprXll0wJC%vk=3$@Xyu3k=9}}JQB->CCK>nHMin6jM zz~>d$GGFR{wA$vJI0o8~m586DbYeOJlMJw7p##D{kJ#Qp1JGxgk(Sn2U;l0TOmvgF zt}gDbA)$K4-{3h|ijEF7!lR%d7|llKmf`y(YM;bjK4w}Ws8&T!?^RgXhcvyazG%3f zkWP7cc%TcHgkFklG~uU%9#SYMJpE~vaq0K^^APW3&H-IHZt)DOM z?cIGKzefUwuiibKC;u56u3Ye$>kvwx;=A@SNnvTDM=M$8`|qpS0rR$B>Nlnqev`l0 zJRBvsJ{ejm(``+0E%K)Rx>LrR1p5nuQz=O`+CL&}?A3WrXl!OUiO`U$u#?AbMkto| z%SF3Maj~LHXB7rwrb!63(Mb_cD_77j-6g@y%8?!nZH)_RWvK-@+H! z2=WWL>%xja&ek;<8VZ$+sk7xCK9UmW>+AVTfbO-H9*8R7u@w=(^ zn0+~FsJYUr*S)x&dL-akWxVRMGdG8F@?VYi$=GGRNsV&pjXw|~Ezdws%2z!WtkvyF zX00i<;4B>hm*?r!H{CZ8?{FXeHX~8N=DuFC2>QHv)!t6zFx` z&Vx8$w;sY1PBh5FXY%fy_3Ng+()ar;VWvVBRDq5}p*yvGkdt1_F;#H`8yiCTU0ZH_KBLJKZv7CRGLbPESc9kN1^wv8F^gzhf$Oj|;aO*j27>a}c?yeUPeU#r6@VWohY-D6H-0oH?q0x7ULZ8@_ba zhW-Std>r-1xy`QPnfDy^Yo3GAU15FmaxOE+SST+ZJ$CG9sod4G2T1OzyceSoZej9! z={9tQe8+)``#)m*?!NvaGakeG;Hm6I*3x{1tkO?w8?;uuK7>ObclKv^9?i}^-SqSG z=TBqDqifI5^EA^$w7nq&-{pCxV&Q8O-#K}SndBMq78dJcj~>G{%?y!{5pMyP9{N;|*`$1wVT6##i9D!!zB>6WS)P z|DM3G#cuJPcQR9a%dVTcy1W0{p8k|R$@}-!PycmZ0Uz^!4mNoI_&Y&rd9U#04J zRoLkG+3f0Fe}BEtx6n6PdaHM_y~D90fk1+8aS_?y&t3Y|Tcx`9SlSWkGDltA!0Cs_ z9?iZ^@G2@g@?Wp7dYvK**vG6+-~Cc9ob%F+9~0Xgm==}keq6r1|M>fu6#u_AF6*XG z67sBA(Vfn%TV~RE{ypEa*K@RgoaXO`F&_WlUGIPS_Wn;d_CNJTzv(4%SiKdKiU(Z( zuIb%f=UDcmr1#ZM4U{X)RU2lWxhtss&qd$$tzIXeopyarJe4$dwd~(paTb0ZMg8}# z{@=Rn|9-;%y%2tHQubPB2&bo9Ze}=HZSO$!_mVt9EGZKw9?#!6R%#c>b1hhFpgmz+ z@@E2nv#%O?0lWFn) zfK{8v?U%}GwS^YM`T@t5mDDv(to1hu z+9S2Zu}Mf+X=($Dr)U1CRuzZ04)d^owJ3LYCOIa*BO7E+9Rv#eKzm%AXy*d=AR=<- z)AN&PRDplcD#L2HWKo!wW&ytdxcSL~Dj!i{HW_ziZ5 z4d~VwndmPUj=_w^i4zJLo79Qz_(~V3iJKcs<@tGe{l>WvxnZhUo0g;e<2CQ%Zf-tN z>Ae??Sk#jzzpAN~Fyz=fP6GZqQq0K`FK*{yp@Mr2H_ge;UfbCjK3o8m!F|5vf|I6x z%#{NN56&Jm+UZ5q#r_H&6mLs@Nx776K-irdYhuEz8)hw4lq86jq*|c{8y(Dj{~l?8 zZo%jF=NCQ;hP)Sg7%}cl*Iiv5*pvj>0>Z})7rmtX8CqewR<0aRc%=ev(ZIei-*US@xQ7HwvroGEx>9T*av zBpSz6Ps}X0$6pFjOV{a2&IRerk&x6 zEdc??<#!$XvI9nB{~$P~A<{#NXFO?GjY)aWjkP-@DM6>~qMwZB@844=E4|G}IPicd zS;NmTxZU%u1{8@C%uHB4u8fi|aTh4=of#Y&x~i;ftM6oCY`g$@T0~eFd{)*^=#)CT zx){MIlyc%ND=TXVJ2Cg`*B?8f)fDLXv%|Id7Cnb(X!2ba%psEGYU%3frMHd?2i~9^ zRkR`_C-<1j?Sxr;_UPr+EzWoDU`sYtRh>&}1?YAML?PM~q&8-Q9y}n4x$?C=p*Rvu z<-Ie_0O*+Sgg-BxhrG8RvVqX;uY8y=FJBwsB?P~Tjg@988kXp+s2D;k>i#Qy2LVD_ zN@}W3xkqVqG*hh&Z^^IwIQpcu9wTA+Nk~|Sy%jz(3@)$znRO}cudMubHtaCbFN2A2 z;!~z(lm9cRzX|u)+}zxZEo-^cPOoFobRlU&<1nB#+1yyix_85ME6>Ipo1&iK0e2`A zW1=G#zJY*%MqR-g8y(CUK^TB#ZXClbS~w*lAIB054i3Vc2Jr$1ymf+z)4RI67{q1ky~$0-9obW4@P>k&(C| z)6y`bfEnb(gCH2e85kAa1eJqVhz`0J0{+7VLmZLG1w`wuZ)%RR?fDntV z&$_=3`U&X1BDZB*tRCdP8E^;a^kJibmJWIxN(vbTg=e$xzR@9ry(-auufumf34Ndu z=xEv+#;^SPSWIyy+M(=hW`Y+)th#O6w@XMcHCJa4f;l)i{5GX(Z9x6GUE0vWW$M~C zmZD1{&S;&`g2~OOC~khLXGwj)%))|!j?N|ea_0_iZf<-AOiIgA5Oy4=dzZm9Ew8S+ z-nrvo(KUVFxC==Ga$^#P7gRy^!8PV4&K3&BajBqT>ssN``|?uJ3=I_b8w z)Kon@DpaWE=4RM?N?n%%=>*JS23S*^`Fx$v4I|u+j*h%#f4<=Z6dI6ag##lcTm?P9 z!F~mnq&PVlf25BA!tu)dIBd5vI2i%MG~pN?@lD3Ez@jnrR=++DZja048n1T3f#x*M$=mW-&OEHpj=t zF34b&@f3MaoY+S}A%z46y1-{2{RcjIYiny_`5LaJN|elE%;R&oOR;ZlIU+=>ZT>vw zYJIP`yRUty`<$RiTq%O+3C&AiP4u!(jpyg8vRI?0{CqKu1R&b~DH+IDN|( zI<#SwJap&~F?oZ2LOSE8Q;?PY09Px{mKp#4eI8uhuW&`L3Tsp!1Pn$<0vRP-Ftm

yg4};Cy_kozo~3y#!+kIOTom$%c~?W?*`%z1Eu_M?ZJc&dBpBWXIoniio>T* z@^LM~NG8Ws676pnAtt~K`{8H-6%~WCXDeX!MLHgznrdWfgJy~|AKrr=9{c|NC!(e9 zDJ!-D#;-;jBaxjxH#HGYbU+2HB^NY@f*()q0BosIdKRg>~+!I>Wo|W*C0O2?jZ~w>5-ICbr?Q62;DEwbei;_l0R{v{$fQ>;AUK1JyKsI3-iPt6?yq0P zVYgoM(qc!s0&9x=D2=m>U_1x2r4V`?0x>3wp?@`-@kf2$ggpVAh$N<=l2yrR`37N4 zvWMl>JeOF&nVFeUgD8#&zgA0um`#o8Ft{v%6QEMVkITx!;#Xr>sN;dbeiS9*0L1oP za4fA)D{!2VITevwQi3_t-WAQ<{QPKan7@JmavD6otg(1lO;KV(e0>rw)`9MfZiHDP1q(s1kjsW zSfsU%gW=kLq{ZsfE170w-|JtR+eQyOvE^bMRAO_)&|don429PF}uPD zAOSoFQV0l$I86ZZd69VQO3@wWxybPUY`HKV0+t8l6vO6ss;f97N-^oOqGDM%ZK$6N zp#{VmjQ7Y&qIqs=s;aOIzUZEUYwQn;1oAt^klXHDI7&&fi@3Fad0mD3jj5w@dsSAd zR$AHAAXVTn5FM%BPydgF+l~_HMmCOE?4n^!DDlL50bB1KKpTbHA5v!igkIz>du?fje#*u>B?D5 z$@BIFcON^^R#HyLH!Z}}3$CnjCVMozKbh>8xEOopg3Am+U_<2L)j@T5%!$G}3yTq; zs1zgvED3-?Gej|@X}BOrcI*Jc$S5jW0Sp=wwuEp{esh?+p`*PWlRy|f0Mnh9n+wGm z2Srs53`7qX-~);7IqcpeIC1CvdGF(=8$-F&HNqGK%vE8!#CaXq?CeUfK9$=eePMtU zX#>>@Gvn=+3chJKg|KQEO{9@eDPvy_n!(qNf+L>KD(|a))S-Xh_v5{DL+L zmcZoXx_O?By(4o2MkXfd`sI?xju}9Q!JM!@tasRWvT@@%ao}RXYL;;hO+xB{p>$)p z$rJQ)8GINxK>*dRbn1->;hQi9XkqUn96kEJz5QBT%lFYyWSF}KwD8@*8@C2KI5hmF z-@jqbT0sevPdba3xWa)(kh9hXIq?c-C^E%}(jOS!Fk~x%Q7p9d=u{pC1UT5*zQA;% zn%bewVN!DP{%li>e1690*0BSmz^5lQe&~rWe!YP>!jU_~W1SdiY0b1gkEs*w?0|EK z4_iAsSlv}Q(~^^iqIV$Cz{#$Qt%DHEgHDgrFfc2P2?$gm_z>Vmm~2n!gpUd?a^gGy zTu%@b5!4u`g!E4|y42Mj7ZOrWR(&KBi)I%u{WGNS0(tFC`vmHJ;K;N_ zT1Cc;y@S*T&)*~Sf+b5ee!l%1k{w9D8yyO~Q?qn4(Q?L3ULlk3s9M9}#e(L83qkvJ zauQCwrJ3DEWXtG*S?lZTqspu~>bIO~QSOV|-i?GTXAz-qXZga{V5H7HZEbVf&LRQ= z48c_#l!n|{IXMOS`C@IU!BuDQ{7?lTbfddMMMc#T2Z6b2N|U9i8gGQ+WXz-P(ce}M z*La*bk5RrJo2wYw#bfVJIg_74+LBgY9a(yB$iZ{QQO3~yf$%1lxc1;xHXSWN8Mnv_ z;^g<77I#uQ4~V-{Nn{0B)z(GGoj-odB!LPy#P-d#f52uzD+qUAc!N{-W}nlrd#^a$3{xZo8^qUmv-18tg;RlKWSM@6+En}Sv4Wknh7{=Kx7YP^hPUA`!xK{m!)k0{oG z%t0a#asdPJ$Xq^=sZW&3DCQ9ejlq>+0zN zM1t2~ZU2n(12*Lx1Hw-cpQ{iE!iyK|qLy#KXJ+EyfR&iBfq^@YJDm6+O^eaC&jE!vg~c+`j8)>UZT@l*0uMD;;+VC^6g@!3HdIl2%kyME@H) zyg{%1E>7{pC#Lb(PfM<|G98-oPk4~Zj0zMWIfEkn5S6tAY%FT6#0O0uXy6K7@ALcjS=Q>$z{f&Wa|VQR@@?QOcu?iPyBrDOU`2&Y{3m2uSYIeL zAbkQ27Ah93bGs8Oz3_;{iUqw;0a893m+r{$z0YYtWx*lK7o|?*wTVhtPG}%cfQb?K zI4y0Dpr8Z#&tzv~R7zmh!k;UiP5-KQ)H{SvoL0`z){C=&A&Z-aR0<*HZ9d%E9Q|oo zXMj@lX0DsnMi&ja*-OBE$kSobgd#c*7Aye^stb6``bj6iK?j2jwMlhdU0?v5LV#~U zrZwxig2c28G)4$7QSYRr50(W51Q>ybjqDyCMa#2ek?=&AXNKe(ap$6k2U>>ZPHZao zZE*`Bsgi|>kao@r{5?N?`ZQYwCmwk06qpD{*TWyACT|+$A&76e9Xy&o;%>{LZn{!c zp5aDnKMTjN@$I<#K!PDWTSA`esBuB7yGE!{;N9w5y0$y#o~53wDCbP~lZs4~-^{|z zU69Zxzg9!P&X*FNBA|q>+CxZUz{`k?ru1}TRMej6`bFi-%BY^40Tqg}T%gk<@EYHh zrPx)#-#D|$Z7VyNn2-S?fr%`CCPr&r@|O7Pq#q2xdU|@0=4nm_-MKR)QlymB*wz;1 z{O|{M4b)KP87;!6U&oZ+Gdy()0NI7^mlO3O<-Cx1XsD|z9X+~OR8>_iHNvAHF5TA&DsclK~Y~{KN(?^j{t)V1s|0> z0>#Jc(o*Mcl@O~KEHm&U@Oh9Gh3ME+QBnS@$4ej$-LA4I)=)bW!ibw6u)v)^KTAqV zs_5t_gz6TF5dcQTgAiXOCR)^lh)YO76cKgr9+-bn0Kv;_brg!gh3BmRAmR-TiMa0L~-aG^D@? zgg-x@f;A@KZooVp7NmF!3uL7bC=RYy@qXo_rluCgDZh~Wpug7q(;BE!p@V>jt1R4D z{ib1%0$jmA4NiLSXqw8nugr~oUA&yT`RDD!y8=Q&!1IAS%{$yAR5z!+CM5Rm%|Le^ zg=~ueV0M_eE(WU-FCQ)j13*n=pg6qHhIgjZTl_HPz4Q?I?9|+I03OIHK;5O@ym@yr z>NG+uj_uB|Ow&W->^uoEGYQoc;0suUJ`}Z5I(d?Tq(cJA$?CTV5{}XRkl^44QZ8q# ztUz|US7(El80zl>Pyp-}ny1^hZn#nfmp4zGiUw+=S(16;S1V*1abxomT z-*Q~@9x}-=iq0zY3b^Tasy1D)*_|pTZO*@TwdctL3A&lRvI4BeC7qfXlXvUem6XFa z+?}7`To5^>tNL+rmFX30pzv(kvv!hTc*=wg-}?bOW%1+oqPJy4H>F)m&m15@WDZ1O+ui)Zo{UP_+z z)h@Q+c!9$W$JXVkFGskzxUgp6#O(Xk7=Hn1Ktd`*fYZN!`{Ex9{xh+rJ}N)^~<%_#yv^*?#&>n?|pgTw;fj4h|`#wVL(9LP86EnHEGF zYxs#57bBns_(9YJC#dm$_1ds4{P3<(7VSsrgE}_W)(5d2(JjJ0Um;2@DANX-7p0K6mu5w-(nJxf& z=o%IJWx_lO5g8~OInkThr7XU`qQa^VHNo&l9`4XV1?1saHQI%Hsi^!>V3e+{E>quL zXZ4-Z(%O0(EQ(Nfp#I^6g%LmhC}@E505KXKN*`55N((79l5`tmnCj@~b`S=bpo^H~ z+Z?}gWg(|yE2uF<<`x6|9VtB^Ca3e~;RIP@C59RXSF=fv%eZ$rSTcWh^0|$0dd%8O z;duUZ=JJeugZzR=g{qCee89-$&N6J29m#x_`D+KS659^7j}9{Tbh!WdIC|8lXUquF zr)xp4RRVn=?xqiVi1Rip4yi($D=$ZB^I(&R-Y3DsjX6 zN9OV428!^Yz*kqvRja)PP|n4{=EZoxZc^& zGomc}&58~ygBmRs`=PE5iuYo31wtJlNC$LmfsumbWnAO`9MK4zv)eSxe0%pk z_!@~1Z&u=J4<-dtC(qJ^+`&g{e*4MHCTK!|l8TJX|JJQdx8H*No|ciZmQD{}En2|! zK)dASVGuEahzhkkSJ0PPj4Wqt%@giKgoE;p*%wb-Z1}cAqu{-Ax zB>j{dY5Di>AC~%5T|N9VV=Dt}Fk6Pwl=T6DefiRuc?obAQf*LR-QYE`y1JSk0tOpY z2SGv9rKcMbOTD{0%+HFJFtgWu8yO)`q4vCVwvhBkkFGh_Ps~Q>X2SKdyx{o(G6N7g zSaN_?HZwJ~Y11Zt0fB)?Jv;F@6tTFA*Z%w=qSwfXh#K~&H*em+wi15o9xrtT zqu@0K1Qi)sXJ>J8@(t3$fE4=gt+4g{kP&cmIm3%Sv-P%^jMO-8P%(*y^hhW*byy*Qmq`l(7a>(n}n zlOL$Wuk7@>5?KA?5yGCa%JIa;YhjOzDy?5X-%~0d@le?QYjmsgKDvc~E#F#te8mNa zMz+g2jg6lVTD$9aVejqxXXkeBJD^3IVMFp9TwmpV*oCXPqCxJv{P|z}I^)!b%t^pE%_^Czy308S>>UhASI_L?|l{ zchZU4z556~G?I}u0a($toSZt~09B!4{yY#pWRUT{-!{ZfSJEqri;Hg!Kb@$4M}?m83_3y3?YFh*bzuR^i5ng!l@7goMtc>P49k^MvcyuLJ!@bxt<4FfjOy zMn#;qp~6v+oncpmKE~kXp&lNjMX>3~PznqUg^hi>OU^~+#l^*RXSCE{H-ZWa)iBzg zz>*)zd)+U8`QqivnGyOhs~BYWsF+{BmRns9pFG?#4^~DYv^{892$uHZjIHkj9Plta zsVQwMLB+6HfOr6AWc*lZE8%<6(bhIyXJ~w1-s9&(eL*^EY6p9JX;1~6 zs9yF!voeibBw3IN~>|KZ(m0}O~VYiS0&6KubUW{sEI z=dn-}vzps&aM@@tqo)qYgUe&8-PBn{Rkdt&X&k7r(}fG5TTh{@i3C9=L5{r!Hq}$- zw9)iCyi3aKw@@PMI2IrPEgUnj>4il;+BejQ>JB=(86HDN6W5rhZCe?l z3U#lJJH51IoYcCm{bGRJFkblB-jOYYE@{29%$1t~kBeg6Z6n&%_6WNc#36g_z0YKF zCLpZmDn|L>zz#j=1cGeV5-nBCofO8 z@upHz4ACr>LW6QMEiKVaURhaLl-)S;U**;P=FYtPGbK|c|3 zasIP715!;~1@X<#d6l^A90S6>0}&c>l0^I(Fi$uM00?qvS%S|c$ht=5zVd6?NQ$hC z2_qAM_KvJ_u3xz!;8#f;I!KEqZ_V z%a<`U2bBg=ZGZ@9TOwN?ZxzN6OhQHt??mzADJ_Vi0~Ng>?B;Hz8s5d?dZrg zEezFJPCdbo+j$nsrw=mo-AK6IC;L8TaH9DPjC z5@8dI9GM=TB|a1x{#t8ri3(EY4ph#!tXKzkUOy`U{X|6#gY701OKG}V52f<--l|47}*Yj)SO!Z`Pwj*S$gO_nf%jkHHIqdJa^yUF`E&P*rIyEWW&cUovU;Q(9EFBRe>hp&S2|*>vG2E8BL>W7NK<6>1aLag27B&aTjPiHE9gXxe7s!TD+F zCyOUvkY}mRoH}>%L}X^--jL2CDL2Q~{F?4K`~EzrseIafcEVLKs)bB>Pb^wJ&i)6p zV)<;Mz^u_Ox^C?`brac#38BeZ?p={uiRNkfpR_~@rgGqWFKW2_t;@!{bESR&)&a14FZ;Q5w52u7@> zpa%i(w6l9$UcLsH23vFCfC@59fZm{5@G|z6vY3O6Gy!x@Fe)ej5ENiIRB*B(g}@(i z72XcXuo1`N)a$*dp-~TLVCd3Sm_G#vqhGBJ2%2Ql-q8VN_>`nI;94L<*w#fTr)>-HH|~!4=rEXIk2Mp_mI=K{TIb6sVsGffWFZxH+>>xP~z5 z!udqLQ-o)MvkeUq%;#Bqitap2s?GLeyP%;=7Rto8VK_otY4-X`Q*UoPmtnO_58!=F zGSCd$;JwGg&dv_G7$!`e&Yhp14+{!R3ObiT3Z4Ovgz>q8F>m=Di@L-W^kws*rq>O}iKy8Pt2u9$mnen!r@U{g1gG3^& zHh{4)-jRzrB)Ck4mWU?1AyR};{fuFbY56PEV_=;@-Z)*_4$E2~GK+DWLI;7~Ug#;Z z`ZG9yfF+swaYg9oj%a+6>_kd2Q@#b}zVY!;^^Xk<;OwlUzm08-qs`sL<-pu7qJqrL z9TD-N|FQFG?bK!2ehjoKE;i;4Uszs#5TVZyUSV1L{ynIM7>|*jjjVJLW-(yVR(qs_ zt`Hg$QtIaJs6c-?Lwgt?DzKqp=ToR@@!%N)Q-RFFSpe2?eWed_>tE?n;HK#5>)X7a zs?iCF1O;YCFzR81DNV@&w2xQ&&@nFaCwVx^DaX|Drs_`OZa&d0<&jO8QmL@8V=H|I9Q0aKs(wk)iD$lyYb~adKO6 z=iTUuhDGc%I&n8iTQ{}1-iF963K}6<7;`%b#X`4XB1M-AGPDH|Ym7C;I_3iewyi9l14x3QA zupjs!(oFMKEHDeK-)qLTwN&4;McA3l@{MRD5&>JG9Qr^^QaP{^_0QW}8pSJT}8%RnVP zjZ#0=l{Y0Tn+p#HFI{QeCNEGL^wzBQqo2$$lVeA7lta+Pr0?!suHpI|m);kdw7k10 z=(Jd+zduBMX;{XIRmZw=G}2YZP+cRkwk_ys+X*fYMjHEpBZ5x5H4c=@1nf6x3@5LZ z&Y#&ZQI{_5W8cjyW3F_eT|9%!wym+l*kj+?+f}B%o6Y;`YEM37-Y%iHDDS$of#R3G zv2n3Oo1pJmwph(WK)VQSd0)mX1;wcQEv4Ozw(<}i9nZ+5qTsxCfh+w`=4cz&SZT0+ zueR^+7Y?7-mMBKrTgGd~l%$J|!mAHUlBDmnRNm+1meFs#DrDy%ZQF73*|s*p(WsOc z7b)oIlC6&Lb~KWnGBEg%Zk{Q%d#0#T(p;>YrFSCsA+0KRvc$Q^skhJ0rk8Olp4;6Q zq3)*6ctD>olH=5F_9^ zp@V>Kq1yZC?z*2s&H?=(h>Gbn8P-$uD$R2~@XQXe9{aYCxJG&%bJq8$L}B8&pA0wh z{tjNXG~gW0%Pd^VyZb{30+&oz*VJn!66WT+{^}DJguykO^Ix}=^eCI=U$T~RW=5~S z-@V5ApYt!z{^$J57>0k(zqC|^(8QJ{Ka$afP^<*=P}%=i#X-77_%%kKQMG^8we=t0 z^{8Ui+&tjvW#ov6Kk)QW;axhKQKFgx?w7rtouzSmUtb>%;HIWBhS7tCaL&XD4!1jGpygvB z2rNjNn*fpMYHFI+oH$}`>m~GF0(7%!87XQPYv4TC22dWm9|_q?yQl;v z{Q$H;0_PpGR72R#faQzOMI_EHF1xu4OyZ$Orh0Jh&z=6FT~d?sGG{|6%Q2B^H#g}o z*O8=P#6xZeN@+|FLO!Cv2fr~G4d-@X4pOY9yW{0F^)#fy9S#L3PS;(31D=w_r~D@< zNE`}K&;lWBHIBA520N+E0ksCo-@}bpAG?E>2R;URvSQ~xvFr69XXq_-wpd+buNfm; z*DCYRXM{Y6`3-j(8?ji}!$@hT=}wbocAm`CC$i#=v6_5<$9I?tMsf3Q#Lc;OjGB{F z@dHQ#9CrKcE?hVbQI@}d4UWE)loU|n)_x_fnU#8;0%yFdYYS``V8Aq3DVK0McMZfN z(1tPD>)eMJ21dpi*k?V!>@Z+^MDZT7QxfM7;Yarm<;JZDYzxq=!~Fs60UI!{z>Gp0 z*{x?+jSFbCuysvD^%&oEw*LKln0J_&n4sH*okFzoFgnupjy~PSO3?m*K6WEn2I9}m zJU>$}0@@W}orc9gAUlY>fBn_asD^*1qU&fQ3bSATT!vQ!u85GNgH^UXd-9J2CuaAp zOiOow7SBMjB^rf)NS@8W@le8|UPV8IuopV-RoE6c4-ZU7G<|uV6^zjA?0st(6NU#0+7`Hufb|yt zP!ua5nLu_FXa@oU!SBOh1x-F#9M@?4FWf%`?h_hQu!wXz_#T|O}Y8mh9 zyD&h7j1U}Dc-bP=qXl;sj4^B!RHjfd_1f z&4z_fRP+RQIHou1#dz*T`HvKtyoOrx(qJx}c81;`=uSgTZ3?!e&d%VcUIOI_#X5u^@C0bG`%&a^-xga5_%STW%rN%^ z?qm!M6)x-y#OOU99>Tf#q1@F=7(!Jkic=584fX^oAyn+3}Ly`cEv?pV$lfwpU?PZ_jS?jCI6s%MOgOKh?HY^ zDn@3gr8V~#9tl)NSD9h!)~#GzqzpGqx}~P>DkrU>{>ORi<4E0)Qyv^BaGTK{MG^_L z!*p)il&tV01|*4sN8eSip4NIX4MD<;DWaX*aJAU)58miyG0Qe z>g5o&?#df9IZ*+sr*U)pSwWTqzZOt^oJ3gy)RInMB9V&D=@^AFS zAdeuL9mQs!ri$K5Q&7-sJ+6F2U&J142{b}qn4s!JMF)4*$hf$yma&D~)VGy#JGO;m zNef0{2>?{cZo`x$pa~vbN}wzl34s6rdJ%wUJl~;cNAWnq>J%RsUO8AXaOB!WvI9&B zq!DCwj1mh*#$U6;nz)v0{+mtoD%d-_x;Adyh{f+u@JOMxvb6jH9RT|8DQHS~c+3NR zh5s%JyU<@XQ^3E_qe0hn>Gy=JFL^=M$wFv{PQ1(<9K;EOR)`*80tpjORLU5aBJ=dD zmDMybb0oX~OS;k3sIS*ZX~J^{c?w4>dZFXvV6Sz-vI!*wrW-qIe*vnCN(9hOS7jxV zfWXAhezu)pmceu3Q|!SS1Z152K&ia2{46ao@dZ<=5K`u_i8YTp{E48P0&hDEpA=2d zu-6Dg1%=vc|HFyd#;|iZu8ck@rfRmg9|DAS=#W9C{>#EbjOc^Wb#TK{cOYOf)aDu* z7`clr2>A)lRgkB=qs{Nz?qNI*_6mwFoLhftQS{WU!JkL~RD>pusn>6iWk3v@9q-WL zi^R|v99JL_m%}U`2aZ_md9baNl1?O@0AcG22+mlWI9qfxTP;Nwz&u@8fN3)iKGXha zn%i#?LpE&Oi1`G|3k$eS1osss8aOw_#d$(7heh4Y;s?GYaL(96PH`C-Ba{n||6F{D zvZG^mT3VaapcbNi?{9gyXO}%~Z)w?>pwoax*+k2w#rQ@Dks=Vgo&- zC=HCKab0AlC*@wr9K`=$^QVB2^!Y9fi3W?VEsgj;ic!x|qkoB}|NHFm-_z6o$29Z* zg&$O&_o71bmZsY~S^DSlDc;-!vNJF=nm)nJod%K8?x zy!{xP#9n&e--B}{%P+3}z4#*L-d&ofT6ZMKB=)slXnbM7-t8fpd&Em4HLF2gBm06u zzuQ5xhBHio0t3xm&VvdG-E|zrVa10HwBxkufZNm1Ots9Mp=s&I7W;iS`6}pQ6;# z^sz5Lzuv7)qO?xisB*eJ>P9nLRQN-=qhFU)q}5)Gjf9!!*jaX!iR937N=r4Vv?g4Y zE`64KvPOMf@|ophOVqDMNB_Rhqm&DcWAmgtSrM;;IFBeaZ%Nzn-fSZ+=a}snUFg^^;gqR@x`Ki32u6 zMd6#TZ(t6K5(qK(_^alom{RQJ)3lundZ=;kGF{S9&sXl}7j4!n#YTk~@kbbU8r2KE z5Iz-qN?b^wx}~bAqBF(gtEK%!2wQQx(plliO}1kDcJ7Nk@y6(t-|xUp_ntWv9I>g- z5;UDPceng|6^%zEM`tyZJx|paw496kv7)+V++O&#sa;M7zlyG`<8(vK9wyUn)1Z4M z^5LnKCg;6u`|4f@C4H#S7ChhKz#8>4df(p{I@~e3(JJeRG3od0hWf?@hU-t2hPUin znU`A6ecdFjmCZO-O-)%STkpGc<}9;+-FN*FUESVod{v9GcgwU4PxZK4(EoiQil4EI z1crL)X+b?XMQ&o&1VvDjotsd%y*gWLNaT?u{ju4NneqPqX=I0!QR*oz_N@gfo$8Jv zhC2nq8=LRnV~aA#kjz*8^oZe^XS{ZYzPVXnYqP|$KUp9JMWN_&yMAbneNKJ(=^iup zxsg4;IV(ALes$`|3wz2d)pPLMEPaT4%EtMLDDvkwdKn_@?-@mvuD^NfUVFFV_YNiQ znaz`KD{ljj&P)y6QYc+kP0*CqJu@;obav3j{^ol1zgKbg20n73PlcU$dq*3lPX<+e z1&*)%!r``h%kHZ16R0Zo>>wvEi#WDsGYM*+x7rg)&GP9{+v7G99-e#eh0-Dt5AJt) zawJFhyEK?7X?O2{rF4!rHfm4CsppLQG!CSk6p+r-VbGLzmL;edC*MopF?XwYFLnAY^l>@U=fffKZOs_$J{TC*j4lpz{l)wEOKO z)vJF|gD>+3*Zu`LP)u$5A7F$3ljr$bxx8$<(@wv)qJm=g9~}~nm?ynA&>k?!&!jbX za(ujve67Lw>hYdA%6}*}aT8;Z{;&R;AD%g0;@dz>f07GaPP=+gPeWkt8!R}zD~J^$ zm-}nc%uzVaOujTa)2CmjB~>3+bmd-qm$=re-ly&3M@&`|hE1a}xMOy?LzH%T#h1m; zG>rzo`k>v-{5}jU*~4lI=jUC>E^a?gr@Na=HvCEq3$u+~$wUB}N_5EaQ6zi&aMVn< ze^AA@J4_sNavZ&8J&8Au*8Q&Nan}7o?s)~`p@$_a{%>yQ%;pU?G9NCgUs=X$P#jm$ LR(`5vdE-9-d+J`m literal 0 HcmV?d00001 From 6d03d67741dc47b58dcce9a83eeed6e41899aabf Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 23 Jul 2020 14:54:57 +0300 Subject: [PATCH 48/77] feat: create RootThemeBasicModule and import NgxValidateCoreModule to it resolves #4858 --- .../theme-basic/src/lib/theme-basic.module.ts | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts b/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts index f599124e83..bfacbcf9a7 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts @@ -42,6 +42,20 @@ export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, Empt NgbCollapseModule, NgbDropdownModule, NgxValidateCoreModule, + ], + entryComponents: [...LAYOUTS, ValidationErrorComponent, CurrentUserComponent, LanguagesComponent], +}) +export class ThemeBasicModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: RootThemeBasicModule, + providers: [BASIC_THEME_NAV_ITEM_PROVIDERS, BASIC_THEME_STYLES_PROVIDERS], + }; + } +} + +@NgModule({ + imports: [ NgxValidateCoreModule.forRoot({ targetSelector: '.form-group', blueprints: { @@ -63,13 +77,5 @@ export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, Empt errorTemplate: ValidationErrorComponent, }), ], - entryComponents: [...LAYOUTS, ValidationErrorComponent, CurrentUserComponent, LanguagesComponent], }) -export class ThemeBasicModule { - static forRoot(): ModuleWithProviders { - return { - ngModule: ThemeBasicModule, - providers: [BASIC_THEME_NAV_ITEM_PROVIDERS, BASIC_THEME_STYLES_PROVIDERS], - }; - } -} +export class RootThemeBasicModule {} From 3cd678d16e4448ad2a9f1b7ede93f1dd79647671 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Thu, 23 Jul 2020 20:37:28 +0800 Subject: [PATCH 49/77] Update Blob-Storing-Aliyun.md --- docs/en/Blob-Storing-Aliyun.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/en/Blob-Storing-Aliyun.md b/docs/en/Blob-Storing-Aliyun.md index 6621e2dbd2..4aab8e41b9 100644 --- a/docs/en/Blob-Storing-Aliyun.md +++ b/docs/en/Blob-Storing-Aliyun.md @@ -45,17 +45,19 @@ Configure(options => * **AccessKeyId** ([NotNull]string): AccessKey is the key to access the Alibaba Cloud API. It has full permissions for the account. Please keep it safe! Recommend to follow [Alibaba Cloud security best practicess](https://help.aliyun.com/document_detail/102600.html),Use RAM sub-user AccessKey to call API. * **AccessKeySecret** ([NotNull]string): Same as above. -* **Endpoint** ([NotNull]string): Endpoint is the external domain name of OSS. See the [document](https://help.aliyun.com/document_detail/31837.html) for details. +* **Endpoint** ([NotNull]string): Endpoint is the external domain name of OSS. See the [document](https://help.aliyun.com/document_detail/31837.html) for details. +* **UseSecurityTokenService** (bool): Use [STS temporary credentials](https://help.aliyun.com/document_detail/100624.html) to access OSS services,default: `false`. * **RegionId** (string): Access address of STS service. See the [document](https://help.aliyun.com/document_detail/66053.html) for details. * **RoleArn** ([NotNull]string): STS required role ARN. See the [document](https://help.aliyun.com/document_detail/100624.html) for details. * **RoleSessionName** ([NotNull]string): Used to identify the temporary access credentials, it is recommended to use different application users to distinguish. * **Policy** (string): Additional permission restrictions. See the [document](https://help.aliyun.com/document_detail/100680.html) for details. -* **DurationSeconds** (int): Validity period(s) of a temporary access certificate,minimum is 900 and the maximum is 3600. **note**: Using subaccounts operated OSS,if the value is 0. +* **DurationSeconds** (int): Validity period(s) of a temporary access certificate,minimum is 900 and the maximum is 3600. * **ContainerName** (string): You can specify the container name in Aliyun. If this is not specified, it uses the name of the BLOB container defined with the `BlogContainerName` attribute (see the [BLOB storing document](Blob-Storing.md)). Please note that Aliyun has some **rules for naming containers**. A container name must be a valid DNS name, conforming to the [following naming rules](https://help.aliyun.com/knowledge_detail/39668.html): * Container names must start or end with a letter or number, and can contain only letters, numbers, and the dash (-) character. * Container names Must start and end with lowercase letters and numbers. * Container names must be from **3** through **63** characters long. * **CreateContainerIfNotExists** (bool): Default value is `false`, If a container does not exist in Aliyun, `AliyunBlobProvider` will try to create it. +* **TemporaryCredentialsCacheKey** (bool): the cache key of STS credentials. ## Aliyun Blob Name Calculator @@ -70,4 +72,4 @@ Aliyun Blob Provider organizes BLOB name and implements some conventions. The fu * `AliyunBlobProvider` is the main service that implements the Aliyun BLOB storage provider, if you want to override/replace it via [dependency injection](Dependency-Injection.md) (don't replace `IBlobProvider` interface, but replace `AliyunBlobProvider` class). * `IAliyunBlobNameCalculator` is used to calculate the full BLOB name (that is explained above). It is implemented by the `DefaultAliyunBlobNameCalculator` by default. -* `IOssClientFactory` is used create OSS client. It is implemented by the `DefaultOssClientFactory` by default. You can override/replace it,if you want customize. \ No newline at end of file +* `IOssClientFactory` is used create OSS client. It is implemented by the `DefaultOssClientFactory` by default. You can override/replace it,if you want customize. From f06258e623835b560f0326941d0a6a89f1d595dc Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Thu, 23 Jul 2020 20:38:52 +0800 Subject: [PATCH 50/77] Update Blob-Storing-Aliyun.md --- docs/en/Blob-Storing-Aliyun.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Blob-Storing-Aliyun.md b/docs/en/Blob-Storing-Aliyun.md index 4aab8e41b9..602d666787 100644 --- a/docs/en/Blob-Storing-Aliyun.md +++ b/docs/en/Blob-Storing-Aliyun.md @@ -57,7 +57,7 @@ Configure(options => * Container names Must start and end with lowercase letters and numbers. * Container names must be from **3** through **63** characters long. * **CreateContainerIfNotExists** (bool): Default value is `false`, If a container does not exist in Aliyun, `AliyunBlobProvider` will try to create it. -* **TemporaryCredentialsCacheKey** (bool): the cache key of STS credentials. +* **TemporaryCredentialsCacheKey** (bool): The cache key of STS credentials. ## Aliyun Blob Name Calculator From 79f11ee7637d903e0aed36b755042c46a14a65d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87otur?= Date: Thu, 23 Jul 2020 15:53:32 +0300 Subject: [PATCH 51/77] Added entity extension base to Identity module --- .../AbpIdentityApplicationContractsModule.cs | 23 +++ .../IdentityRoleCreateOrUpdateDtoBase.cs | 5 + .../IdentityUserCreateOrUpdateDtoBase.cs | 5 + .../AbpIdentityWebAutoMapperProfile.cs | 8 +- .../AbpIdentityWebModule.cs | 21 +++ .../Pages/Identity/Roles/CreateModal.cshtml | 26 ++- .../Identity/Roles/CreateModal.cshtml.cs | 5 +- .../Pages/Identity/Roles/EditModal.cshtml | 28 +++- .../Pages/Identity/Roles/EditModal.cshtml.cs | 3 +- .../Pages/Identity/Roles/Index.cshtml | 9 +- .../Pages/Identity/Roles/index.js | 155 ++++++++++-------- .../Pages/Identity/Users/CreateModal.cshtml | 24 ++- .../Identity/Users/CreateModal.cshtml.cs | 3 +- .../Pages/Identity/Users/EditModal.cshtml | 25 ++- .../Pages/Identity/Users/EditModal.cshtml.cs | 3 +- .../Pages/Identity/Users/Index.cshtml | 11 +- .../Pages/Identity/Users/index.js | 143 +++++++++------- .../Pages/TenantManagement/Tenants/Index.js | 128 ++++++++------- 18 files changed, 409 insertions(+), 216 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/AbpIdentityApplicationContractsModule.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/AbpIdentityApplicationContractsModule.cs index 107acba9d1..43481ff30a 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/AbpIdentityApplicationContractsModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/AbpIdentityApplicationContractsModule.cs @@ -1,6 +1,8 @@ using Volo.Abp.Application; using Volo.Abp.Authorization; using Volo.Abp.Modularity; +using Volo.Abp.ObjectExtending; +using Volo.Abp.ObjectExtending.Modularity; using Volo.Abp.PermissionManagement; using Volo.Abp.Users; @@ -19,5 +21,26 @@ namespace Volo.Abp.Identity { } + + public override void PostConfigureServices(ServiceConfigurationContext context) + { + ModuleExtensionConfigurationHelper + .ApplyEntityConfigurationToApi( + IdentityModuleExtensionConsts.ModuleName, + IdentityModuleExtensionConsts.EntityNames.Role, + getApiTypes: new[] { typeof(IdentityRoleDto) }, + createApiTypes: new[] { typeof(IdentityRoleCreateDto) }, + updateApiTypes: new[] { typeof(IdentityRoleUpdateDto) } + ); + + ModuleExtensionConfigurationHelper + .ApplyEntityConfigurationToApi( + IdentityModuleExtensionConsts.ModuleName, + IdentityModuleExtensionConsts.EntityNames.User, + getApiTypes: new[] { typeof(IdentityUserDto) }, + createApiTypes: new[] { typeof(IdentityUserCreateDto) }, + updateApiTypes: new[] { typeof(IdentityUserUpdateDto) } + ); + } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleCreateOrUpdateDtoBase.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleCreateOrUpdateDtoBase.cs index 491e82d766..6b68c43568 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleCreateOrUpdateDtoBase.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleCreateOrUpdateDtoBase.cs @@ -13,5 +13,10 @@ namespace Volo.Abp.Identity public bool IsDefault { get; set; } public bool IsPublic { get; set; } + + protected IdentityRoleCreateOrUpdateDtoBase() : base(false) + { + + } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs index e06ea8b987..41981b2ca8 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs @@ -32,5 +32,10 @@ namespace Volo.Abp.Identity [CanBeNull] public string[] RoleNames { get; set; } + + protected IdentityUserCreateOrUpdateDtoBase() : base(false) + { + + } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebAutoMapperProfile.cs b/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebAutoMapperProfile.cs index b0154f543f..f7dfc69099 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebAutoMapperProfile.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebAutoMapperProfile.cs @@ -22,7 +22,7 @@ namespace Volo.Abp.Identity.Web //CreateModal CreateMap() - .Ignore(x => x.ExtraProperties) + .MapExtraProperties() .ForMember(dest => dest.RoleNames, opt => opt.Ignore()); CreateMap() @@ -30,7 +30,7 @@ namespace Volo.Abp.Identity.Web //EditModal CreateMap() - .Ignore(x => x.ExtraProperties) + .MapExtraProperties() .ForMember(dest => dest.RoleNames, opt => opt.Ignore()); CreateMap() @@ -44,11 +44,11 @@ namespace Volo.Abp.Identity.Web //CreateModal CreateMap() - .Ignore(x => x.ExtraProperties); + .MapExtraProperties(); //EditModal CreateMap() - .Ignore(x => x.ExtraProperties); + .MapExtraProperties(); } } } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebModule.cs b/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebModule.cs index 79b636418c..23a4529502 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebModule.cs @@ -7,6 +7,8 @@ using Volo.Abp.AutoMapper; using Volo.Abp.Identity.Localization; using Volo.Abp.Identity.Web.Navigation; using Volo.Abp.Modularity; +using Volo.Abp.ObjectExtending; +using Volo.Abp.ObjectExtending.Modularity; using Volo.Abp.PermissionManagement.Web; using Volo.Abp.UI.Navigation; using Volo.Abp.VirtualFileSystem; @@ -62,5 +64,24 @@ namespace Volo.Abp.Identity.Web options.Conventions.AuthorizePage("/Identity/Roles/EditModal", IdentityPermissions.Roles.Update); }); } + + public override void PostConfigureServices(ServiceConfigurationContext context) + { + ModuleExtensionConfigurationHelper + .ApplyEntityConfigurationToUi( + IdentityModuleExtensionConsts.ModuleName, + IdentityModuleExtensionConsts.EntityNames.Role, + createFormTypes: new[] { typeof(Volo.Abp.Identity.Web.Pages.Identity.Roles.CreateModalModel.RoleInfoModel) }, + editFormTypes: new[] { typeof(Volo.Abp.Identity.Web.Pages.Identity.Roles.EditModalModel.RoleInfoModel) } + ); + + ModuleExtensionConfigurationHelper + .ApplyEntityConfigurationToUi( + IdentityModuleExtensionConsts.ModuleName, + IdentityModuleExtensionConsts.EntityNames.User, + createFormTypes: new[] { typeof(Volo.Abp.Identity.Web.Pages.Identity.Users.CreateModalModel.UserInfoViewModel) }, + editFormTypes: new[] { typeof(Volo.Abp.Identity.Web.Pages.Identity.Users.EditModalModel.UserInfoViewModel) } + ); + } } } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml index ce075d0b58..72ea9da317 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml @@ -1,9 +1,14 @@ @page @using Microsoft.AspNetCore.Mvc.Localization +@using Microsoft.Extensions.Localization @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.Identity.Localization -@model Volo.Abp.Identity.Web.Pages.Identity.Roles.CreateModalModel +@using Volo.Abp.Identity.Web.Pages.Identity.Roles +@using Volo.Abp.Localization +@using Volo.Abp.ObjectExtending +@model CreateModalModel @inject IHtmlLocalizer L +@inject IStringLocalizerFactory StringLocalizerFactory @{ Layout = null; } @@ -12,8 +17,27 @@ + + + + @foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties()) + { + if (propertyInfo.Type.IsEnum) + { + + } + else + { + + } + } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml.cs index df86acd94d..79787da08b 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.ObjectExtending; using Volo.Abp.Validation; namespace Volo.Abp.Identity.Web.Pages.Identity.Roles @@ -19,6 +20,8 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Roles public virtual Task OnGetAsync() { + Role = new RoleInfoModel(); + return Task.FromResult(Page()); } @@ -32,7 +35,7 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Roles return NoContent(); } - public class RoleInfoModel + public class RoleInfoModel : ExtensibleObject { [Required] [DynamicStringLength(typeof(IdentityRoleConsts), nameof(IdentityRoleConsts.MaxNameLength))] diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml index 0a3cd3668d..8ef33b32cc 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml @@ -1,9 +1,14 @@ @page @using Microsoft.AspNetCore.Mvc.Localization +@using Microsoft.Extensions.Localization @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.Identity.Localization -@model Volo.Abp.Identity.Web.Pages.Identity.Roles.EditModalModel +@using Volo.Abp.Identity.Web.Pages.Identity.Roles +@using Volo.Abp.Localization +@using Volo.Abp.ObjectExtending +@model EditModalModel @inject IHtmlLocalizer L +@inject IStringLocalizerFactory StringLocalizerFactory @{ Layout = null; } @@ -12,7 +17,9 @@ + + @if (Model.Role.IsStatic) { @@ -21,8 +28,27 @@ { } + + + + @foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties()) + { + if (propertyInfo.Type.IsEnum) + { + + } + else + { + + } + } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml.cs index 8a004e95a5..14236c1466 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Domain.Entities; +using Volo.Abp.ObjectExtending; using Volo.Abp.Validation; namespace Volo.Abp.Identity.Web.Pages.Identity.Roles @@ -36,7 +37,7 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Roles return NoContent(); } - public class RoleInfoModel : IHasConcurrencyStamp + public class RoleInfoModel : ExtensibleObject, IHasConcurrencyStamp { [HiddenInput] public Guid Id { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/Index.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/Index.cshtml index 4658f44013..734fc40fe5 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/Index.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/Index.cshtml @@ -41,13 +41,6 @@ - - - - @L["Actions"] - @L["RoleName"] - - - + \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js index 4331ab4229..d66d7240e3 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js @@ -12,76 +12,76 @@ abp.appPath + 'Identity/Roles/CreateModal' ); - $(function () { - var _$wrapper = $('#IdentityRolesWrapper'); - var _$table = _$wrapper.find('table'); + var _dataTable = null; - var _dataTable = _$table.DataTable( - abp.libs.datatables.normalizeConfiguration({ - order: [[1, 'asc']], - searching: false, - processing: true, - serverSide: true, - scrollX: true, - paging: true, - ajax: abp.libs.datatables.createAjax( - _identityRoleAppService.getList - ), - columnDefs: [ + abp.ui.extensions.entityActions.get('identity.role').addContributor( + function(actionList) { + return actionList.addManyTail( + [ { - rowAction: { - items: [ - { - text: l('Edit'), - visible: abp.auth.isGranted( - 'AbpIdentity.Roles.Update' - ), - action: function (data) { - _editModal.open({ - id: data.record.id, - }); - }, - }, - { - text: l('Permissions'), - visible: abp.auth.isGranted( - 'AbpIdentity.Roles.ManagePermissions' - ), - action: function (data) { - _permissionsModal.open({ - providerName: 'R', - providerKey: data.record.name, - }); - }, - }, - { - text: l('Delete'), - visible: function (data) { - return ( - !data.isStatic && - abp.auth.isGranted( - 'AbpIdentity.Roles.Delete' - ) - ); //TODO: Check permission - }, - confirmMessage: function (data) { - return l( - 'RoleDeletionConfirmationMessage', - data.record.name - ); - }, - action: function (data) { - _identityRoleAppService - .delete(data.record.id) - .then(function () { - _dataTable.ajax.reload(); - }); - }, - }, - ], + text: l('Edit'), + visible: abp.auth.isGranted( + 'AbpIdentity.Roles.Update' + ), + action: function (data) { + _editModal.open({ + id: data.record.id, + }); + }, + }, + { + text: l('Permissions'), + visible: abp.auth.isGranted( + 'AbpIdentity.Roles.ManagePermissions' + ), + action: function (data) { + _permissionsModal.open({ + providerName: 'R', + providerKey: data.record.name, + }); + }, + }, + { + text: l('Delete'), + visible: function (data) { + return ( + !data.isStatic && + abp.auth.isGranted( + 'AbpIdentity.Roles.Delete' + ) + ); //TODO: Check permission + }, + confirmMessage: function (data) { + return l( + 'RoleDeletionConfirmationMessage', + data.record.name + ); }, + action: function (data) { + _identityRoleAppService + .delete(data.record.id) + .then(function () { + _dataTable.ajax.reload(); + }); + }, + } + ] + ); + } + ); + + abp.ui.extensions.tableColumns.get('identity.role').addContributor( + function (columnList) { + columnList.addManyTail( + [ + { + title: l("Actions"), + rowAction: { + items: abp.ui.extensions.entityActions.get('identity.role').actions.toArray() + } }, { + title: l('RoleName'), data: 'name', render: function (data, type, row) { var name = '' + data + ''; @@ -99,8 +99,29 @@ } return name; }, - }, - ], + } + ] + ); + }, + 0 //adds as the first contributor + ); + + $(function () { + var _$wrapper = $('#IdentityRolesWrapper'); + var _$table = _$wrapper.find('table'); + + _dataTable = _$table.DataTable( + abp.libs.datatables.normalizeConfiguration({ + order: [[1, 'asc']], + searching: false, + processing: true, + serverSide: true, + scrollX: true, + paging: true, + ajax: abp.libs.datatables.createAjax( + _identityRoleAppService.getList + ), + columnDefs: abp.ui.extensions.tableColumns.get('identity.role').columns.toArray() }) ); diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml index d20d0a0026..b171b785a2 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml @@ -1,9 +1,14 @@ @page @using Microsoft.AspNetCore.Mvc.Localization +@using Microsoft.Extensions.Localization @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.Identity.Localization -@model Volo.Abp.Identity.Web.Pages.Identity.Users.CreateModalModel +@using Volo.Abp.Identity.Web.Pages.Identity.Users +@using Volo.Abp.Localization +@using Volo.Abp.ObjectExtending +@model CreateModalModel @inject IHtmlLocalizer L +@inject IStringLocalizerFactory StringLocalizerFactory @{ Layout = null; } @@ -22,6 +27,23 @@ + + @foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties()) + { + if (propertyInfo.Type.IsEnum) + { + + } + else + { + + } + } @for (var i = 0; i < Model.Roles.Length; i++) diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs index 6149c676b8..a868890632 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Auditing; using Volo.Abp.Application.Dtos; +using Volo.Abp.ObjectExtending; using Volo.Abp.Validation; namespace Volo.Abp.Identity.Web.Pages.Identity.Users @@ -52,7 +53,7 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users return NoContent(); } - public class UserInfoViewModel + public class UserInfoViewModel : ExtensibleObject { [Required] [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml index 26962350e0..56ac3929f1 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml @@ -1,9 +1,14 @@ @page @using Microsoft.AspNetCore.Mvc.Localization +@using Microsoft.Extensions.Localization @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.Identity.Localization -@model Volo.Abp.Identity.Web.Pages.Identity.Users.EditModalModel +@using Volo.Abp.Identity.Web.Pages.Identity.Users +@using Volo.Abp.Localization +@using Volo.Abp.ObjectExtending +@model EditModalModel @inject IHtmlLocalizer L +@inject IStringLocalizerFactory StringLocalizerFactory @{ Layout = null; } @@ -24,6 +29,24 @@ + + @foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties()) + { + if (propertyInfo.Type.IsEnum) + { + + } + else + { + + } + } + @for (var i = 0; i < Model.Roles.Length; i++) diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs index 08c5ae0dd9..a2336c2b8e 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc; using Volo.Abp.Auditing; using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Entities; +using Volo.Abp.ObjectExtending; using Volo.Abp.Validation; namespace Volo.Abp.Identity.Web.Pages.Identity.Users @@ -55,7 +56,7 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users return NoContent(); } - public class UserInfoViewModel : IHasConcurrencyStamp + public class UserInfoViewModel : ExtensibleObject, IHasConcurrencyStamp { [HiddenInput] public Guid Id { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/Index.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/Index.cshtml index 23f7747ac8..ee9192d58f 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/Index.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/Index.cshtml @@ -42,15 +42,6 @@ - - - - @L["Actions"] - @L["UserName"] - @L["EmailAddress"] - @L["PhoneNumber"] - - - + \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/index.js b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/index.js index 53f66ddb5e..71675d8b8e 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/index.js +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/index.js @@ -12,10 +12,91 @@ abp.appPath + 'AbpPermissionManagement/PermissionManagementModal' ); + var _dataTable = null; + + abp.ui.extensions.entityActions.get('identity.user').addContributor( + function(actionList) { + return actionList.addManyTail( + [ + { + text: l('Edit'), + visible: abp.auth.isGranted( + 'AbpIdentity.Users.Update' + ), + action: function (data) { + _editModal.open({ + id: data.record.id, + }); + }, + }, + { + text: l('Permissions'), + visible: abp.auth.isGranted( + 'AbpIdentity.Users.ManagePermissions' + ), + action: function (data) { + _permissionsModal.open({ + providerName: 'U', + providerKey: data.record.id, + }); + }, + }, + { + text: l('Delete'), + visible: abp.auth.isGranted( + 'AbpIdentity.Users.Delete' + ), + confirmMessage: function (data) { + return l( + 'UserDeletionConfirmationMessage', + data.record.userName + ); + }, + action: function (data) { + _identityUserAppService + .delete(data.record.id) + .then(function () { + _dataTable.ajax.reload(); + }); + }, + } + ] + ); + } + ); + + abp.ui.extensions.tableColumns.get('identity.user').addContributor( + function (columnList) { + columnList.addManyTail( + [ + { + title: l("Actions"), + rowAction: { + items: abp.ui.extensions.entityActions.get('identity.user').actions.toArray() + } + }, + { + title: l('UserName'), + data: 'userName', + }, + { + title: l('EmailAddress'), + data: 'email', + }, + { + title: l('PhoneNumber'), + data: 'phoneNumber', + } + ] + ); + }, + 0 //adds as the first contributor + ); + $(function () { var _$wrapper = $('#IdentityUsersWrapper'); var _$table = _$wrapper.find('table'); - var _dataTable = _$table.DataTable( + _dataTable = _$table.DataTable( abp.libs.datatables.normalizeConfiguration({ order: [[1, 'asc']], processing: true, @@ -25,65 +106,7 @@ ajax: abp.libs.datatables.createAjax( _identityUserAppService.getList ), - columnDefs: [ - { - rowAction: { - items: [ - { - text: l('Edit'), - visible: abp.auth.isGranted( - 'AbpIdentity.Users.Update' - ), - action: function (data) { - _editModal.open({ - id: data.record.id, - }); - }, - }, - { - text: l('Permissions'), - visible: abp.auth.isGranted( - 'AbpIdentity.Users.ManagePermissions' - ), - action: function (data) { - _permissionsModal.open({ - providerName: 'U', - providerKey: data.record.id, - }); - }, - }, - { - text: l('Delete'), - visible: abp.auth.isGranted( - 'AbpIdentity.Users.Delete' - ), - confirmMessage: function (data) { - return l( - 'UserDeletionConfirmationMessage', - data.record.userName - ); - }, - action: function (data) { - _identityUserAppService - .delete(data.record.id) - .then(function () { - _dataTable.ajax.reload(); - }); - }, - }, - ], - }, - }, - { - data: 'userName', - }, - { - data: 'email', - }, - { - data: 'phoneNumber', - }, - ], + columnDefs: abp.ui.extensions.tableColumns.get('identity.user').columns.toArray() }) ); diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.js b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.js index b168fe0fb6..4b19d0bbb7 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.js +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.js @@ -17,69 +17,79 @@ modalClass: 'TenantConnectionStringManagement', }); - abp.ui.extensions.tableColumns.get("tenant").addContributor( + var _dataTable = null; + + abp.ui.extensions.entityActions.get('tenantManagement.tenant').addContributor( + function(actionList) { + return actionList.addManyTail( + [ + { + text: l('Edit'), + visible: abp.auth.isGranted( + 'AbpTenantManagement.Tenants.Update' + ), + action: function (data) { + _editModal.open({ + id: data.record.id, + }); + }, + }, + { + text: l('ConnectionStrings'), + visible: abp.auth.isGranted( + 'AbpTenantManagement.Tenants.ManageConnectionStrings' + ), + action: function (data) { + _connectionStringsModal.open({ + id: data.record.id, + }); + }, + }, + { + text: l('Features'), + visible: abp.auth.isGranted( + 'AbpTenantManagement.Tenants.ManageFeatures' + ), + action: function (data) { + _featuresModal.open({ + providerName: 'T', + providerKey: data.record.id, + }); + }, + }, + { + text: l('Delete'), + visible: abp.auth.isGranted( + 'AbpTenantManagement.Tenants.Delete' + ), + confirmMessage: function (data) { + return l( + 'TenantDeletionConfirmationMessage', + data.record.name + ); + }, + action: function (data) { + _tenantAppService + .delete(data.record.id) + .then(function () { + _dataTable.ajax.reload(); + }); + }, + } + ] + ); + } + ); + + abp.ui.extensions.tableColumns.get('tenantManagement.tenant').addContributor( function (columnList) { columnList.addManyTail( [ { title: l("Actions"), rowAction: { - items: [ - { - text: l('Edit'), - visible: abp.auth.isGranted( - 'AbpTenantManagement.Tenants.Update' - ), - action: function (data) { - _editModal.open({ - id: data.record.id, - }); - }, - }, - { - text: l('ConnectionStrings'), - visible: abp.auth.isGranted( - 'AbpTenantManagement.Tenants.ManageConnectionStrings' - ), - action: function (data) { - _connectionStringsModal.open({ - id: data.record.id, - }); - }, - }, - { - text: l('Features'), - visible: abp.auth.isGranted( - 'AbpTenantManagement.Tenants.ManageFeatures' - ), - action: function (data) { - _featuresModal.open({ - providerName: 'T', - providerKey: data.record.id, - }); - }, - }, - { - text: l('Delete'), - visible: abp.auth.isGranted( - 'AbpTenantManagement.Tenants.Delete' - ), - confirmMessage: function (data) { - return l( - 'TenantDeletionConfirmationMessage', - data.record.name - ); - }, - action: function (data) { - _tenantAppService - .delete(data.record.id) - .then(function () { - _dataTable.ajax.reload(); - }); - }, - }, - ], - }, + items: abp.ui.extensions.entityActions.get('tenantManagement.tenant').actions.toArray() + } }, { title: l("TenantName"), @@ -94,7 +104,7 @@ $(function () { var _$wrapper = $('#TenantsWrapper'); - var _dataTable = _$wrapper.find('table').DataTable( + _dataTable = _$wrapper.find('table').DataTable( abp.libs.datatables.normalizeConfiguration({ order: [[1, 'asc']], processing: true, @@ -102,7 +112,7 @@ scrollX: true, serverSide: true, ajax: abp.libs.datatables.createAjax(_tenantAppService.getList), - columnDefs: abp.ui.extensions.tableColumns.get("tenant").columns.toArray(), + columnDefs: abp.ui.extensions.tableColumns.get('tenantManagement.tenant').columns.toArray(), }) ); From 1bf4599ce935cba6fdb82b603a49b93826609f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 23 Jul 2020 17:39:53 +0300 Subject: [PATCH 52/77] Update Part-10.md --- docs/en/Tutorials/Part-10.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md index e522f676a9..57aa71f6cb 100644 --- a/docs/en/Tutorials/Part-10.md +++ b/docs/en/Tutorials/Part-10.md @@ -64,6 +64,12 @@ Open the `Books/Book.cs` in the `Acme.BookStore.Domain` project and add the foll public Guid AuthorId { get; set; } ```` +{{if DB=="EF"}} + +> In this tutorial, we preferred to not add a **navigation property** to the `Author` entity (like `public Author Author { get; set; }`). This is due to follow the DDD best practices (rule: refer to other aggregates only by id). However, you can add such a navigation property and configure it for the EF Core. In this way, you don't need to write join queries while getting books with their entities (just like we will done below) which makes your application code simpler. + +{{end}} + ## Database & Data Migration Added a new, required `AuthorId` property to the `Book` entity. But, what about the existing books on the database? They currently don't have `AuthorId`s and this will be a problem when we try to run the application. From 8254312aa098f513f04293ebb9cf4b746453c788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 23 Jul 2020 18:02:21 +0300 Subject: [PATCH 53/77] Update Part-10.md --- docs/en/Tutorials/Part-10.md | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md index 57aa71f6cb..d525cbbc61 100644 --- a/docs/en/Tutorials/Part-10.md +++ b/docs/en/Tutorials/Part-10.md @@ -915,29 +915,6 @@ You can run the application and try to create a new book or update an existing b {{else if UI=="NG"}} -### Service Proxy Generation - -Since the book service has changed (added a new action, changed DTOs), we need to refresh the service proxies. Run the following command in the root folder of the angular application: - -````bash -abp generate-proxy -```` - -### Show Author Name on the Books Table - -Open the `/src/book/book.component.html` and add the following column to the `ngx-datatable`, just after the `name` column. - -````html - -```` - -This should add an *Author* column to the books page: - -![bookstore-added-author-to-book-list-angular](images/bookstore-added-author-to-book-list-angular.png) - -TODO... +***Angular UI is being prepared...*** {{end}} \ No newline at end of file From 48bfc012030b036f574e0fbbc82308c45ed09b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 23 Jul 2020 18:16:43 +0300 Subject: [PATCH 54/77] Update Part-6.md --- docs/en/Tutorials/Part-6.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index 413ff48504..0c480e4318 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -248,7 +248,7 @@ Whenever you throw an `AuthorAlreadyExistsException`, the end use will see a nic `AuthorManager` injects the `IAuthorRepository`, so we need to define it. Create this new interface in the `Authors` folder (namespace) of the `Acme.BookStore.Domain` project: -````charp +````csharp using System; using System.Collections.Generic; using System.Threading.Tasks; From 82e98e91f496a39f1da2876422de4e951d4d1be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 23 Jul 2020 18:19:15 +0300 Subject: [PATCH 55/77] Update links --- docs/en/Tutorials/Part-3.md | 2 +- docs/en/Tutorials/Part-4.md | 2 +- docs/en/Tutorials/Part-5.md | 2 +- docs/en/Tutorials/Part-6.md | 2 +- docs/en/Tutorials/Part-7.md | 2 +- docs/en/Tutorials/Part-8.md | 2 +- docs/en/Tutorials/Part-9.md | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index aeff04b1d4..a0e839186c 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -1185,4 +1185,4 @@ Clicking the "Delete" action calls the `delete` method which then shows a confir ## The Next Part -See the [next part](part-4.md) of this tutorial. +See the [next part](Part-4.md) of this tutorial. diff --git a/docs/en/Tutorials/Part-4.md b/docs/en/Tutorials/Part-4.md index 1f27e1f6a5..acbce65c3e 100644 --- a/docs/en/Tutorials/Part-4.md +++ b/docs/en/Tutorials/Part-4.md @@ -252,4 +252,4 @@ Congratulations, the **green icons** indicates that the tests have been successf ## The Next Part -See the [next part](part-5.md) of this tutorial. \ No newline at end of file +See the [next part](Part-5.md) of this tutorial. \ No newline at end of file diff --git a/docs/en/Tutorials/Part-5.md b/docs/en/Tutorials/Part-5.md index 6fc760f66a..5674cf83d6 100644 --- a/docs/en/Tutorials/Part-5.md +++ b/docs/en/Tutorials/Part-5.md @@ -406,4 +406,4 @@ Open the `/src/app/book/book.component.html` file and replace the edit and delet ## The Next Part -See the [next part](part-6.md) of this tutorial. \ No newline at end of file +See the [next part](Part-6.md) of this tutorial. \ No newline at end of file diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index 0c480e4318..1bb07007b6 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -286,4 +286,4 @@ This part covered the domain layer of the authors functionality of the book stor ## The Next Part -See the [next part](part-7.md) of this tutorial. \ No newline at end of file +See the [next part](Part-7.md) of this tutorial. \ No newline at end of file diff --git a/docs/en/Tutorials/Part-7.md b/docs/en/Tutorials/Part-7.md index ead535720a..81fd07f1bf 100644 --- a/docs/en/Tutorials/Part-7.md +++ b/docs/en/Tutorials/Part-7.md @@ -233,4 +233,4 @@ namespace Acme.BookStore.Authors ## The Next Part -See the [next part](part-8.md) of this tutorial. \ No newline at end of file +See the [next part](Part-8.md) of this tutorial. \ No newline at end of file diff --git a/docs/en/Tutorials/Part-8.md b/docs/en/Tutorials/Part-8.md index 0930ca88d8..38e5b6013f 100644 --- a/docs/en/Tutorials/Part-8.md +++ b/docs/en/Tutorials/Part-8.md @@ -572,4 +572,4 @@ Created some tests for the application service methods, which should be clear to ## The Next Part -See the [next part](part-9.md) of this tutorial. \ No newline at end of file +See the [next part](Part-9.md) of this tutorial. \ No newline at end of file diff --git a/docs/en/Tutorials/Part-9.md b/docs/en/Tutorials/Part-9.md index 32149f1485..3b5936732c 100644 --- a/docs/en/Tutorials/Part-9.md +++ b/docs/en/Tutorials/Part-9.md @@ -851,4 +851,4 @@ That's all! This is a fully working CRUD page, you can create, edit and delete a ## The Next Part -See the [next part](part-10.md) of this tutorial. \ No newline at end of file +See the [next part](Part-10.md) of this tutorial. \ No newline at end of file From 123740c623ea18e33a5fb3be310ae56929e3fa47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 23 Jul 2020 18:26:24 +0300 Subject: [PATCH 56/77] Update Part-9.md --- docs/en/Tutorials/Part-9.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/en/Tutorials/Part-9.md b/docs/en/Tutorials/Part-9.md index 3b5936732c..1b9451700d 100644 --- a/docs/en/Tutorials/Part-9.md +++ b/docs/en/Tutorials/Part-9.md @@ -731,14 +731,14 @@ Open the `/src/app/author/author.component.html` and replace the content as belo

- {{ '::Menu:Authors' | abpLocalization }} + {%{{{ '::Menu:Authors' | abpLocalization }}}%}
@@ -759,14 +759,14 @@ Open the `/src/app/author/author.component.html` and replace the content as belo aria-haspopup="true" ngbDropdownToggle > - {{ '::Actions' | abpLocalization }} + {%{{{ '::Actions' | abpLocalization }}}%}
@@ -775,7 +775,7 @@ Open the `/src/app/author/author.component.html` and replace the content as belo - {{ row.birthDate | date }} + {%{{{ row.birthDate | date }}}%} @@ -784,7 +784,7 @@ Open the `/src/app/author/author.component.html` and replace the content as belo -

{{ (selectedAuthor.id ? '::Edit' : '::NewAuthor') | abpLocalization }}

+

{%{{{ (selectedAuthor.id ? '::Edit' : '::NewAuthor') | abpLocalization }}}%}

@@ -810,12 +810,12 @@ Open the `/src/app/author/author.component.html` and replace the content as belo
From 7a90fbefce4b1143e27c6b08f679960524ace8e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 23 Jul 2020 18:29:13 +0300 Subject: [PATCH 57/77] Update Part-10.md --- docs/en/Tutorials/Part-10.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md index d525cbbc61..fbecf39b86 100644 --- a/docs/en/Tutorials/Part-10.md +++ b/docs/en/Tutorials/Part-10.md @@ -226,7 +226,7 @@ The only change is that we set the `AuthorId` properties of the `Book` entities. You can now run the `.DbMigrator` console application to **migrate** the **database schema** and **seed** the initial data. -{{else if DB="Mongo"}} +{{else if DB=="Mongo"}} You can now run the `.DbMigrator` console application to **seed** the initial data. From c0230b7ccc1eb6a77d293909c46f5684fc5b4803 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 24 Jul 2020 06:01:08 +0300 Subject: [PATCH 58/77] feat: add support for mapping of culture name to Angular locale file name resolves #4867 --- npm/ng-packs/packages/core/src/lib/core.module.ts | 9 +++++++-- .../packages/core/src/lib/models/common.ts | 1 + .../core/src/lib/services/localization.service.ts | 4 +++- .../packages/core/src/lib/tokens/options.token.ts | 11 +++++++++++ .../packages/core/src/lib/utils/initial-utils.ts | 14 ++++++++------ 5 files changed, 30 insertions(+), 9 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts index f118ab2836..3fd29fec53 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -34,7 +34,7 @@ import { ConfigState } from './states/config.state'; import { ProfileState } from './states/profile.state'; import { ReplaceableComponentsState } from './states/replaceable-components.state'; import { SessionState } from './states/session.state'; -import { CORE_OPTIONS } from './tokens/options.token'; +import { CORE_OPTIONS, coreOptionsFactory } from './tokens/options.token'; import { noop } from './utils/common-utils'; import './utils/date-extensions'; import { getInitialData, localeInitializer, configureOAuth } from './utils/initial-utils'; @@ -171,9 +171,14 @@ export class CoreModule { useValue: { environment: options.environment }, }, { - provide: CORE_OPTIONS, + provide: 'CORE_OPTIONS', useValue: options, }, + { + provide: CORE_OPTIONS, + useFactory: coreOptionsFactory, + deps: ['CORE_OPTIONS'], + }, { provide: HTTP_INTERCEPTORS, useClass: ApiInterceptor, diff --git a/npm/ng-packs/packages/core/src/lib/models/common.ts b/npm/ng-packs/packages/core/src/lib/models/common.ts index 930265020c..aa2f961b7b 100644 --- a/npm/ng-packs/packages/core/src/lib/models/common.ts +++ b/npm/ng-packs/packages/core/src/lib/models/common.ts @@ -9,6 +9,7 @@ export namespace ABP { environment: Partial; skipGetAppConfiguration?: boolean; sendNullsAsQueryParam?: boolean; + cultureNameToLocaleFileNameMapping?: Dictionary; } export interface Test { diff --git a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts index 3f89642fd9..e488bafaf4 100644 --- a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts @@ -8,6 +8,7 @@ import { Config } from '../models/config'; import { ConfigState } from '../states/config.state'; import { registerLocale } from '../utils/initial-utils'; import { createLocalizer, createLocalizerWithFallback } from '../utils/localization-utils'; +import { CORE_OPTIONS } from '../tokens/options.token'; type ShouldReuseRoute = (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) => boolean; @@ -44,11 +45,12 @@ export class LocalizationService { registerLocale(locale: string) { const router = this.injector.get(Router); + const { cultureNameToLocaleFileNameMapping: localeNameMap } = this.injector.get(CORE_OPTIONS); const { shouldReuseRoute } = router.routeReuseStrategy; router.routeReuseStrategy.shouldReuseRoute = () => false; router.navigated = false; - return registerLocale(locale).then(() => { + return registerLocale(locale, localeNameMap).then(() => { this.ngZone.run(async () => { await router.navigateByUrl(router.url).catch(noop); router.routeReuseStrategy.shouldReuseRoute = shouldReuseRoute; diff --git a/npm/ng-packs/packages/core/src/lib/tokens/options.token.ts b/npm/ng-packs/packages/core/src/lib/tokens/options.token.ts index 2c4dffe9dc..a4b05c8cc7 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/options.token.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/options.token.ts @@ -1,4 +1,15 @@ import { InjectionToken } from '@angular/core'; import { ABP } from '../models/common'; +import differentLocales from '../constants/different-locales'; export const CORE_OPTIONS = new InjectionToken('CORE_OPTIONS'); + +export function coreOptionsFactory({ + cultureNameToLocaleFileNameMapping: localeNameMap = {}, + ...options +}: ABP.Root) { + return { + ...options, + cultureNameToLocaleFileNameMapping: { ...differentLocales, ...localeNameMap }, + } as ABP.Root; +} diff --git a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts index bfca50bcd2..78677b7ca4 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts @@ -4,7 +4,6 @@ import { Store } from '@ngxs/store'; import { OAuthService } from 'angular-oauth2-oidc'; import { tap } from 'rxjs/operators'; import { GetAppConfiguration } from '../actions/config.actions'; -import differentLocales from '../constants/different-locales'; import { ABP } from '../models/common'; import { ConfigState } from '../states/config.state'; import { CORE_OPTIONS } from '../tokens/options.token'; @@ -45,22 +44,25 @@ function checkAccessToken(store: Store, injector: Injector) { export function localeInitializer(injector: Injector) { const fn = () => { const store: Store = injector.get(Store); + const options = injector.get(CORE_OPTIONS); const lang = store.selectSnapshot(state => state.SessionState.language) || 'en'; return new Promise((resolve, reject) => { - registerLocale(lang).then(() => resolve('resolved'), reject); + registerLocale(lang, options.cultureNameToLocaleFileNameMapping).then( + () => resolve('resolved'), + reject, + ); }); }; return fn; } -export function registerLocale(locale: string) { +export function registerLocale(locale: string, localeNameMap: ABP.Dictionary) { return import( - /* webpackInclude: /(af|ar|am|ar-SA|as|az-Latn|be|bg|bn-BD|bn-IN|bs|ca|ca-ES-VALENCIA|cs|cy|da|de|de|el|en-GB|en|es|en|es-US|es-MX|et|eu|fa|fi|en|fr|fr|fr-CA|ga|gd|gl|gu|ha|he|hi|hr|hu|hy|id|ig|is|it|it|ja|ka|kk|km|kn|ko|kok|en|en|lb|lt|lv|en|mk|ml|mn|mr|ms|mt|nb|ne|nl|nl-BE|nn|en|or|pa|pa-Arab|pl|en|pt|pt-PT|en|en|ro|ru|rw|pa-Arab|si|sk|sl|sq|sr-Cyrl-BA|sr-Cyrl|sr-Latn|sv|sw|ta|te|tg|th|ti|tk|tn|tr|tt|ug|uk|ur|uz-Latn|vi|wo|xh|yo|zh-Hans|zh-Hant|zu)\.js$/ */ - /* webpackChunkName: "[request]"*/ - `@angular/common/locales/${differentLocales[locale] || locale}.js` + /* webpackChunkName: "_locale-[request]"*/ + `@angular/common/locales/${localeNameMap[locale] || locale}.js` ).then(module => { registerLocaleData(module.default); }); From 6b2cb684ce30a039931ca367f664b7b6e951fef0 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 24 Jul 2020 06:03:14 +0300 Subject: [PATCH 59/77] docs: add Mapping of Culture Name to Angular Locale File Name topic to Localization.md #4867 --- docs/en/UI/Angular/Localization.md | 29 +++++++++++++++++++++ docs/en/UI/Angular/images/locale-error.png | Bin 0 -> 19175 bytes 2 files changed, 29 insertions(+) create mode 100644 docs/en/UI/Angular/images/locale-error.png diff --git a/docs/en/UI/Angular/Localization.md b/docs/en/UI/Angular/Localization.md index 78822a16de..d3215bd010 100644 --- a/docs/en/UI/Angular/Localization.md +++ b/docs/en/UI/Angular/Localization.md @@ -193,6 +193,35 @@ import { Component } from '@angular/core'; export class AppComponent {} ``` +## Mapping of Culture Name to Angular Locale File Name + +Some of the culture names defined in .NET do not match Angular locales. In such cases, the Angular app throws an error like below at runtime: + +![locale-error](./images/locale-error.png) + +If you see the error like above, you should pass the `cultureNameToLocaleFileNameMapping` property like below to CoreModule's forRoot static method. + +```js +// app.module.ts + +@NgModule({ + imports: [ + // other imports + CoreModule.forRoot({ + // other options + cultureNameToLocaleFileNameMapping: { + "X": "Y", + "AnotherLocaleNameDefinedInDotnet": "AnotherLocaleNameDefinedInAngular" + } + }) + //... +``` + +- The key indicated by "X" above represents the culture name defined in .NET (e.g. "en-US"). +- The value indicated by "Y" above represents the locale file name defined in Angular (e.g. "en"). + +See the [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales) + ## See Also diff --git a/docs/en/UI/Angular/images/locale-error.png b/docs/en/UI/Angular/images/locale-error.png new file mode 100644 index 0000000000000000000000000000000000000000..de385a59c021b876249fe9886d6758dd81b1d2b1 GIT binary patch literal 19175 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 Date: Fri, 24 Jul 2020 09:42:22 +0300 Subject: [PATCH 60/77] Split solutions into two groups --- build/common.ps1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build/common.ps1 b/build/common.ps1 index c75e77178c..f3ade1f242 100644 --- a/build/common.ps1 +++ b/build/common.ps1 @@ -13,13 +13,14 @@ $solutionPaths = ( "../modules/identity", "../modules/identityserver", "../modules/tenant-management", - "../modules/account", - "../modules/docs", - "../modules/blogging", "../modules/audit-logging", "../modules/background-jobs", + "../modules/account", + "../modules/client-simulation", "../modules/virtual-file-explorer", + "../modules/docs", + "../modules/blogging", "../templates/module/aspnet-core", "../templates/app/aspnet-core", "../abp_io/AbpIoLocalization" From 8e478adca2c827d75864cd7142015841b62ce876 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 24 Jul 2020 10:00:14 +0300 Subject: [PATCH 61/77] docs: update Localization.md --- docs/en/UI/Angular/Localization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/UI/Angular/Localization.md b/docs/en/UI/Angular/Localization.md index d3215bd010..94660c6f44 100644 --- a/docs/en/UI/Angular/Localization.md +++ b/docs/en/UI/Angular/Localization.md @@ -211,7 +211,7 @@ If you see the error like above, you should pass the `cultureNameToLocaleFileNam // other options cultureNameToLocaleFileNameMapping: { "X": "Y", - "AnotherLocaleNameDefinedInDotnet": "AnotherLocaleNameDefinedInAngular" + "AnotherCultureNameDefinedInDotnet": "AnotherLocaleFileNameDefinedInAngular" } }) //... From 986494c2713d12afa4fa0efbe1eedda120abe6a1 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Fri, 24 Jul 2020 10:13:05 +0300 Subject: [PATCH 62/77] add LicenseStartDate --- .../AbpIoLocalization/Admin/Localization/Resources/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index a07b35c66b..776b61b168 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -103,7 +103,7 @@ "MasterModules": "Master Modules", "OrganizationName": "Organization name", "CreationDate": "Creation date", - "LicenseStart": "License start date", + "LicenseStartDate": "License start date", "LicenseEndDate": "License end date", "OrganizationNamePlaceholder": "Organization name...", "TotalQuestionCountPlaceholder": "Total question count...", From 9f60ec849e04e5f1bcae57cbfa518b8d2631f545 Mon Sep 17 00:00:00 2001 From: Erol Arkat Date: Fri, 24 Jul 2020 10:30:24 +0300 Subject: [PATCH 63/77] Reduce build-all.ps1 --- build/build-all.ps1 | 6 ++-- build/common.ps1 | 70 +++++++++++++++++++++++++++++---------------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/build/build-all.ps1 b/build/build-all.ps1 index ffaffa67a3..5223b9e8c6 100644 --- a/build/build-all.ps1 +++ b/build/build-all.ps1 @@ -1,6 +1,8 @@ -. ".\common.ps1" +$dev = $args[0] -# Build all solutions +. ".\common.ps1" $dev + +# Build all solutions foreach ($solutionPath in $solutionPaths) { $solutionAbsPath = (Join-Path $rootFolder $solutionPath) diff --git a/build/common.ps1 b/build/common.ps1 index f3ade1f242..ccb124a486 100644 --- a/build/common.ps1 +++ b/build/common.ps1 @@ -1,27 +1,49 @@ -# COMMON PATHS +$dev = $args[0] -$rootFolder = (Get-Item -Path "./" -Verbose).FullName +# COMMON PATHS -# List of solutions +$rootFolder = (Get-Item -Path "./" -Verbose).FullName -$solutionPaths = ( - "../framework", - "../modules/users", - "../modules/permission-management", - "../modules/setting-management", - "../modules/feature-management", - "../modules/identity", - "../modules/identityserver", - "../modules/tenant-management", - "../modules/audit-logging", - "../modules/background-jobs", - "../modules/account", - - "../modules/client-simulation", - "../modules/virtual-file-explorer", - "../modules/docs", - "../modules/blogging", - "../templates/module/aspnet-core", - "../templates/app/aspnet-core", - "../abp_io/AbpIoLocalization" -) \ No newline at end of file +if ($dev -eq "-d") +{ + # List of solutions used only in development mode + Write-host "" + Write-host ":::::::::::::: !!! You are in development mode !!! ::::::::::::::" -ForegroundColor red -BackgroundColor yellow + Write-host "" + $solutionPaths = ( + "../framework", + "../modules/users", + "../modules/permission-management", + "../modules/setting-management", + "../modules/feature-management", + "../modules/identity", + "../modules/identityserver", + "../modules/tenant-management", + "../modules/audit-logging", + "../modules/background-jobs", + "../modules/account" + ) +}else{ + # List of all solutions + $solutionPaths = ( + "../framework", + "../modules/users", + "../modules/permission-management", + "../modules/setting-management", + "../modules/feature-management", + "../modules/identity", + "../modules/identityserver", + "../modules/tenant-management", + "../modules/audit-logging", + "../modules/background-jobs", + "../modules/account", + + "../modules/client-simulation", + "../modules/virtual-file-explorer", + "../modules/docs", + "../modules/blogging", + "../templates/module/aspnet-core", + "../templates/app/aspnet-core", + "../abp_io/AbpIoLocalization" + ) +} From c16b25fc9948157819465120c0c0cad04d31e344 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Fri, 24 Jul 2020 10:34:23 +0300 Subject: [PATCH 64/77] Update Text-Templating.md --- docs/en/Text-Templating.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index c71787f598..3008db5c41 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -4,7 +4,7 @@ ABP Framework provides a simple, yet efficient text template system. Text templating is used to dynamically render contents based on a template and a model (a data object): -***TEMPLATE + MODEL ==render==> RENDERED CONTENT*** +Template + Model =renderer=> Rendered Content It is very similar to an ASP.NET Core Razor View (or Page): @@ -454,4 +454,4 @@ Return `null` if your source can not find the content, so `ITemplateContentProvi * [The source code of the sample application](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo) developed and referred through this document. * [Localization system](Localization.md). -* [Virtual File System](Virtual-File-System.md). \ No newline at end of file +* [Virtual File System](Virtual-File-System.md). From e001950324b160defe609cd170710658e18502b6 Mon Sep 17 00:00:00 2001 From: Erol Arkat Date: Fri, 24 Jul 2020 11:58:27 +0300 Subject: [PATCH 65/77] added -f parameter for build-all.ps1 --- build/build-all.ps1 | 6 +++--- build/common.ps1 | 37 +++++++++++++------------------------ 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/build/build-all.ps1 b/build/build-all.ps1 index 5223b9e8c6..5841b16bb6 100644 --- a/build/build-all.ps1 +++ b/build/build-all.ps1 @@ -1,8 +1,8 @@ -$dev = $args[0] +$full = $args[0] -. ".\common.ps1" $dev +. ".\common.ps1" $full -# Build all solutions +# Build all solutions foreach ($solutionPath in $solutionPaths) { $solutionAbsPath = (Join-Path $rootFolder $solutionPath) diff --git a/build/common.ps1 b/build/common.ps1 index ccb124a486..18019a154e 100644 --- a/build/common.ps1 +++ b/build/common.ps1 @@ -1,16 +1,11 @@ -$dev = $args[0] +$full = $args[0] # COMMON PATHS $rootFolder = (Get-Item -Path "./" -Verbose).FullName -if ($dev -eq "-d") -{ - # List of solutions used only in development mode - Write-host "" - Write-host ":::::::::::::: !!! You are in development mode !!! ::::::::::::::" -ForegroundColor red -BackgroundColor yellow - Write-host "" - $solutionPaths = ( +# List of solutions used only in development mode +$solutionPaths = @( "../framework", "../modules/users", "../modules/permission-management", @@ -23,21 +18,11 @@ if ($dev -eq "-d") "../modules/background-jobs", "../modules/account" ) -}else{ - # List of all solutions - $solutionPaths = ( - "../framework", - "../modules/users", - "../modules/permission-management", - "../modules/setting-management", - "../modules/feature-management", - "../modules/identity", - "../modules/identityserver", - "../modules/tenant-management", - "../modules/audit-logging", - "../modules/background-jobs", - "../modules/account", - + +if ($full -eq "-f") +{ + # List of additional solutions required for full build + $solutionPaths += ( "../modules/client-simulation", "../modules/virtual-file-explorer", "../modules/docs", @@ -45,5 +30,9 @@ if ($dev -eq "-d") "../templates/module/aspnet-core", "../templates/app/aspnet-core", "../abp_io/AbpIoLocalization" - ) + ) +}else{ + Write-host "" + Write-host ":::::::::::::: !!! You are in development mode !!! ::::::::::::::" -ForegroundColor red -BackgroundColor yellow + Write-host "" } From e3c81fed142a98c1a66300709260b6b31b798a64 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 24 Jul 2020 12:01:26 +0300 Subject: [PATCH 66/77] docs: update Localization.md --- docs/en/UI/Angular/Localization.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/en/UI/Angular/Localization.md b/docs/en/UI/Angular/Localization.md index 94660c6f44..2282a40598 100644 --- a/docs/en/UI/Angular/Localization.md +++ b/docs/en/UI/Angular/Localization.md @@ -207,19 +207,16 @@ If you see the error like above, you should pass the `cultureNameToLocaleFileNam @NgModule({ imports: [ // other imports - CoreModule.forRoot({ + CoreModule.forRoot({ // other options cultureNameToLocaleFileNameMapping: { - "X": "Y", - "AnotherCultureNameDefinedInDotnet": "AnotherLocaleFileNameDefinedInAngular" + "DotnetCultureName": "AngularLocaleFileName", + "pt-BR": "pt" // example } }) //... ``` -- The key indicated by "X" above represents the culture name defined in .NET (e.g. "en-US"). -- The value indicated by "Y" above represents the locale file name defined in Angular (e.g. "en"). - See the [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales) From aaf3c7d47f3034a32093893475992e1963c9403b Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 24 Jul 2020 12:02:50 +0300 Subject: [PATCH 67/77] docs: add a dot --- docs/en/UI/Angular/Localization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/UI/Angular/Localization.md b/docs/en/UI/Angular/Localization.md index 2282a40598..af80ccf965 100644 --- a/docs/en/UI/Angular/Localization.md +++ b/docs/en/UI/Angular/Localization.md @@ -217,7 +217,7 @@ If you see the error like above, you should pass the `cultureNameToLocaleFileNam //... ``` -See the [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales) +See the [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales). ## See Also From 7011863bbee54934d007f929343bfcbeae53fd8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Levent=20Arman=20=C3=96zak?= Date: Fri, 24 Jul 2020 12:04:14 +0300 Subject: [PATCH 68/77] Update Localization.md --- docs/en/UI/Angular/Localization.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/UI/Angular/Localization.md b/docs/en/UI/Angular/Localization.md index af80ccf965..1ee68ace82 100644 --- a/docs/en/UI/Angular/Localization.md +++ b/docs/en/UI/Angular/Localization.md @@ -199,7 +199,7 @@ Some of the culture names defined in .NET do not match Angular locales. In such ![locale-error](./images/locale-error.png) -If you see the error like above, you should pass the `cultureNameToLocaleFileNameMapping` property like below to CoreModule's forRoot static method. +If you see an error like this, you should pass the `cultureNameToLocaleFileNameMapping` property like below to CoreModule's forRoot static method. ```js // app.module.ts @@ -217,7 +217,7 @@ If you see the error like above, you should pass the `cultureNameToLocaleFileNam //... ``` -See the [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales). +See [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales). ## See Also @@ -226,4 +226,4 @@ See the [all locale files in Angular](https://github.com/angular/angular/tree/ma ## What's Next? -* [Permission Management](./Permission-Management.md) \ No newline at end of file +* [Permission Management](./Permission-Management.md) From 91c96ad7cab24854cea999924685ffeef0294f37 Mon Sep 17 00:00:00 2001 From: Erol Arkat Date: Fri, 24 Jul 2020 12:50:36 +0300 Subject: [PATCH 69/77] added -f parameter for test and release build --- build/build-all-release.ps1 | 2 +- build/build-all.ps1 | 2 ++ build/test-all.ps1 | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/build-all-release.ps1 b/build/build-all-release.ps1 index 1c86351379..f54912d4f5 100644 --- a/build/build-all-release.ps1 +++ b/build/build-all-release.ps1 @@ -1,4 +1,4 @@ -. ".\common.ps1" +. ".\common.ps1" -f # Build all solutions diff --git a/build/build-all.ps1 b/build/build-all.ps1 index 5841b16bb6..16f8bf037c 100644 --- a/build/build-all.ps1 +++ b/build/build-all.ps1 @@ -4,6 +4,8 @@ $full = $args[0] # Build all solutions +Write-Host $solutionPaths + foreach ($solutionPath in $solutionPaths) { $solutionAbsPath = (Join-Path $rootFolder $solutionPath) Set-Location $solutionAbsPath diff --git a/build/test-all.ps1 b/build/test-all.ps1 index 1465bff6c3..9123aee313 100644 --- a/build/test-all.ps1 +++ b/build/test-all.ps1 @@ -1,4 +1,4 @@ -. ".\common.ps1" +. ".\common.ps1" -f # Test all solutions From b4906e94843d9db9f139d1350430c0aa83966bac Mon Sep 17 00:00:00 2001 From: Erol Arkat Date: Fri, 24 Jul 2020 13:42:27 +0300 Subject: [PATCH 70/77] added -f parameter for test-all.ps1 --- build/test-all.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/test-all.ps1 b/build/test-all.ps1 index 9123aee313..9a94ec3760 100644 --- a/build/test-all.ps1 +++ b/build/test-all.ps1 @@ -1,4 +1,6 @@ -. ".\common.ps1" -f +$full = $args[0] + +. ".\common.ps1" $full # Test all solutions From 1a1dc3e0da20e447234350a528ddf818a0639160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 24 Jul 2020 13:55:48 +0300 Subject: [PATCH 71/77] Fix Account.cshtml --- .../Themes/Basic/Layouts/Account.cshtml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml index b4ae34d8fb..5fcd5951cd 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml @@ -39,8 +39,10 @@ @(ViewBag.Title == null ? BrandingProvider.AppName : ViewBag.Title) - - + @if (ViewBag.Description != null) + { + + } @await RenderSectionAsync("styles", false) From 276a48a240a62a53bccba60ba7ef08c0439ca5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 24 Jul 2020 14:49:17 +0300 Subject: [PATCH 72/77] Resolved #4862 Support async object mapping for the base CRUD application service. --- .../Services/AbstractKeyCrudAppService.cs | 39 ++++++++++++++-- .../Services/AbstractKeyReadOnlyAppService.cs | 46 ++++++++++++++++++- .../Application/Services/CrudAppService.cs | 5 ++ 3 files changed, 83 insertions(+), 7 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs index 11550c456f..91879f030e 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Entities; @@ -51,6 +52,11 @@ namespace Volo.Abp.Application.Services } + protected override Task MapToGetListOutputDtoAsync(TEntity entity) + { + return MapToGetOutputDtoAsync(entity); + } + protected override TEntityDto MapToGetListOutputDto(TEntity entity) { return MapToGetOutputDto(entity); @@ -80,13 +86,13 @@ namespace Volo.Abp.Application.Services { await CheckCreatePolicyAsync(); - var entity = MapToEntity(input); + var entity = await MapToEntityAsync(input); TryToSetTenantId(entity); await Repository.InsertAsync(entity, autoSave: true); - return MapToGetOutputDto(entity); + return await MapToGetOutputDtoAsync(entity); } public virtual async Task UpdateAsync(TKey id, TUpdateInput input) @@ -95,10 +101,10 @@ namespace Volo.Abp.Application.Services var entity = await GetEntityByIdAsync(id); //TODO: Check if input has id different than given id and normalize if it's default value, throw ex otherwise - MapToEntity(input, entity); + await MapToEntityAsync(input, entity); await Repository.UpdateAsync(entity, autoSave: true); - return MapToGetOutputDto(entity); + return await MapToGetOutputDtoAsync(entity); } public virtual async Task DeleteAsync(TKey id) @@ -125,6 +131,17 @@ namespace Volo.Abp.Application.Services await CheckPolicyAsync(DeletePolicyName); } + /// + /// Maps to to create a new entity. + /// It uses by default. + /// It can be overriden for custom mapping. + /// Overriding this has higher priority than overriding the + /// + protected virtual Task MapToEntityAsync(TCreateInput createInput) + { + return Task.FromResult(MapToEntity(createInput)); + } + /// /// Maps to to create a new entity. /// It uses by default. @@ -153,6 +170,18 @@ namespace Volo.Abp.Application.Services } } + /// + /// Maps to to update the entity. + /// It uses by default. + /// It can be overriden for custom mapping. + /// Overriding this has higher priority than overriding the + /// + protected virtual Task MapToEntityAsync(TUpdateInput updateInput, TEntity entity) + { + MapToEntity(updateInput, entity); + return Task.CompletedTask; + } + /// /// Maps to to update the entity. /// It uses by default. @@ -190,4 +219,4 @@ namespace Volo.Abp.Application.Services return entity.GetType().GetProperty(nameof(IMultiTenant.TenantId)) != null; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs index 9085434211..5781fb1fa3 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; using System.Threading.Tasks; @@ -6,6 +7,7 @@ using Volo.Abp.Application.Dtos; using Volo.Abp.Auditing; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; +using Volo.Abp.ObjectMapping; namespace Volo.Abp.Application.Services { @@ -52,7 +54,8 @@ namespace Volo.Abp.Application.Services await CheckGetPolicyAsync(); var entity = await GetEntityByIdAsync(id); - return MapToGetOutputDto(entity); + + return await MapToGetOutputDtoAsync(entity); } public virtual async Task> GetListAsync(TGetListInput input) @@ -67,10 +70,11 @@ namespace Volo.Abp.Application.Services query = ApplyPaging(query, input); var entities = await AsyncExecuter.ToListAsync(query); + var entityDtos = await MapToGetListOutputDtosAsync(entities); return new PagedResultDto( totalCount, - entities.Select(MapToGetListOutputDto).ToList() + entityDtos ); } @@ -161,6 +165,17 @@ namespace Volo.Abp.Application.Services return ReadOnlyRepository; } + /// + /// Maps to . + /// It internally calls the by default. + /// It can be overriden for custom mapping. + /// Overriding this has higher priority than overriding the + /// + protected virtual Task MapToGetOutputDtoAsync(TEntity entity) + { + return Task.FromResult(MapToGetOutputDto(entity)); + } + /// /// Maps to . /// It uses by default. @@ -171,6 +186,33 @@ namespace Volo.Abp.Application.Services return ObjectMapper.Map(entity); } + /// + /// Maps a list of to objects. + /// It uses method for each item in the list. + /// + protected virtual async Task> MapToGetListOutputDtosAsync(List entities) + { + var dtos = new List(); + + foreach (var entity in entities) + { + dtos.Add(await MapToGetListOutputDtoAsync(entity)); + } + + return dtos; + } + + /// + /// Maps to . + /// It internally calls the by default. + /// It can be overriden for custom mapping. + /// Overriding this has higher priority than overriding the + /// + protected virtual Task MapToGetListOutputDtoAsync(TEntity entity) + { + return Task.FromResult(MapToGetListOutputDto(entity)); + } + /// /// Maps to . /// It uses by default. diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs index 7832e8a4ab..6c06cf95fe 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs @@ -55,6 +55,11 @@ namespace Volo.Abp.Application.Services } + protected override Task MapToGetListOutputDtoAsync(TEntity entity) + { + return base.MapToGetOutputDtoAsync(entity); + } + protected override TEntityDto MapToGetListOutputDto(TEntity entity) { return MapToGetOutputDto(entity); From 27050189dc6653c851fa99ce191013c600a2bfad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 24 Jul 2020 15:27:04 +0300 Subject: [PATCH 73/77] Check IHasCreationTime instead of ICreationAuditedObject --- .../Abp/Application/Services/AbstractKeyReadOnlyAppService.cs | 4 ++-- .../Volo/Abp/Application/Services/CrudAppService.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs index 5781fb1fa3..b9ffe3359b 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs @@ -122,9 +122,9 @@ namespace Volo.Abp.Application.Services /// The query. protected virtual IQueryable ApplyDefaultSorting(IQueryable query) { - if (typeof(TEntity).IsAssignableTo()) + if (typeof(TEntity).IsAssignableTo()) { - return query.OrderByDescending(e => ((ICreationAuditedObject)e).CreationTime); + return query.OrderByDescending(e => ((IHasCreationTime)e).CreationTime); } throw new AbpException("No sorting specified but this query requires sorting. Override the ApplyDefaultSorting method for your application service derived from AbstractKeyReadOnlyAppService!"); diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs index 6c06cf95fe..db66881fb7 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs @@ -102,9 +102,9 @@ namespace Volo.Abp.Application.Services protected override IQueryable ApplyDefaultSorting(IQueryable query) { - if (typeof(TEntity).IsAssignableTo()) + if (typeof(TEntity).IsAssignableTo()) { - return query.OrderByDescending(e => ((ICreationAuditedObject)e).CreationTime); + return query.OrderByDescending(e => ((IHasCreationTime)e).CreationTime); } else { From 07728499717d5dc4a2cb647a3fa36d2742b056b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 24 Jul 2020 15:27:51 +0300 Subject: [PATCH 74/77] Add more info about the CRUD app services. --- docs/en/Application-Services.md | 78 +++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/docs/en/Application-Services.md b/docs/en/Application-Services.md index d28a7290fb..9cfec614cb 100644 --- a/docs/en/Application-Services.md +++ b/docs/en/Application-Services.md @@ -367,6 +367,84 @@ public class DistrictKey } ```` +### Authorization (for CRUD App Services) + +There are two ways of authorizing the base application service methods; + +1. You can set the policy properties (xxxPolicyName) in the constructor of your service. Example: + +```csharp +public class MyPeopleAppService : CrudAppService +{ + public MyPeopleAppService(IRepository repository) + : base(repository) + { + GetPolicyName = "..."; + GetListPolicyName = "..."; + CreatePolicyName = "..."; + UpdatePolicyName = "..."; + DeletePolicyName = "..."; + } +} +``` + +`CreatePolicyName` is checked by the `CreateAsync` method and so on... You should specify a policy (permission) name defined in your application. + +2. You can override the check methods (CheckXxxPolicyAsync) in your service. Example: + +```csharp +public class MyPeopleAppService : CrudAppService +{ + public MyPeopleAppService(IRepository repository) + : base(repository) + { + } + + protected override async Task CheckDeletePolicyAsync() + { + await AuthorizationService.CheckAsync("..."); + } +} +``` + +You can perform any logic in the `CheckDeletePolicyAsync` method. It is expected to throw an `AbpAuthorizationException` in any unauthorized case, like `AuthorizationService.CheckAsync` already does. + +### Base Properties & Methods + +CRUD application service base class provides many useful base methods that **you can override** to customize it based on your requirements. + +#### CRUD Methods + +These are the essential CRUD methods. You can override any of them to completely customize the operation. Here, the definitions of the methods: + +````csharp +Task GetAsync(TKey id); +Task> GetListAsync(TGetListInput input); +Task CreateAsync(TCreateInput input); +Task UpdateAsync(TKey id, TUpdateInput input); +Task DeleteAsync(TKey id); +```` + +#### Querying + +These methods are low level methods those can be control how to query entities from the database. + +* `CreateFilteredQuery` can be overridden to create an `IQueryable` that is filtered by the given input. If your `TGetListInput` class contains any filter, it is proper to override this method and filter the query. It returns the (unfiltered) repository (which is already `IQueryable`) by default. +* `ApplyPaging` is used to make paging on the query. If your `TGetListInput` already implements `IPagedResultRequest`, you don't need to override this since the ABP Framework automatically understands it and performs the paging. +* `ApplySorting` is used to sort (order by...) the query. If your `TGetListInput` already implements the `ISortedResultRequest`, ABP Framework automatically sorts the query. If not, it fallbacks to the `ApplyDefaultSorting` which tries to sort by creating time, if your entity implements the standard `IHasCreationTime` interface. +* `GetEntityByIdAsync` is used to get an entity by id, which calls `Repository.GetAsync(id)` by default. +* `DeleteByIdAsync` is used to delete an entity by id, which calls `Repository.DeleteAsync(id)` by default. + +#### Object to Object Mapping + +These methods are used to convert Entities to DTOs and vice verse. They uses the [IObjectMapper](Object-To-Object-Mapping.md) by default. + +* `MapToGetOutputDtoAsync` is used to map the entity to the DTO returned from the `GetAsync`, `CreateAsync` and `UpdateAsync` methods. Alternatively, you can override the `MapToGetOutputDto` if you don't need to perform any async operation. +* `MapToGetListOutputDtosAsync` is used to map a list of entities to a list of DTOs returned from the `GetListAsync` method. It uses the `MapToGetListOutputDtoAsync` to map each entity in the list. You can override one of them based on your case. Alternatively, you can override the `MapToGetListOutputDto` if you don't need to perform any async operation. +* `MapToEntityAsync` method has two overloads; + * `MapToEntityAsync(TCreateInput)` is used to create an entity from `TCreateInput`. + * `MapToEntityAsync(TUpdateInput, TEntity)` is used to update an existing entity from `TUpdateInput`. + ## Lifetime Lifetime of application services are [transient](Dependency-Injection.md) and they are automatically registered to the dependency injection system. From f5ab58632a5fab56221af36c08edc563e9a5c17b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 24 Jul 2020 17:33:21 +0300 Subject: [PATCH 75/77] Fix link. --- docs/en/Tutorials/Part-2.md | 2 +- docs/en/UI/AspNetCore/Dynamic-JavaScript-Proxies.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 docs/en/UI/AspNetCore/Dynamic-JavaScript-Proxies.md diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 32f54c1d04..1f3b6c84d1 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -56,7 +56,7 @@ This tutorials has multiple versions based on your **UI** and **Database** prefe It's common to call the HTTP API endpoints via AJAX from the **JavaScript** side. You can use `$.ajax` or another tool to call the endpoints. However, ABP offers a better way. -ABP **dynamically** creates **[JavaScript Proxies](../UI/AspNetCore/)** for all API endpoints. So, you can use any **endpoint** just like calling a **JavaScript function**. +ABP **dynamically** creates **[JavaScript Proxies](../UI/AspNetCore/Dynamic-JavaScript-Proxies.md)** for all API endpoints. So, you can use any **endpoint** just like calling a **JavaScript function**. ### Testing in the Developer Console diff --git a/docs/en/UI/AspNetCore/Dynamic-JavaScript-Proxies.md b/docs/en/UI/AspNetCore/Dynamic-JavaScript-Proxies.md new file mode 100644 index 0000000000..391a63a910 --- /dev/null +++ b/docs/en/UI/AspNetCore/Dynamic-JavaScript-Proxies.md @@ -0,0 +1,3 @@ +# Dynamic JavaScript HTTP API Proxies + +TODO \ No newline at end of file From c2b1cb9c5b0417b7471bf44195581c041359b663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 26 Jul 2020 17:49:12 +0300 Subject: [PATCH 76/77] Revert #4630: Remove AutoMapper to the Application.Contracts project. --- ...nyName.MyProjectName.Application.Contracts.csproj | 1 - .../MyProjectNameApplicationContractsModule.cs | 12 +----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj index f9c3f9a045..fe2e40eb14 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj @@ -13,7 +13,6 @@ - diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyProjectNameApplicationContractsModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyProjectNameApplicationContractsModule.cs index d8e848e86b..5c3ba41364 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyProjectNameApplicationContractsModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyProjectNameApplicationContractsModule.cs @@ -1,5 +1,4 @@ using Volo.Abp.Account; -using Volo.Abp.AutoMapper; using Volo.Abp.FeatureManagement; using Volo.Abp.Identity; using Volo.Abp.Modularity; @@ -16,8 +15,7 @@ namespace MyCompanyName.MyProjectName typeof(AbpIdentityApplicationContractsModule), typeof(AbpPermissionManagementApplicationContractsModule), typeof(AbpTenantManagementApplicationContractsModule), - typeof(AbpObjectExtendingModule), - typeof(AbpAutoMapperModule) + typeof(AbpObjectExtendingModule) )] public class MyProjectNameApplicationContractsModule : AbpModule { @@ -25,13 +23,5 @@ namespace MyCompanyName.MyProjectName { MyProjectNameDtoExtensions.Configure(); } - - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.AddMaps(); - }); - } } } From f2a722314e7b9aaf0d109bca33493b14fefbe331 Mon Sep 17 00:00:00 2001 From: maliming <6908465+maliming@users.noreply.github.com> Date: Mon, 27 Jul 2020 14:28:25 +0800 Subject: [PATCH 77/77] Update Part-3.md --- docs/zh-Hans/Tutorials/Part-3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh-Hans/Tutorials/Part-3.md b/docs/zh-Hans/Tutorials/Part-3.md index cded954e6b..c9eb3bac78 100644 --- a/docs/zh-Hans/Tutorials/Part-3.md +++ b/docs/zh-Hans/Tutorials/Part-3.md @@ -23,7 +23,7 @@ end ### 关于本教程 -这是ASP.NET Core{{UI_Value}}系列教程的第二章. 共有三章: +这是ASP.NET Core{{UI_Value}}系列教程的第三章. 共有三章: - [Part-1: 创建项目和书籍列表页面](Part-1.md) - [Part 2: 创建,编辑,删除书籍](Part-2.md)