diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo.Abp.AspNetCore.Mvc.Client.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo.Abp.AspNetCore.Mvc.Client.csproj index 911782697b..9d05c9e34b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo.Abp.AspNetCore.Mvc.Client.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo.Abp.AspNetCore.Mvc.Client.csproj @@ -17,6 +17,7 @@ + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs index 3f1ed47435..6c306073c5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Caching; using Volo.Abp.Http.Client; +using Volo.Abp.Localization; using Volo.Abp.Modularity; namespace Volo.Abp.AspNetCore.Mvc.Client @@ -8,7 +9,8 @@ namespace Volo.Abp.AspNetCore.Mvc.Client [DependsOn( typeof(AbpHttpClientModule), typeof(AbpAspNetCoreMvcContractsModule), - typeof(AbpCachingModule) + typeof(AbpCachingModule), + typeof(AbpLocalizationModule) )] public class AbpAspNetCoreMvcClientModule : AbpModule { @@ -21,6 +23,11 @@ namespace Volo.Abp.AspNetCore.Mvc.Client RemoteServiceName, asDefaultServices: false ); + + Configure(options => + { + options.GlobalContributors.Add(); + }); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClient.cs index f7df64b257..4ceb8bdd1c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClient.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClient.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using Microsoft.AspNetCore.Http; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Distributed; @@ -45,7 +46,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Client async () => await Proxy.Service.GetAsync(), () => new DistributedCacheEntryOptions { - AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5) + AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(60) //TODO: Should be configurable. Default value should be higher (5 mins would be good). } ); @@ -59,7 +60,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Client protected virtual string CreateCacheKey() { - return $"ApplicationConfiguration_{CurrentUser.Id?.ToString("N") ?? "Anonymous"}"; + return $"ApplicationConfiguration_{CurrentUser.Id?.ToString("N") ?? "Anonymous"}_{CultureInfo.CurrentUICulture.Name}"; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClientExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClientExtensions.cs new file mode 100644 index 0000000000..601a2d1088 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClientExtensions.cs @@ -0,0 +1,13 @@ +using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; +using Volo.Abp.Threading; + +namespace Volo.Abp.AspNetCore.Mvc.Client +{ + public static class CachedApplicationConfigurationClientExtensions + { + public static ApplicationConfigurationDto Get(this ICachedApplicationConfigurationClient client) + { + return AsyncHelper.RunSync(client.GetAsync); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemoteLocalizationContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemoteLocalizationContributor.cs new file mode 100644 index 0000000000..d994a84335 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemoteLocalizationContributor.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Localization; + +namespace Volo.Abp.AspNetCore.Mvc.Client +{ + public class RemoteLocalizationContributor : ILocalizationResourceContributor + { + private LocalizationResource _resource; + private ICachedApplicationConfigurationClient _applicationConfigurationClient; + private ILogger _logger; + + public void Initialize(LocalizationResourceInitializationContext context) + { + _resource = context.Resource; + _applicationConfigurationClient = context.ServiceProvider.GetRequiredService(); + _logger = context.ServiceProvider.GetService>() + ?? NullLogger.Instance; + } + + public LocalizedString GetOrNull(string cultureName, string name) + { + var resource = GetResourceOrNull(); + if (resource == null) + { + return null; + } + + var value = resource.GetOrDefault(name); + if (value == null) + { + return null; + } + + return new LocalizedString(name, value); + } + + public void Fill(string cultureName, Dictionary dictionary) + { + var resource = GetResourceOrNull(); + if (resource == null) + { + return; + } + + foreach (var keyValue in resource) + { + dictionary[keyValue.Key] = new LocalizedString(keyValue.Key, keyValue.Value); + } + } + + private Dictionary GetResourceOrNull() + { + var resource = _applicationConfigurationClient + .Get() + .Localization.Values + .GetOrDefault(_resource.ResourceName); + + if (resource == null) + { + _logger.LogWarning($"Could not find the localization resource {_resource.ResourceName} on the remote server!"); + } + + return resource; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemoteSettingProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemoteSettingProvider.cs new file mode 100644 index 0000000000..db3ea6e9a6 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemoteSettingProvider.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Settings; + +namespace Volo.Abp.AspNetCore.Mvc.Client +{ + public class RemoteSettingProvider : ISettingProvider, ITransientDependency + { + protected ICachedApplicationConfigurationClient ConfigurationClient { get; } + + public RemoteSettingProvider(ICachedApplicationConfigurationClient configurationClient) + { + ConfigurationClient = configurationClient; + } + + public async Task GetOrNullAsync(string name) + { + var configuration = await ConfigurationClient.GetAsync(); + return configuration.Setting.Values.GetOrDefault(name); + } + + public async Task> GetAllAsync() + { + var configuration = await ConfigurationClient.GetAsync(); + return configuration + .Setting.Values + .Select(s => new SettingValue(s.Key, s.Value)) + .ToList(); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs index ecafcb3274..6f41399c29 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs @@ -9,6 +9,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations public ApplicationAuthConfigurationDto Auth { get; set; } + public ApplicationSettingConfigurationDto Setting { get; set; } + public CurrentUserDto CurrentUser { get; set; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationSettingConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationSettingConfigurationDto.cs new file mode 100644 index 0000000000..40cbd89fd1 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationSettingConfigurationDto.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +{ + [Serializable] + public class ApplicationSettingConfigurationDto + { + public Dictionary Values { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperService.cs index ce47163be0..566cbcffbc 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperService.cs @@ -56,93 +56,5 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers Process(context, output); return Task.CompletedTask; } - - protected virtual TagHelperOutput GetInnerTagHelper(TagHelperAttributeList attributeList, TagHelperContext context, TagHelper tagHelper, string tagName = "div", TagMode tagMode = TagMode.SelfClosing, bool runAsync = false) - { - var innerOutput = new TagHelperOutput(tagName, attributeList, (useCachedResult, encoder) => Task.Run(() => new DefaultTagHelperContent())) - { - TagMode = tagMode - }; - - var innerContext = new TagHelperContext(attributeList, context.Items, Guid.NewGuid().ToString()); - - tagHelper.Init(context); - - if (runAsync) - { - AsyncHelper.RunSync(() => tagHelper.ProcessAsync(innerContext, innerOutput)); - } - else - { - tagHelper.Process(innerContext, innerOutput); - } - - return innerOutput; - } - - protected virtual string RenderTagHelper(TagHelperAttributeList attributeList, TagHelperContext context, TagHelper tagHelper, HtmlEncoder htmlEncoder, string tagName = "div", TagMode tagMode = TagMode.SelfClosing, bool runAsync = false) - { - var innerOutput = GetInnerTagHelper(attributeList, context, tagHelper, tagName, tagMode, runAsync); - - return RenderTagHelperOutput(innerOutput, htmlEncoder); - } - - protected virtual string RenderTagHelperOutput(TagHelperOutput output, HtmlEncoder htmlEncoder) - { - using (var writer = new StringWriter()) - { - output.WriteTo(writer, htmlEncoder); - return writer.ToString(); - } - } - - protected virtual T GetAttribute(ModelExplorer property) where T : Attribute - { - return property?.Metadata?.ContainerType?.GetTypeInfo()?.GetProperty(property.Metadata.PropertyName)?.GetCustomAttribute(); - } - - protected virtual List GetFormGroupContentsList(TagHelperContext context, out bool surpress) - { - var items = GetValueFromContext>(context, FormGroupContents); - surpress = items != null; - - return items ?? new List(); - } - - protected virtual T GetValueFromContext(TagHelperContext context, string key) - { - if (!context.Items.ContainsKey(key)) - { - return default(T); - } - - return (T)context.Items[key]; - } - - protected virtual string GetIdAttributeAsString(TagHelperOutput inputTag) - { - var idAttr = inputTag.Attributes.FirstOrDefault(a => a.Name == "id"); - - return idAttr != null ? "for=\"" + idAttr.Value + "\"" : ""; - } - - protected virtual int GetInputOrder(ModelExplorer explorer) - { - return GetAttribute(explorer)?.Number ?? DisplayOrder.Default; - } - - protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool surpress) - { - var list = GetFormGroupContentsList(context, out surpress); - - if (list != null && !list.Any(igc => igc.HtmlContent.Contains("id=\"" + propertyName.Replace('.', '_') + "\""))) - { - list.Add(new FormGroupItem - { - HtmlContent = html, - Order = order - }); - } - } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs index c19ac3acd8..1d6204373a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs @@ -2,6 +2,7 @@ using System.Text.Encodings.Web; using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb { @@ -20,13 +21,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb output.Attributes.AddClass("breadcrumb-item"); output.Attributes.AddClass(AbpBreadcrumbItemActivePlaceholder); - var list = GetValueFromContext>(context, BreadcrumbItemsContent); + var list = context.GetValue>(BreadcrumbItemsContent); output.Content.SetHtmlContent(GetInnerHtml(context, output)); list.Add(new BreadcrumbItem { - Html = RenderTagHelperOutput(output, _encoder), + Html = output.Render(_encoder), Active = TagHelper.Active }); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselItemTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselItemTagHelperService.cs index 9b44254f17..5a4bafd47d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselItemTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselItemTagHelperService.cs @@ -3,6 +3,7 @@ using System.Text; using System.Text.Encodings.Web; using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel { @@ -32,9 +33,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel private void AddToContext(TagHelperContext context, TagHelperOutput output) { - var getOutputAsHtml = RenderTagHelperOutput(output, _encoder); + var getOutputAsHtml = output.Render(_encoder); - var itemList = GetValueFromContext>(context, CarouselItemsContent); + var itemList = context.GetValue>(CarouselItemsContent); itemList.Add(new CarouselItem(getOutputAsHtml, TagHelper.Active ?? false)); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelperService.cs index 1f9f57c567..9dc279334b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelperService.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.TagHelpers; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse { @@ -15,7 +16,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse var html = GetAccordionHeaderItem(context, output) + GetAccordionContentItem(context, output, innerContent); - var tabHeaderItems = GetValueFromContext>(context, AccordionItems); + var tabHeaderItems = context.GetValue>(AccordionItems); tabHeaderItems.Add(html); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownButtonTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownButtonTagHelperService.cs index 8b01781602..ee17262d96 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownButtonTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownButtonTagHelperService.cs @@ -1,10 +1,12 @@ using System; using System.Text; using System.Text.Encodings.Web; +using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown { @@ -22,22 +24,25 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown _serviceProvider = serviceProvider; } - public override void Process(TagHelperContext context, TagHelperOutput output) + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { - var buttonsAsHtml = GetButtonsAsHtml(context, output); + var content = await output.GetChildContentAsync(); + + var buttonsAsHtml = GetButtonsAsHtml(context, output, content); output.PreElement.SetHtmlContent(buttonsAsHtml); output.TagName = "div"; output.TagMode = TagMode.StartTagAndEndTag; + output.Content.SetContent(""); output.Attributes.Clear(); } - protected virtual string GetButtonsAsHtml(TagHelperContext context, TagHelperOutput output) + protected virtual string GetButtonsAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperContent content) { var buttonBuilder = new StringBuilder(""); - var mainButton = GetMainButton(context, output); + var mainButton = GetMainButton(context, output, content); buttonBuilder.AppendLine(mainButton); @@ -51,10 +56,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown return buttonBuilder.ToString(); } - protected virtual string GetMainButton(TagHelperContext context, TagHelperOutput output) + protected virtual string GetMainButton(TagHelperContext context, TagHelperOutput output, TagHelperContent content) { var abpButtonTagHelper = _serviceProvider.GetRequiredService(); - + abpButtonTagHelper.Icon = TagHelper.Icon; abpButtonTagHelper.Text = TagHelper.Text; abpButtonTagHelper.IconType = TagHelper.IconType; @@ -62,15 +67,17 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown abpButtonTagHelper.ButtonType = TagHelper.ButtonType; var attributes = GetAttributesForMainButton(context, output); - var buttonTag = GetInnerTagHelper(attributes, context, abpButtonTagHelper, "button", TagMode.StartTagAndEndTag); + var buttonTag = abpButtonTagHelper.ProcessAndGetOutput(attributes, context, "button", TagMode.StartTagAndEndTag); + + buttonTag.PreContent.SetHtmlContent(content.GetContent()); if ((TagHelper.NavLink ?? false) || (TagHelper.Link ?? false)) { var linkTag = ConvertButtonToLink(buttonTag); - return RenderTagHelperOutput(linkTag, _htmlEncoder); + return linkTag.Render(_htmlEncoder); } - return RenderTagHelperOutput(buttonTag, _htmlEncoder); + return buttonTag.Render(_htmlEncoder); } protected virtual string GetSplitButton(TagHelperContext context, TagHelperOutput output) @@ -81,7 +88,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown abpButtonTagHelper.ButtonType = TagHelper.ButtonType; var attributes = GetAttributesForSplitButton(context, output); - return RenderTagHelper(attributes, context, abpButtonTagHelper, _htmlEncoder, "button", TagMode.StartTagAndEndTag); + return abpButtonTagHelper.Render(attributes, context, _htmlEncoder, "button", TagMode.StartTagAndEndTag); } protected virtual TagHelperAttributeList GetAttributesForMainButton(TagHelperContext context, TagHelperOutput output) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownTagHelperService.cs index 945221ff2d..8f37fca5ee 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Dropdown/AbpDropdownTagHelperService.cs @@ -8,6 +8,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown public override void Process(TagHelperContext context, TagHelperOutput output) { output.TagName = "div"; + output.Attributes.AddClass("dropdown"); output.Attributes.AddClass("btn-group"); SetDirection(context, output); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/ModelExplorerExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/ModelExplorerExtensions.cs new file mode 100644 index 0000000000..b6bedac11b --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/ModelExplorerExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Reflection; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions +{ + public static class ModelExplorerExtensions + { + public static T GetAttribute(this ModelExplorer property) where T : Attribute + { + return property?.Metadata?.ContainerType?.GetTypeInfo()?.GetProperty(property.Metadata.PropertyName)?.GetCustomAttribute(); + } + + public static int GetDisplayOrder(this ModelExplorer explorer) + { + return GetAttribute(explorer)?.Number ?? DisplayOrder.Default; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperContextExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperContextExtensions.cs new file mode 100644 index 0000000000..c2b3e41192 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperContextExtensions.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions +{ + public static class TagHelperContextExtensions + { + public static T GetValue(this TagHelperContext context, string key) + { + if (!context.Items.ContainsKey(key)) + { + return default(T); + } + + return (T)context.Items[key]; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs new file mode 100644 index 0000000000..ee984474d2 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs @@ -0,0 +1,41 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.TagHelpers; +using System.Text.Encodings.Web; +using Volo.Abp.Threading; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions +{ + public static class TagHelperExtensions + { + public static TagHelperOutput ProcessAndGetOutput(this TagHelper tagHelper, TagHelperAttributeList attributeList, TagHelperContext context, string tagName = "div", TagMode tagMode = TagMode.SelfClosing, bool runAsync = false) + { + var innerOutput = new TagHelperOutput(tagName, attributeList, (useCachedResult, encoder) => Task.Run(() => new DefaultTagHelperContent())) + { + TagMode = tagMode + }; + + var innerContext = new TagHelperContext(attributeList, context.Items, Guid.NewGuid().ToString()); + + tagHelper.Init(context); + + if (runAsync) + { + AsyncHelper.RunSync(() => tagHelper.ProcessAsync(innerContext, innerOutput)); + } + else + { + tagHelper.Process(innerContext, innerOutput); + } + + return innerOutput; + } + + public static string Render(this TagHelper tagHelper, TagHelperAttributeList attributeList, TagHelperContext context, HtmlEncoder htmlEncoder, string tagName = "div", TagMode tagMode = TagMode.SelfClosing, bool runAsync = false) + { + var innerOutput = tagHelper.ProcessAndGetOutput(attributeList, context, tagName, tagMode, runAsync); + + return innerOutput.Render(htmlEncoder); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperOutputExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperOutputExtensions.cs new file mode 100644 index 0000000000..0cc9bae6e2 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperOutputExtensions.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using System.IO; +using System.Text.Encodings.Web; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions +{ + public static class TagHelperOutputExtensions + { + public static string Render(this TagHelperOutput output, HtmlEncoder htmlEncoder) + { + using (var writer = new StringWriter()) + { + output.WriteTo(writer, htmlEncoder); + return writer.ToString(); + } + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs index fa3ec9cce6..a9f3bb9c62 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { @@ -66,7 +67,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form ViewContext = TagHelper.ViewContext }; - var formTagOutput = GetInnerTagHelper(output.Attributes, context, formTagHelper, "form", TagMode.StartTagAndEndTag); + var formTagOutput = formTagHelper.ProcessAndGetOutput(output.Attributes, context, "form", TagMode.StartTagAndEndTag); await formTagOutput.GetChildContentAsync(); @@ -146,7 +147,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { var abpSelectTagHelper = GetSelectGroupTagHelper(context, output, model); - RenderTagHelper(new TagHelperAttributeList(), context, abpSelectTagHelper, _htmlEncoder, "div", TagMode.StartTagAndEndTag); + abpSelectTagHelper.Render(new TagHelperAttributeList(), context, _htmlEncoder, "div", TagMode.StartTagAndEndTag); } protected virtual AbpTagHelper GetSelectGroupTagHelper(TagHelperContext context, TagHelperOutput output, ModelExpression model) @@ -156,7 +157,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form GetSelectTagHelper(model); } - private AbpTagHelper GetSelectTagHelper(ModelExpression model) + protected virtual AbpTagHelper GetSelectTagHelper(ModelExpression model) { var abpSelectTagHelper = _serviceProvider.GetRequiredService(); abpSelectTagHelper.AspFor = model; @@ -165,9 +166,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return abpSelectTagHelper; } - private AbpTagHelper GetAbpRadioInputTagHelper(ModelExpression model) + protected virtual AbpTagHelper GetAbpRadioInputTagHelper(ModelExpression model) { - var radioButtonAttribute = GetAttribute(model.ModelExplorer); + var radioButtonAttribute = model.ModelExplorer.GetAttribute(); var abpRadioInputTagHelper = _serviceProvider.GetRequiredService(); abpRadioInputTagHelper.AspFor = model; abpRadioInputTagHelper.AspItems = null; @@ -184,7 +185,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form abpButtonTagHelper.Text = "Submit"; abpButtonTagHelper.ButtonType = AbpButtonType.Primary; - return RenderTagHelper(attributes, context, abpButtonTagHelper, _htmlEncoder, "button", TagMode.StartTagAndEndTag); + return abpButtonTagHelper.Render(attributes, context, _htmlEncoder, "button", TagMode.StartTagAndEndTag); } protected virtual void ProcessInputGroup(TagHelperContext context, TagHelperOutput output, ModelExpression model) @@ -194,7 +195,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form abpInputTagHelper.ViewContext = TagHelper.ViewContext; abpInputTagHelper.DisplayRequiredSymbol = TagHelper.RequiredSymbols ?? true; - RenderTagHelper(new TagHelperAttributeList(), context, abpInputTagHelper, _htmlEncoder, "div", TagMode.StartTagAndEndTag); + abpInputTagHelper.Render(new TagHelperAttributeList(), context, _htmlEncoder, "div", TagMode.StartTagAndEndTag); } protected virtual List GetModels(TagHelperContext context, TagHelperOutput output) @@ -280,12 +281,12 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual bool AreSelectItemsProvided(ModelExplorer explorer) { - return GetAttribute(explorer) != null; + return explorer.GetAttribute() != null; } protected virtual bool IsRadioGroup(ModelExplorer explorer) { - return GetAttribute(explorer) != null; + return explorer.GetAttribute() != null; } } } \ No newline at end of file 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 bc48d7b1d8..fa16acbd93 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 @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text.Encodings.Web; @@ -6,6 +7,7 @@ using Microsoft.AspNetCore.Mvc.TagHelpers; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { @@ -26,7 +28,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { var innerHtml = GetFormInputGroupAsHtml(context, output, out var isCheckbox); - var order = GetInputOrder(TagHelper.AspFor.ModelExplorer); + var order = TagHelper.AspFor.ModelExplorer.GetDisplayOrder(); AddGroupToFormGroupContents( context, @@ -45,7 +47,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form output.TagMode = TagMode.StartTagAndEndTag; output.TagName = "div"; LeaveOnlyGroupAttributes(context, output); - output.Attributes.AddClass(isCheckbox ? "form-check" : "form-group"); + output.Attributes.AddClass(isCheckbox ? "custom-checkbox" : "form-group"); + output.Attributes.AddClass(isCheckbox ? "custom-control" : ""); output.Attributes.AddClass(isCheckbox ? "mb-2" : ""); output.Content.SetHtmlContent(output.Content.GetContent() + innerHtml); } @@ -54,8 +57,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual string GetFormInputGroupAsHtml(TagHelperContext context, TagHelperOutput output, out bool isCheckbox) { var inputTag = GetInputTagHelperOutput(context, output, out isCheckbox); - - var inputHtml = RenderTagHelperOutput(inputTag, _encoder); + + var inputHtml = inputTag.Render(_encoder); var label = GetLabelAsHtml(context, output, inputTag, isCheckbox); var info = GetInfoAsHtml(context, output, inputTag, isCheckbox); var validation = isCheckbox ? "" : GetValidationAsHtml(context, output, inputTag); @@ -78,7 +81,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form var attributeList = new TagHelperAttributeList { { "class", "text-danger" } }; - return RenderTagHelper(attributeList, context, validationMessageTagHelper, _encoder, "span", TagMode.StartTagAndEndTag, true); + return validationMessageTagHelper.Render(attributeList, context, _encoder, "span", TagMode.StartTagAndEndTag, true); } protected virtual string GetContent(TagHelperContext context, TagHelperOutput output, string label, string inputHtml, string validation, string infoHtml, bool isCheckbox) @@ -92,14 +95,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual string SurroundInnerHtmlAndGet(TagHelperContext context, TagHelperOutput output, string innerHtml, bool isCheckbox) { - return "
" + + return "
" + Environment.NewLine + innerHtml + Environment.NewLine + "
"; } protected virtual TagHelper GetInputTagHelper(TagHelperContext context, TagHelperOutput output) { - var textAreaAttribute = GetAttribute