Open Closed

First Post and need help with commercial modules #4825


User avatar
0
Navneet@aol.com.au created

Check the docs before asking a question: https://docs.abp.io/en/commercial/latest/ Check the samples, to see the basic tasks: https://docs.abp.io/en/commercial/latest/samples/index The exact solution to your question may have been answered before, please use the search on the homepage.

If you're creating a bug/problem report, please include followings:

  • ABP Framework version: v7.0.3
  • UI type: Angular / MVC / Blazor
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): no
  • Exception message and stack trace:
  • Steps to reproduce the issue:"

Hello support team and other ABP users,

I have tried my level best to first search below issues but could not find solutions, I am a new developer so if any question is silly then sorry

Could you please help me with below:

  1. After I create a new Module via ABP Suite with UI, the Suite create all necessary CRUD pages, model, DTO and repositories, however when I change anything manually, for example N-2-N relationship it creates only one side of UI and I manually create another side, the suite change the code back to default if I re-run the same entity generation. I believe I can get around by creating partial class, but as suite create a lot of classes in domain, efcore, application, applicationcontracts... I end up some errors with referring partial classes in repositories. IS it ok if you can provide a sample of book entity created via suite and using partial class. I believe that will be helpful for other community member looking for similar solution.

  2. When I create a new application via suite and add Volo.Docs and Volo.Payment via suite, they are not part of Feature under Editions settings, but both are available under Role --> Permissions, can you please help me, how can I add them to Edition --> Features (can you please suggest any code I can override related class instead of changing source code, as I don’t have access to source code dueto my license)

  3. Regarding ABP.Volo.Payment, why tenant has permission to create plan or update plan by default. I am just trying to understand what is the logic to allow tenant to change plans created by host. Is it to allow my tenants to set their own payment Payment Gateway so my tenant can issue their own invoice and received payment from their customers, like B2B2C

  4. As I don’t have access to all commercial sourcecode due to license level, how can I override to make changes to base class, please code sample of only three, rest of classes I will figureout Replace OpenIddictApplication.cs with myOpenIddictApplication.cs Replace OpenIddictDbContextModelCreatingExtensions.cs with myOpenIddictDbContextModelCreatingExtensions.cs last, I want to replace AbpOpenIddictProPermissionDefinitionProvider.cs with my myAbpOpenIddictProPermissionDefinitionProvider

  5. When I publish my project, I noticed that appsettings.secrets.json is also in published folder, is it safe to give it to my client while hand overing the finished Application

  6. When I create a MVC Application with public page & CMS (identity & HostApi not separate), the public page does not use API but use database directly, how can I make it use API instead of interacting with DB directly (please note, I don’t want to have separate Identity & HostApi)

  7. MVC public page, in responsive mode, I don’t see login button, it’s hidden, can you please help me, how can I fix it? Screenshot attached:

Is there anyplace like GitHub, can I read Commercial Module change log for example - CRMpro module changes from 7.0.1 to 7.0.3, this will help me to plan before migrating to next version.

Please let me know if anything not clear and I will try to give more details Regards, Navneet


57 Answer(s)
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    1

    You can customize the code templates to add partial keyword. https://docs.abp.io/en/commercial/latest/abp-suite/editing-templates

    2

    Because there is no such feature definition, you can add feature definitions in your own FeatureDefinitionProvider: https://docs.abp.io/en/abp/latest/Features#featuredefinitionprovider And hide the menu&icon if the feature is not enabled.

    4

    Replace OpenIddictApplication.cs with myOpenIddictApplication.cs Replace OpenIddictDbContextModelCreatingExtensions.cs with myOpenIddictDbContextModelCreatingExtensions.cs

    You can't replace the OpenIddictApplication entity with your own. you can consider using the object extensions system: https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities

    last, I want to replace AbpOpenIddictProPermissionDefinitionProvider.cs with my myAbpOpenIddictProPermissionDefinitionProvider

    Configure<AbpPermissionOptions>(options =>
    {
        options.DefinitionProviders.Remove<AbpOpenIddictProPermissionDefinitionProvider>();
    });
    

    5

    It's necessary, and it's safe.

    6

    You can create a separate project and copycat the public project to your solution.

    7

    public class MyToolbarContributor : IToolbarContributor
    {
        public virtual Task ConfigureToolbarAsync(IToolbarConfigurationContext context)
        {
           
            if (!context.ServiceProvider.GetRequiredService<ICurrentUser>().IsAuthenticated)
            {
                context.Toolbar.Items.Add(new ToolbarItem(typeof(LoginLinkViewComponent)));
            }
    
            return Task.CompletedTask;
        }
    }
    
    Configure<AbpToolbarOptions>(options =>
    {
        options.Contributors.Add(new MyToolbarContributor());
    });
    
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Is there anyplace like GitHub, can I read Commercial Module change log for example - CRMpro module changes from 7.0.1 to 7.0.3, this will help me to plan before migrating to next version.

    https://docs.abp.io/en/commercial/latest/release-notes https://docs.abp.io/en/commercial/latest/change-logs/index

  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    3

    Regarding ABP.Volo.Payment, why tenant has permission to create plan or update plan by default. I am just trying to understand what is the logic to allow tenant to change plans created by host. Is it to allow my tenants to set their own payment Payment Gateway so my tenant can issue their own invoice and received payment from their customers, like B2B2C

    It seems there is a logical issue in there, we'll try to enhance this logic in the next version but you can use the following workaround in your application for now;

    • Create a PermissionDefinition provider in your project. (Applcation.Contracts prefferred)

      And set multitenantcy side as Host for each permission in the group.

      public class PaymentFixPermissionDefinitionProvider : PermissionDefinitionProvider
      {
          public override void Define(IPermissionDefinitionContext context)
          {
              var paymentAdminGroup = context.GetGroupOrNull(PaymentAdminPermissions.GroupName);
              if (paymentAdminGroup != null)
              {
                  foreach (var permissionDefinition in paymentAdminGroup.Permissions)
                  {
                      ConvertToHostSide(permissionDefinition);
                  }
              }
          }
      
          private void ConvertToHostSide(PermissionDefinition permissionDefinition)
          {
              permissionDefinition.MultiTenancySide = Abp.MultiTenancy.MultiTenancySides.Host;
      
              foreach(var childPermissionDefinition in permissionDefinition.Children) 
              {
                  ConvertToHostSide(childPermissionDefinition);
              }
          }
      }
      
  • User Avatar
    0
    Navneet@aol.com.au created

    3

    Regarding ABP.Volo.Payment, why tenant has permission to create plan or update plan by default. I am just trying to understand what is the logic to allow tenant to change plans created by host. Is it to allow my tenants to set their own payment Payment Gateway so my tenant can issue their own invoice and received payment from their customers, like B2B2C

    It seems there is a logical issue in there, we'll try to enhance this logic in the next version but you can use the following workaround in your application for now;

    • Create a PermissionDefinition provider in your project. (Applcation.Contracts prefferred)

      And set multitenantcy side as Host for each permission in the group.

      public class PaymentFixPermissionDefinitionProvider : PermissionDefinitionProvider 
      { 
          public override void Define(IPermissionDefinitionContext context) 
          { 
              var paymentAdminGroup = context.GetGroupOrNull(PaymentAdminPermissions.GroupName); 
              if (paymentAdminGroup != null) 
              { 
                  foreach (var permissionDefinition in paymentAdminGroup.Permissions) 
                  { 
                      ConvertToHostSide(permissionDefinition); 
                  } 
              } 
          } 
      
          private void ConvertToHostSide(PermissionDefinition permissionDefinition) 
          { 
              permissionDefinition.MultiTenancySide = Abp.MultiTenancy.MultiTenancySides.Host; 
      
              foreach(var childPermissionDefinition in permissionDefinition.Children)  
              { 
                  ConvertToHostSide(childPermissionDefinition); 
              } 
          } 
      } 
      

    Hi Enisn,

    So, does it mean that It's only for Host purpose not Tenant purpose?

    Regards, Navneet

  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    Currently, tenants can't configure their own payment gateway configurations by default, all the configurations are made in appsettings.json, so tenants can't use different payment accounts (different stripe, payu accaounts etc.), so there is no meaning adding new productId by tenants. So, converting that permission for host only is a better approach over this condition. We'll make some overhauls on this in the next version.

  • User Avatar
    0
    Navneet@aol.com.au created

    1

    You can customize the code templates to add partial keyword. https://docs.abp.io/en/commercial/latest/abp-suite/editing-templates

    2

    Because there is no such feature definition, you can add feature definitions in your own FeatureDefinitionProvider: https://docs.abp.io/en/abp/latest/Features#featuredefinitionprovider And hide the menu&icon if the feature is not enabled.

    4

    Replace OpenIddictApplication.cs with myOpenIddictApplication.cs Replace OpenIddictDbContextModelCreatingExtensions.cs with myOpenIddictDbContextModelCreatingExtensions.cs

    You can't replace the OpenIddictApplication entity with your own. you can consider using the object extensions system: https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities

    last, I want to replace AbpOpenIddictProPermissionDefinitionProvider.cs with my myAbpOpenIddictProPermissionDefinitionProvider

    Configure<AbpPermissionOptions>(options => 
    { 
        options.DefinitionProviders.Remove<AbpOpenIddictProPermissionDefinitionProvider>(); 
    }); 
    

    5

    It's necessary, and it's safe.

    6

    You can create a separate project and copycat the public project to your solution.

    7

    public class MyToolbarContributor : IToolbarContributor 
    { 
        public virtual Task ConfigureToolbarAsync(IToolbarConfigurationContext context) 
        { 
            
            if (!context.ServiceProvider.GetRequiredService<ICurrentUser>().IsAuthenticated) 
            { 
                context.Toolbar.Items.Add(new ToolbarItem(typeof(LoginLinkViewComponent))); 
            } 
     
            return Task.CompletedTask; 
        } 
    } 
     
    Configure<AbpToolbarOptions>(options => 
    { 
        options.Contributors.Add(new MyToolbarContributor()); 
    }); 
    

    Thanks liangshiwei

    For point 1: Your method will apply to project wide, I have only three entity which I want to change generated by suite, can you please give me some sample code for entity “Book” and “Author” using partial class and using N-N navigation, I will use that sample to replicate in my project for other class.

    For point 2: Thanks, I manage to create feature with below code, how to wireup with module so that I can control from FeatureManagement, by hiding icon & menu it does not disable if someone try to navigate manually.

    Also, how can I show/hide in Roles --> Permission based on Feature is True/False

        public class DocPaymentDefinitionProvider : FeatureDefinitionProvider
        {
            public override void Define(IFeatureDefinitionContext context)
            {
                var myDocs = context.AddGroup("Docs");
                myDocs.AddFeature("Docs.Allow", defaultValue: "false");
    
                var myPayment = context.AddGroup("Payment");
                myPayment.AddFeature("Payment.Allow", defaultValue: "false");
    
            }
        }
    
    

    For point 4: I need OpenIddictApplication.cs & OpenIddictDbContextModelCreatingExtensions.cs to inherit from my custom Iinterface class. Can you please help?

    For point 7: The code you have suggested is already there in my project. On desktop screen login button is visible, however when I use on mobile device, the login button is not visible.

    The links you have sent me see changelog, does not have details of change, it has simple summary in 2 or 3 word, how can I understand what is changed?

    Regards, Navneet

  • User Avatar
    0
    Navneet@aol.com.au created

    Currently, tenants can't configure their own payment gateway configurations by default, all the configurations are made in appsettings.json, so tenants can't use different payment accounts (different stripe, payu accaounts etc.), so there is no meaning adding new productId by tenants. So, converting that permission for host only is a better approach over this condition. We'll make some overhauls on this in the next version.

    Thanks for your quick response, I was about to commit tenant level payment function to my client.

    Does the payment module has any built in Subscription feature, where I can save subscription details in host DB and call payment api gateway to process payment each month, I know that as per documentation, only stripe support recurring and subscription payment, but I want to manage subscription on my application so that my application can use all payment gateway.

    Also, I have noticed that Volo.Docs also should be host only as tenant has access to host documents

    Regards, Navneet

  • User Avatar
    0
    Navneet@aol.com.au created

    I am in love with ABP

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    For point 1: Your method will apply to project wide, I have only three entity which I want to change generated by suite, can you please give me some sample code for entity “Book” and “Author” using partial class and using N-N navigation, I will use that sample to replicate in my project for other class.

    You can only change the Server.Entity.Entity.txt template to add the partial keyword,

    And we have a document for creating N-N navigation: https://docs.abp.io/en/commercial/latest/abp-suite/creating-many-to-many-relationship

    For point 2: Thanks, I manage to create feature with below code, how to wireup with module so that I can control from FeatureManagement, by hiding icon & menu it does not disable if someone try to navigate manually.

    You can create a middleware interception request: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-7.0

    Point: check the feature is enabled if the request path is from Doc or Payment modules.

    For point 4: I need OpenIddictApplication.cs & OpenIddictDbContextModelCreatingExtensions.cs to inherit from my custom Iinterface class. Can you please help?

    Sorry, but it's not possible, You can consider adding a new entity and having a one-on-one relationship with OpenIddictApplication.

    For point 7: The code you have suggested is already there in my project. On desktop screen login button is visible, however when I use on mobile device, the login button is not visible.

    Can you share a project that can reproduce the problem? shiwei.liang@volosoft.com I will check it

    The links you have sent me see changelog, does not have details of change, it has simple summary in 2 or 3 word, how can I understand what is changed?

    You can read the blog post / announcement

    like: https://blog.abp.io/abp/ABP.IO-Platform-7.1-RC-Has-Been-Published

  • User Avatar
    0
    Navneet@aol.com.au created

    Hi liangshiwei,

    I will send you a sample project with issue I am facing

    Regards, Navneet

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    ok, thanks.

  • User Avatar
    0
    Navneet@aol.com.au created

    HI Liangshiwei,

    I have sent you email with download link

    Regards, Navneet

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    1

    This is a known problem, we have fixed it. you can try:

    Remove the following code:

    if (context.Toolbar.Name != StandardToolbars.Main)
    {
        return Task.CompletedTask;
    }
    

    Put the Default.cshtml file in the Acme.BookStore.Web.Public/Themes/LeptonX/Components/SideMenu/MobileNavbar folder:

    @using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Themes.LeptonX.Components.SideMenu.MainMenu
    @using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Themes.LeptonX.Components.Common.MobileGeneralSettings
    @using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Themes.LeptonX.Components.SideMenu.MobileNavbar
    @using Volo.Abp.LeptonX.Shared.Localization
    @using Microsoft.Extensions.Localization
    @using Volo.Abp.Users
    
    @model MobileNavbarViewModel
    @inject ICurrentUser CurrentUser
    @inject IStringLocalizer<LeptonXResource> L
    
    <div class="lpx-mobile-navbar-container">
    	<div class="lpx-mobile-navbar">
    		<ul class="lpx-mobile-nav-tabs">
    
    			@foreach (var viewModel in Model.SelectedMenuItems)
    			{
    				var url = string.IsNullOrEmpty(viewModel.MenuItem.Url) ? "#" : Url.IsLocalUrl(viewModel.MenuItem.Url) ? Url.Content(viewModel.MenuItem.Url.EnsureStartsWith('~')) : viewModel.MenuItem.Url;
    
    				<li class="lpx-mobile-nav-tab">
    					<a id="@viewModel.MenuItem.ElementId" href="@url" target="@viewModel.MenuItem.Target" class="lpx-mobile-nav-item @viewModel.MenuItem.CssClass">
    						<i class="menu-item-icon @viewModel.MenuItem.Icon" aria-hidden="true"></i>
    						<span class="mobile-item-text"> @viewModel.MenuItem.DisplayName </span>
    					</a>
    				</li>
    			}
    
    			<li class="lpx-mobile-nav-tab menu-toggle">
    				<a href="javascript:void(0)" class="lpx-mobile-hamburger" data-lpx-mobile-menu-toggle="routes">
    					<span class="hamburger-icon" aria-hidden="true">
    						<span class="icon-part"></span>
    						<span class="icon-part"></span>
    						<span class="icon-part"></span>
    						<span class="icon-part"></span>
    						<span class="icon-part"></span>
    						<span class="icon-part"></span>
    					</span>
    				</a>
    			</li>
    
    			<li class="lpx-mobile-nav-tab">
    				<a class="lpx-mobile-nav-item" data-lpx-mobile-menu-toggle="settings">
    					<i class="menu-item-icon bi bi-gear-wide-connected" aria-hidden="true"></i>
    					<span class="mobile-item-text">Settings</span>
    				</a>
    			</li>
    			<li class="lpx-mobile-nav-tab">
    				@if (CurrentUser.IsAuthenticated)
    				{
    				  <a class="lpx-mobile-nav-item" data-lpx-mobile-menu-toggle="user">
    				    <div class="lpx-avatar">
    				      <img class="lpx-avatar-img" src="@Model.ProfileImageUrl" alt="@CurrentUser.UserName avatar" />
    				    </div>
    
    				    <span class="mobile-item-text">@CurrentUser.UserName</span>
    				  </a>
    				}
    				else
    				{
    				  <a href="~/account/login" class="lpx-mobile-nav-item">
    				    <i class="menu-item-icon bi bi-person-fill" aria-hidden="true"></i>
    				    <span class="mobile-item-text">@L["Login"]</span>
    				  </a>
    				}
    			</li>
    		</ul>
    	</div>
    
    	<div class="lpx-mobile-menu hidden">
    		<div class="lpx-logo-container">
    			<a href="/">
    				<div class="lpx-brand-logo"></div>
    			</a>
    		</div>
    		<ul class="lpx-nav-menu d-none" data-lpx-mobile-menu="routes">
    			@await Component.InvokeAsync(typeof(MainMenuViewComponent))
    		</ul>
    
    		<ul class="lpx-nav-menu d-none" data-lpx-mobile-menu="settings">
    			@await Component.InvokeAsync(typeof(MobileGeneralSettingsViewComponent))
    		</ul>
    
    		<ul class="lpx-nav-menu d-none" data-lpx-mobile-menu="user">
    			<div class="d-flex ps-3 pe-3">
    				<div class="lpx-avatar me-2">
    					<img class="lpx-avatar-img" src="@Model.ProfileImageUrl" alt="@CurrentUser.UserName avatar" />
    				</div>
    
    				<div class="d-flex flex-column" style="line-height: normal">
    					<span class="fs-12">@L["Welcome"] <span class="color-active-text"> @CurrentUser.UserName </span> </span>
    					<span class="color-active-text">@CurrentUser.Name @CurrentUser.SurName</span>
    					<span class="fs-12">@CurrentUser.Email</span>
    				</div>
    			</div>
    
    			@if (Model.UserMenu != null)
    			{
    				foreach (var menuItem in Model.UserMenu.Items)
    				{
    					var url = string.IsNullOrEmpty(menuItem.Url) ? "#" : Url.IsLocalUrl(menuItem.Url) ? Url.Content(menuItem.Url.EnsureStartsWith('~')) : menuItem.Url;
    
    					<li class="outer-menu-item">
    						<a class="lpx-menu-item-link lpx-menu-item @menuItem.CssClass" href="@url" target="@menuItem.Target" id="@menuItem.ElementId">
    							<span class="lpx-menu-item-icon"><i class="lpx-icon bi @menuItem.Icon" aria-hidden="true"></i></span>
    
    							<span class="lpx-menu-item-text hidden-in-hover-trigger">@menuItem.DisplayName</span>
    						</a>
    					</li>
    				}
    			}
    		</ul>
    	</div>
    </div>
    
    

    2

    You need to install those admin packages to your solution

  • User Avatar
    0
    Navneet@aol.com.au created

    Thanks liangshiwei,

    For point 1, it has created another issue, please see below screenshot:

    For Point 2, It is already referred in the project, please see screenshot:

    The swagger UI missing some resources, does 7.0.3 has bugs?, please see attached screenshots:

    Am I doing something incorrect while creating projects via ABP Suite?

    Regards, Navneet

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    For point 1, it has created another issue, please see below screenshot:

    Please update the Default.cshtml

    @using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Themes.LeptonX.Components.SideMenu.MainMenu
    @using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Themes.LeptonX.Components.Common.MobileGeneralSettings
    @using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Themes.LeptonX.Components.SideMenu.MobileNavbar
    @using Volo.Abp.LeptonX.Shared.Localization
    @using Microsoft.Extensions.Localization
    @using Volo.Abp.Users
    
    @model MobileNavbarViewModel
    @inject ICurrentUser CurrentUser
    @inject IStringLocalizer<LeptonXResource> L
    
    <div class="lpx-mobile-navbar-container">
    	<div class="lpx-mobile-navbar">
    		<ul class="lpx-mobile-nav-tabs">
    
    			@foreach (var viewModel in Model.SelectedMenuItems)
    			{
    				var url = string.IsNullOrEmpty(viewModel.MenuItem.Url) ? "#" : Url.IsLocalUrl(viewModel.MenuItem.Url) ? Url.Content(viewModel.MenuItem.Url.EnsureStartsWith('~')) : viewModel.MenuItem.Url;
    
    				<li class="lpx-mobile-nav-tab">
    					<a id="@viewModel.MenuItem.ElementId" href="@url" target="@viewModel.MenuItem.Target" class="lpx-mobile-nav-item @viewModel.MenuItem.CssClass">
    						<i class="menu-item-icon @viewModel.MenuItem.Icon" aria-hidden="true"></i>
    						<span class="mobile-item-text"> @viewModel.MenuItem.DisplayName </span>
    					</a>
    				</li>
    			}
    
    			<li class="lpx-mobile-nav-tab menu-toggle">
    				<a href="javascript:void(0)" class="lpx-mobile-hamburger" data-lpx-mobile-menu-toggle="routes">
    					<span class="hamburger-icon" aria-hidden="true">
    						<span class="icon-part"></span>
    						<span class="icon-part"></span>
    						<span class="icon-part"></span>
    						<span class="icon-part"></span>
    						<span class="icon-part"></span>
    						<span class="icon-part"></span>
    					</span>
    				</a>
    			</li>
    
    			<li class="lpx-mobile-nav-tab">
    				<a class="lpx-mobile-nav-item" data-lpx-mobile-menu-toggle="settings">
    					<i class="menu-item-icon bi bi-gear-wide-connected" aria-hidden="true"></i>
    					<span class="mobile-item-text">Settings</span>
    				</a>
    			</li>
    			<li class="lpx-mobile-nav-tab">
    				@if (CurrentUser.IsAuthenticated)
    				{
    				  <a class="lpx-mobile-nav-item" data-lpx-mobile-menu-toggle="user">
    				  <div class="lpx-avatar">
    					   <img class="lpx-avatar-img" src="@Model.ProfileImageUrl" alt="admin avatar">
    					</div>
    
    				    <span class="mobile-item-text">@CurrentUser.UserName</span>
    				  </a>
    				}
    				else
    				{
    				  <a href="~/account/login" class="lpx-mobile-nav-item">
    				    <i class="menu-item-icon bi bi-person-fill" aria-hidden="true"></i>
    				    <span class="mobile-item-text">@L["Login"]</span>
    				  </a>
    				}
    			</li>
    		</ul>
    	</div>
    
    	<div class="lpx-mobile-menu hidden">
    		<div class="lpx-logo-container">
    			<a href="/">
    				<div class="lpx-brand-logo"></div>
    			</a>
    		</div>
    		<ul class="lpx-nav-menu d-none" data-lpx-mobile-menu="routes">
    			@await Component.InvokeAsync(typeof(MainMenuViewComponent))
    		</ul>
    
    		<ul class="lpx-nav-menu d-none" data-lpx-mobile-menu="settings">
    			@await Component.InvokeAsync(typeof(MobileGeneralSettingsViewComponent))
    		</ul>
    
    		<ul class="lpx-nav-menu d-none" data-lpx-mobile-menu="user">
    			<div class="d-flex ps-3 pe-3">
    				<div class="lpx-avatar me-2">
    					<a class="lpx-mobile-nav-item"><img class="lpx-avatar-img" src="@Model.ProfileImageUrl" alt=""></a>
    				</div>
    
    				<div class="d-flex flex-column" style="line-height: normal">
    					<span class="fs-12">@L["Welcome"] <span class="color-active-text"> @CurrentUser.UserName </span> </span>
    					<span class="color-active-text">@CurrentUser.Name @CurrentUser.SurName</span>
    					<span class="fs-12">@CurrentUser.Email</span>
    				</div>
    			</div>
    
    			@if (Model.UserMenu != null)
    			{
    				foreach (var menuItem in Model.UserMenu.Items)
    				{
    					var url = string.IsNullOrEmpty(menuItem.Url) ? "#" : Url.IsLocalUrl(menuItem.Url) ? Url.Content(menuItem.Url.EnsureStartsWith('~')) : menuItem.Url;
    
    					<li class="outer-menu-item">
    						<a class="lpx-menu-item-link lpx-menu-item @menuItem.CssClass" href="@url" target="@menuItem.Target" id="@menuItem.ElementId">
    							<span class="lpx-menu-item-icon"><i class="lpx-icon bi @menuItem.Icon" aria-hidden="true"></i></span>
    
    							<span class="lpx-menu-item-text hidden-in-hover-trigger">@menuItem.DisplayName</span>
    						</a>
    					</li>
    				}
    			}
    		</ul>
    	</div>
    </div>
    

    For Point 2, It is already referred in the project, please see screenshot:

    You also need to install admin.application , admin.application.contract, admin.httpapi etc... packages.

    The swagger UI missing some resources, does 7.0.3 has bugs?, please see attached screenshots:

    You can move the DocsWebModule to the WebPublic project.

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Am I doing something incorrect while creating projects via ABP Suite?

    The docs have two modules: Volo.Docs.Admin and Volo.Docs

    But Volo.Docs.Admin is not available in the ABP suite, It seems a problem, we will fix it.

  • User Avatar
    0
    Navneet@aol.com.au created

    Hi,

    For point 1, it has created another issue, please see below screenshot:

    Please update the Default.cshtml

    @using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Themes.LeptonX.Components.SideMenu.MainMenu 
    @using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Themes.LeptonX.Components.Common.MobileGeneralSettings 
    @using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Themes.LeptonX.Components.SideMenu.MobileNavbar 
    @using Volo.Abp.LeptonX.Shared.Localization 
    @using Microsoft.Extensions.Localization 
    @using Volo.Abp.Users 
     
    @model MobileNavbarViewModel 
    @inject ICurrentUser CurrentUser 
    @inject IStringLocalizer<LeptonXResource> L 
     
    <div class="lpx-mobile-navbar-container"> 
      <div class="lpx-mobile-navbar"> 
      	<ul class="lpx-mobile-nav-tabs"> 
     
      		@foreach (var viewModel in Model.SelectedMenuItems) 
      		{ 
      			var url = string.IsNullOrEmpty(viewModel.MenuItem.Url) ? "#" : Url.IsLocalUrl(viewModel.MenuItem.Url) ? Url.Content(viewModel.MenuItem.Url.EnsureStartsWith('~')) : viewModel.MenuItem.Url; 
     
      			<li class="lpx-mobile-nav-tab"> 
      				<a id="@viewModel.MenuItem.ElementId" href="@url" target="@viewModel.MenuItem.Target" class="lpx-mobile-nav-item @viewModel.MenuItem.CssClass"> 
      					<i class="menu-item-icon @viewModel.MenuItem.Icon" aria-hidden="true"></i> 
      					<span class="mobile-item-text"> @viewModel.MenuItem.DisplayName </span> 
      				</a> 
      			</li> 
      		} 
     
      		<li class="lpx-mobile-nav-tab menu-toggle"> 
      			<a href="javascript:void(0)" class="lpx-mobile-hamburger" data-lpx-mobile-menu-toggle="routes"> 
      				<span class="hamburger-icon" aria-hidden="true"> 
      					<span class="icon-part"></span> 
      					<span class="icon-part"></span> 
      					<span class="icon-part"></span> 
      					<span class="icon-part"></span> 
      					<span class="icon-part"></span> 
      					<span class="icon-part"></span> 
      				</span> 
      			</a> 
      		</li> 
     
      		<li class="lpx-mobile-nav-tab"> 
      			<a class="lpx-mobile-nav-item" data-lpx-mobile-menu-toggle="settings"> 
      				<i class="menu-item-icon bi bi-gear-wide-connected" aria-hidden="true"></i> 
      				<span class="mobile-item-text">Settings</span> 
      			</a> 
      		</li> 
      		<li class="lpx-mobile-nav-tab"> 
      			@if (CurrentUser.IsAuthenticated) 
      			{ 
      			  <a class="lpx-mobile-nav-item" data-lpx-mobile-menu-toggle="user"> 
      			  <div class="lpx-avatar"> 
      				   <img class="lpx-avatar-img" src="@Model.ProfileImageUrl" alt="admin avatar"> 
      				</div> 
     
      			    <span class="mobile-item-text">@CurrentUser.UserName</span> 
      			  </a> 
      			} 
      			else 
      			{ 
      			  <a href="~/account/login" class="lpx-mobile-nav-item"> 
      			    <i class="menu-item-icon bi bi-person-fill" aria-hidden="true"></i> 
      			    <span class="mobile-item-text">@L["Login"]</span> 
      			  </a> 
      			} 
      		</li> 
      	</ul> 
      </div> 
     
      <div class="lpx-mobile-menu hidden"> 
      	<div class="lpx-logo-container"> 
      		<a href="/"> 
      			<div class="lpx-brand-logo"></div> 
      		</a> 
      	</div> 
      	<ul class="lpx-nav-menu d-none" data-lpx-mobile-menu="routes"> 
      		@await Component.InvokeAsync(typeof(MainMenuViewComponent)) 
      	</ul> 
     
      	<ul class="lpx-nav-menu d-none" data-lpx-mobile-menu="settings"> 
      		@await Component.InvokeAsync(typeof(MobileGeneralSettingsViewComponent)) 
      	</ul> 
     
      	<ul class="lpx-nav-menu d-none" data-lpx-mobile-menu="user"> 
      		<div class="d-flex ps-3 pe-3"> 
      			<div class="lpx-avatar me-2"> 
      				<a class="lpx-mobile-nav-item"><img class="lpx-avatar-img" src="@Model.ProfileImageUrl" alt=""></a> 
      			</div> 
     
      			<div class="d-flex flex-column" style="line-height: normal"> 
      				<span class="fs-12">@L["Welcome"] <span class="color-active-text"> @CurrentUser.UserName </span> </span> 
      				<span class="color-active-text">@CurrentUser.Name @CurrentUser.SurName</span> 
      				<span class="fs-12">@CurrentUser.Email</span> 
      			</div> 
      		</div> 
     
      		@if (Model.UserMenu != null) 
      		{ 
      			foreach (var menuItem in Model.UserMenu.Items) 
      			{ 
      				var url = string.IsNullOrEmpty(menuItem.Url) ? "#" : Url.IsLocalUrl(menuItem.Url) ? Url.Content(menuItem.Url.EnsureStartsWith('~')) : menuItem.Url; 
     
      				<li class="outer-menu-item"> 
      					<a class="lpx-menu-item-link lpx-menu-item @menuItem.CssClass" href="@url" target="@menuItem.Target" id="@menuItem.ElementId"> 
      						<span class="lpx-menu-item-icon"><i class="lpx-icon bi @menuItem.Icon" aria-hidden="true"></i></span> 
     
      						<span class="lpx-menu-item-text hidden-in-hover-trigger">@menuItem.DisplayName</span> 
      					</a> 
      				</li> 
      			} 
      		} 
      	</ul> 
      </div> 
    </div> 
    

    For Point 2, It is already referred in the project, please see screenshot:

    You also need to install admin.application , admin.application.contract, admin.httpapi etc... packages.

    The swagger UI missing some resources, does 7.0.3 has bugs?, please see attached screenshots:

    You can move the DocsWebModule to the WebPublic project.

    Hi,

    Your suggested html didn't fix the issue (Please note, I didn't find any difference in HTML you sent me earlier and Yesterday, both looks same), I am using LeptonX version 2.0.5 and ABP pro 7.0.3

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    Sorry again,

    The rich text editor convert my reply and make it wrong.

    Please try this: https://gist.github.com/realLiangshiwei/7ffd48434a102097ca9168016a8a5359

  • User Avatar
    0
    Navneet@aol.com.au created

    Hi,

    Sorry again,

    The rich text editor convert my reply and make it wrong.

    Please try this: https://gist.github.com/realLiangshiwei/7ffd48434a102097ca9168016a8a5359

    We are getting close to fix, however there is still icon missing from list, please see below:


    For Point 2, It is already referred in the project, please see screenshot:

    You also need to install admin.application , admin.application.contract, admin.httpapi etc... packages.

    after adding volo docs admin modules, I can now add documents and pull from GitHub, however I am getting below errors: . . . In UI header and value is mapped incorrectly, however in database everything looks correct: .

    . . Maybe because of above, I am getting error while browsing: . . . Regards, Navneet

  • User Avatar
    0
    hinairusu created

    Hi Navneet,

    Thank you for posting this, it's helped me with an issue I have as well. For the broken Icons, I found if you goto the Default.cshtml and edit the lines near the bottom (and beware the horrible formatting of this editor):

    				@if (Model.UserMenu != null)
    		{
    			foreach (var menuItem in Model.UserMenu.Items)
    			{
    				var url = string.IsNullOrEmpty(menuItem.Url) ? "#" : Url.IsLocalUrl(menuItem.Url) ? Url.Content(menuItem.Url.EnsureStartsWith('~')) : menuItem.Url;&lt;li class=&quot;outer-menu-item&quot;&gt;
    					&lt;a class=&quot;lpx-menu-item-link lpx-menu-item @menuItem.CssClass&quot; href=&quot;@url&quot; target=&quot;@menuItem.Target&quot; id=&quot;@menuItem.ElementId&quot;&gt;
    						&lt;span class=&quot;lpx-menu-item-icon&quot;&gt;&lt;i class=&quot;lpx-icon bi @menuItem.Icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt;&lt;/span&gt;
    
    						&lt;span class=&quot;lpx-menu-item-text hidden-in-hover-trigger&quot;&gt;@menuItem.DisplayName&lt;/span&gt;
    					&lt;/a&gt;
    				&lt;/li&gt;
    			}
    		}
    

    and remove the "bi" from the icons, it will load correctly. My line is:

    				@if (Model.UserMenu != null)
    		{
    			foreach (var menuItem in Model.UserMenu.Items)
    			{
    				var url = string.IsNullOrEmpty(menuItem.Url) ? "#" : Url.IsLocalUrl(menuItem.Url) ? Url.Content(menuItem.Url.EnsureStartsWith('~')) : menuItem.Url;&lt;li class=&quot;outer-menu-item&quot;&gt;
    					&lt;a class=&quot;lpx-menu-item-link lpx-menu-item @menuItem.CssClass&quot; href=&quot;@url&quot; target=&quot;@menuItem.Target&quot; id=&quot;@menuItem.ElementId&quot;&gt;
    						&lt;span class=&quot;lpx-menu-item-icon&quot;&gt;&lt;i class=&quot;lpx-icon @menuItem.Icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt;&lt;/span&gt;
    
    						&lt;span class=&quot;lpx-menu-item-text hidden-in-hover-trigger&quot;&gt;@menuItem.DisplayName&lt;/span&gt;
    					&lt;/a&gt;
    				&lt;/li&gt;
    			}
    		}
    
  • User Avatar
    0
    Navneet@aol.com.au created

    Thanks Hinairusu for your suggestions . . . Hi Liangshiwei, can you please suggest how to fix volo.docs, I have attached screenshots in previous post

    Regards Navneet

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    The problem was fixed: https://github.com/abpframework/abp/pull/15927

    You can put the file index.js in the Pages/Docs/Admin/Documents folder.

    And it will get work, this is my project details:

    BTW, You probably will see a style problem:

    You can put the index.css file in the Pages/Documents/Project folder.

    .code-toolbar .line-highlight {
        margin-top: 1.5em !important;
        background: rgba(233, 237, 241, 0.34) !important;
        padding: 1px !important; }
    
    .input-group .input-group-text{
        background-color: transparent!important;
    }
    
  • User Avatar
    0
    Navneet@aol.com.au created

    Hi liangshiwei,

    Thanks for your suggestion, Admin side is now working properly. . However public side, I am still getting errors: .

    System.IndexOutOfRangeException: Index was outside the bounds of the array.
       at Volo.Docs.GitHub.Projects.ProjectGithubExtensions.GetGitHubInnerUrl(Project project, String languageCode, String documentName)
       at Volo.Docs.GitHub.Documents.GithubDocumentSource.GetLastKnownSignificantUpdateTime(Project project, String documentName, String languageCode, String version, Nullable`1 lastKnownSignificantUpdateTime, Boolean isNavigationDocument, Boolean isParameterDocument, IReadOnlyList`1 commits, DateTime documentCreationTime)
       at Volo.Docs.GitHub.Documents.GithubDocumentSource.GetDocumentAsync(Project project, String documentName, String languageCode, String version, Nullable`1 lastKnownSignificantUpdateTime)
       at Volo.Docs.Documents.DocumentAppService.GetDocumentAsync(String documentName, Project project, String languageCode, String version, Document oldDocument)
       at Volo.Docs.Documents.DocumentAppService.GetDocumentWithDetailsDtoAsync(Project project, String documentName, String languageCode, String version)
       at Volo.Docs.Documents.DocumentAppService.GetDefaultAsync(GetDefaultDocumentInput input)
       at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
       at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
       at Volo.Abp.GlobalFeatures.GlobalFeatureInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
       at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
       at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
       at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
       at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
       at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
       at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
       at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
       at Volo.Abp.Auditing.AuditingInterceptor.ProceedByLoggingAsync(IAbpMethodInvocation invocation, AbpAuditingOptions options, IAuditingHelper auditingHelper, IAuditLogScope auditLogScope)
       at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
       at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
       at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
       at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
       at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
       at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
       at Volo.Docs.Pages.Documents.Project.IndexModel.GetSpecificDocumentOrDefaultAsync(String languageCode)
       at Volo.Docs.Pages.Documents.Project.IndexModel.SetDocumentAsync()
       at Volo.Docs.Pages.Documents.Project.IndexModel.SetPageAsync()
       at Volo.Docs.Pages.Documents.Project.IndexModel.OnGetAsync()
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.GenericTaskHandlerMethod.Convert[T](Object taskAsObject)
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.GenericTaskHandlerMethod.Execute(Object receiver, Object[] arguments)
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync()
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Rethrow(PageHandlerExecutedContext context)
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync()
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeNextExceptionFilterAsync&gt;g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ExceptionContextSealed context)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeNextResourceFilter&gt;g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeFilterPipelineAsync&gt;g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeAsync&gt;g__Logged|17_1(ResourceInvoker invoker)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeAsync&gt;g__Logged|17_1(ResourceInvoker invoker)
       at Microsoft.AspNetCore.Routing.EndpointMiddleware.&lt;Invoke&gt;g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
       at Volo.Abp.AspNetCore.Uow.AbpUnitOfWorkMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
       at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.&lt;&gt;c__DisplayClass6_1.&lt;&lt;UseMiddlewareInterface&gt;b__1>d.MoveNext()
    --- End of stack trace from previous location ---
       at Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
       at Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
       at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.&lt;&gt;c__DisplayClass6_1.&lt;&lt;UseMiddlewareInterface&gt;b__1>d.MoveNext()
    --- End of stack trace from previous location ---
       at Volo.Abp.AspNetCore.Serilog.AbpSerilogMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
       at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.&lt;&gt;c__DisplayClass6_1.&lt;&lt;UseMiddlewareInterface&gt;b__1>d.MoveNext()
    --- End of stack trace from previous location ---
       at Volo.Abp.AspNetCore.Auditing.AbpAuditingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
       at Volo.Abp.AspNetCore.Auditing.AbpAuditingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
       at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.&lt;&gt;c__DisplayClass6_1.&lt;&lt;UseMiddlewareInterface&gt;b__1>d.MoveNext()
    --- End of stack trace from previous location ---
       at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
       at Volo.Abp.AspNetCore.MultiTenancy.MultiTenancyMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
       at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.&lt;&gt;c__DisplayClass6_1.&lt;&lt;UseMiddlewareInterface&gt;b__1>d.MoveNext()
    --- End of stack trace from previous location ---
       at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
       at Volo.Abp.AspNetCore.Security.AbpSecurityHeadersMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
       at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.&lt;&gt;c__DisplayClass6_1.&lt;&lt;UseMiddlewareInterface&gt;b__1>d.MoveNext()
    --- End of stack trace from previous location ---
       at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.RequestLocalization.AbpRequestLocalizationMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
       at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.&lt;&gt;c__DisplayClass6_1.&lt;&lt;UseMiddlewareInterface&gt;b__1>d.MoveNext()
    --- End of stack trace from previous location ---
       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
    

    Regards, Navneet

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    What's your project's github root URL?

  • User Avatar
    0
    Navneet@aol.com.au created

    https://github.com/NavneetProject/abp-commercial-docs

Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11