diff --git a/docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/bento.png b/docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/bento.png new file mode 100644 index 0000000000..bb16a71259 Binary files /dev/null and b/docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/bento.png differ diff --git a/docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/dark-mode.png b/docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/dark-mode.png new file mode 100644 index 0000000000..612a786a71 Binary files /dev/null and b/docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/dark-mode.png differ diff --git a/docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/large.png b/docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/large.png new file mode 100644 index 0000000000..544214db08 Binary files /dev/null and b/docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/large.png differ diff --git a/docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/post.md b/docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/post.md new file mode 100644 index 0000000000..e0f9ec3b5f --- /dev/null +++ b/docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/post.md @@ -0,0 +1,112 @@ +# UI & UX Trends That Will Shape 2026 + +Cinematic, gamified, high-wow-factor websites with scroll-to-play videos or scroll-to-tell stories are wonderful to experience, but you won't find these trends in this article. If you're interested in design trends directly related to the software world, such as **performance**, **accessibility**, **understandability**, and **efficiency**, grab a cup of coffee and enjoy. + +As we approach the end of 2025, I'd like to share with you the most important user interface and user experience design trends that have become more of a **toolkit** than a trend, and that continue to evolve and become a part of our lives. I predict we'll see a lot of them in 2026\. + +## 1\. Simplicity and Speed ​​ + +Designing understandable and readable applications is becoming far more important than designing in line with trends and fashion. In the software and business world, preferences are shifting more and more toward the **right design** over the cool design. As designers developing a product whose direct target audience is software developers, we design our products for the designers' enjoyment, but for the **end user's ease of use**. + +Users no longer care so much about the flashiness of a website. True converts are primarily interested in your product, service, or content. What truly matters to them is how easily and quickly they can access the information they're looking for. + +More users, more sales, better promotion, and a higher conversion rate... The elements that serve these goals are optimized solutions and thoughtful details in our designs, more than visual displays. + +If the "loading" icon appears too often on your digital product, you might not be doing it right. If you fail to optimize speed, the temporary effect of visual displays won't be enough to convert potential users into customers. Remember, the moment people start waiting, you've lost at least half of them. + +## 2\. Dark Mode \- Still, and Forever +![data-model](./dark-mode.png) + +Dark Mode is no longer an option; it's a **standard**. It's become a necessity, not a choice, especially for users who spend hours staring at screens and are accustomed to dark themes in code editors and terminals. However, the approach to dark mode isn't simply about inverting colors; it's much deeper than that. The key is managing contrast and depth. + +The layer hierarchy established in a light-colored design doesn't lose its impact when switched to dark mode. The colors, shadows, highlights, and contrasting elements used to create an **easily perceivable hierarchy** should be carefully considered for each mode. Our [LeptonX theme](https://leptontheme.com/)'s Light, Dark, Semi-dark, and System modes offer valuable insights you might want to explore. + +You might also want to take a look at the dark and light modes we designed with these elements in mind in [ABP Studio](https://abp.io/get-started) and the [ABP.io Documents page](https://abp.io/docs/latest/). + +## 3\. Bento Grid \- A Timeless Trend +![data-model](./bento.png) + +People don't read your website; they **scan** it. + +Bento Grid, an indispensable trend for designers looking to manage their attention, looks set to remain a staple in 2026, just as it was in 2025\. No designer should ignore the fact that many tech giants, especially Apple and Samsung, are still using bento grids on their websites. The bento grid appears not only on websites but also in operating systems, VR headset interfaces, game console interfaces, and game designs. + +The golden rule is **contrast** and **balance**. + +The attractiveness and effectiveness of bento designs depend on certain factors you should consider when implementing them. If you ignore these rules, even with a proven method like bento, you can still alienate users. + +The bento grid is one of the best ways to display different types of content inclusively. When used correctly, it's also a great way to manipulate reading order, guiding the user's eye. Improper contrast and hierarchy can also create a negative experience. Designers should use this to guide the reader's eye: "Read here first, then read here." + +When creating a bento, you inherently have to sacrifice some of your "whitespace." This design has many elements for the user to focus on, and it actually strays from our first point, "Simplicity". Bento design, whose boundaries are drawn from the outset and independent of content, requires care not to include more or less than what is necessary. Too much content makes it boring; too little content makes it very close to meaningless. + +Bento grids should aim for a balanced design by using both simple text and sophisticated visuals. This visual can be an illustration, a video that starts playing when hovered over, a static image, or a large title. Only one or two cards on the screen at a time should have attention. + +## 4\. Larger Fonts, High Readability +![data-model](./large.png) + +Large fonts have been a trend for several years, and it seems web designers are becoming more and more bold. The increasing preference for larger fonts every year is a sign that this trend will continue into 2026\. This trend is about more than just using large font sizes in headlines. + +Creating a cohesive typographic scale and proper line height and letter spacing are critical elements to consider when creating this trend. As the font size increases, line height should decrease, and the space between letters should be narrower. + +The browser default font size, which we used to see in body text and paragraphs and has now become standard, is 16 pixels. In the last few years, we've started seeing body font sizes of 17 or 18 pixels more frequently. The increasing importance of readability every year makes this more common. Font sizes in rem values, rather than px, provide the most efficient results. + +## 5\. Micro Animations + +Unless you're a web design agency designing a website to impress potential clients, you should avoid excessive changes, including excessive image changes during scrolling, and scroll direction changes. There's still room for oversized images and scroll animations. But be sure to create the visuals yourself. + +The trend I'm talking about here is **micro animations**, not macro ones. Small movements, not large ones. + +The animation approach of 2025 is **functional** and **performance-sensitive**. + +Microanimations exist to provide immediate feedback to the user. Instant feedback, like a button's shadow increasing when hovered over, a button's slight collapse when clicked, or a "Save" icon changing to a "Confirm" icon when saving data, keeps your designs alive. + +We see the real impact of the micro-animation trend in static, non-action visuals. The use of non-button elements in your designs, accentuated by micro-movements such as scrolling or hovering, seems poised to continue to create macro effects in 2026\. + +## 6\. Real Images and Human-like Touches + +People quickly spot a fake. It's very difficult to convince a user who visits your website for the first time and doesn't trust you. **First impressions** matter. + +Real photographs, actual product screenshots, and brand-specific illustrations will continue to be among the elements we want to see in **trust-focused** designs in 2026\. + +In addition to flawless work done by AI, vivid, real-life visuals, accompanied by deliberate imperfections, hand-drawn details, or designed products that convey the message, "A human made this site\!", will continue to feel warmer and more welcoming. + +The human touch is evident not only in the visuals but also in your **content and text**. + +In 2026, you'll need more **human-like touches** that will make your design stand out among the thousands of similar websites rapidly generated by AI. + +## 7\. Accessibility \- No Longer an Option, But a Legal and Ethical Obligation + +Accessibility, once considered a nice-to-do thing in recent years, is now becoming a **necessity** in 2026 and beyond. Global regulations like the European Accessibility Act require all digital products to comply with WCAG standards. + +All design and software improvements you make to ensure end users can fully perform their tasks in your products, regardless of their temporary or permanent disabilities, should be viewed as ethical and commercial requirements, not as a requirement to comply with these standards. + +The foundation of accessibility in design is to use semantic HTML for screen readers, provide full keyboard control of all interactive elements, and clearly communicate the roles of complex components to the development team. + +## 8\. Intentional Friction + +Steve Krug, the father of UX design, started the trend of designing everything at a hyper-usable level with his book "Don't Make Me Think." As web designers, we've embraced this idea so much that all we care about is getting the user to their destination in the shortest possible scenario and as quickly as possible. This has required so many understandability measures that, after a while, it's starting to feel like fooling the user. + +In recent years, designers have started looking for ways to make things a little more challenging, rather than just getting the user to the result. + +When the end user visits your website, tries to understand exactly what it is at first glance, struggles a bit, and, after a little effort, becomes familiar with how your world works, they'll be more inclined to consider themselves a part of it. + +This has nothing to do with anti-usability. This philosophy is called Intentional Friction. + +This isn't a flaw; it's the pinnacle of error prevention. It's a step to prevent errors from occurring on autopilot and respects the user's ability to understand complex systems. Examples include reviewing the order summary or manually typing the project name when deleting a project on GitHub. + +## Bonus: Where Does Artificial Intelligence Fit In? + +Artificial intelligence will be an infrastructure in 2026, not a trend. + +As designers, we should leverage AI not to paint us a picture, but to make workflows more intelligent. In my opinion, this is the best use case for AI. + +AI can learn user behavior and adapt the interface accordingly. Real-time A/B testing can save us time by conducting a real-time content review. The ability to actively use AI in any area that allows you to accelerate your progress will take you a step further in your career. + +Since your users are always human, **don't be too eager** to incorporate AI-generated visuals into your design. Unless you're creating and selling a ready-made theme, you should **avoid** AI-generated visuals, random bento grids, and randomly generated content. + +You should definitely incorporate AI into your work for new content, new ideas, personal and professional development, and insights that will take your design a step further. But just as you don't design your website for designers to like, the same applies to AI. Humans, not robots, will experience your website. **AI-assisted**, not AI-generated, designs with a human touch are the trend I most expect seeing in 2026\. + +## Conclusion + +In the end, it's all fundamentally about respect for the user and their time. In 2026, our success as designers and developers will be measured not by how "cool" we are, but by how "efficient" and "reliable" a world we build for our users. + +Thank you for your time. diff --git a/docs/en/modules/cms-kit/dynamic-widget.md b/docs/en/modules/cms-kit/dynamic-widget.md index 8644f7cd63..f0646cc00f 100644 --- a/docs/en/modules/cms-kit/dynamic-widget.md +++ b/docs/en/modules/cms-kit/dynamic-widget.md @@ -124,21 +124,26 @@ In this image, after choosing your widget (on the other case, it changes automat You can edit this output manually if do any wrong coding for that (wrong value or typo) you won't see the widget, even so, your page will be viewed successfully. -## Options -To configure the widget, you should define the below code in YourModule.cs +## Options + +To add content widgets, you should configure the `CmsKitContentWidgetOptions` in your module's `ConfigureServices` method: ```csharp Configure(options => { options.AddWidget(widgetType: "Today", widgetName: "CmsToday", parameterWidgetName: "Format"); + + // Alternatively, you can add a widget conditionally based on a global feature being enabled + options.AddWidgetIfFeatureEnabled(typeof(PagesFeature), "Today", "CmsToday", "Format"); }); ``` -Let's look at these parameters in detail -* `widgetType` is used for end-user and more readable names. The following bold word represents widgetType. -[Widget Type="**Today**" Format="yyyy-dd-mm HH:mm:ss"]. +The `CmsKitContentWidgetOptions` provides two methods for registering widgets: -* `widgetName` is used for your widget name used in code for the name of the `ViewComponent`. +- **AddWidget:** Registers a widget that will be available in the content editor. It accepts the following parameters: + - `widgetType` (required): A user-friendly name for the widget that appears in the widget selection dropdown and is used in content markup. For example, in `[Widget Type="Today"]`, `"Today"` is the `widgetType`. + - `widgetName` (required): The name of the `ViewComponent` that will be rendered. This must match the `Name` attribute of your `ViewComponent` (e.g., `[ViewComponent(Name = "CmsToday")]`). + - `parameterWidgetName` (optional): The name of the parameter widget that will be displayed in the "Add Widget" modal to collect parameter values from users. This is only required when your widget needs parameters. -* `parameterWidgetName` is used the for editor component side to see on the `Add Widget` modal. -After choosing the widget type from listbox (now just defined `Format`) and renders this widget automatically. It's required only to see UI once using parameters \ No newline at end of file +- **AddWidgetIfFeatureEnabled:** Registers a widget conditionally, only if a specified [global feature](../../framework/infrastructure/global-features.md) is enabled. It accepts the same parameters as `AddWidget`, plus an additional first parameter: + - `featureType` (required): The type of the global feature that must be enabled for the widget to be available (e.g., `typeof(PagesFeature)`). \ No newline at end of file diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs index c58c266e3f..7ff890bac7 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs @@ -1,13 +1,10 @@ using System; -using JetBrains.Annotations; using Volo.Abp.Localization; namespace Volo.Abp.Authorization.Permissions; public interface IPermissionDefinitionContext { - //TODO: Add Get methods to find and modify a permission or group. - IServiceProvider ServiceProvider { get; } /// @@ -16,11 +13,11 @@ public interface IPermissionDefinitionContext /// /// Name of the group /// - PermissionGroupDefinition GetGroup([NotNull] string name); + PermissionGroupDefinition GetGroup(string name); /// /// Tries to get a pre-defined permission group. - /// Returns null if can not find the given group. + /// Returns null if it cannot find the given group. /// /// Name of the group /// @@ -33,7 +30,7 @@ public interface IPermissionDefinitionContext /// Localized display name of the group /// PermissionGroupDefinition AddGroup( - [NotNull] string name, + string name, ILocalizableString? displayName = null); /// @@ -44,9 +41,9 @@ public interface IPermissionDefinitionContext void RemoveGroup(string name); /// - /// Tries to get a pre-defined permission group. - /// Returns null if can not find the given group. - /// Name of the group + /// Tries to get a pre-defined permission from all defined groups. + /// Returns null if it cannot find the given permission. + /// Name of the permission /// - PermissionDefinition? GetPermissionOrNull([NotNull] string name); + PermissionDefinition? GetPermissionOrNull(string name); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs index 6811e2a9a1..8965b865d1 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs @@ -185,8 +185,7 @@ public abstract class AbpApplicationBase : IAbpApplication { try { - using var scope = ServiceProvider.CreateScope(); - var logger = scope.ServiceProvider.GetRequiredService>(); + var logger = Services.GetInitLogger(); logger.LogException(ex, LogLevel.Trace); } catch diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetryActivityEventBuilder.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetryActivityEventBuilder.cs index 201ac8c3b6..09c0c28430 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetryActivityEventBuilder.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetryActivityEventBuilder.cs @@ -8,7 +8,7 @@ using Volo.Abp.Internal.Telemetry.Constants; namespace Volo.Abp.Internal.Telemetry.Activity.Providers; -public class TelemetryActivityEventBuilder : ITelemetryActivityEventBuilder, ISingletonDependency +public class TelemetryActivityEventBuilder : ITelemetryActivityEventBuilder, ITransientDependency { private readonly List _activityEnrichers; @@ -31,7 +31,7 @@ public class TelemetryActivityEventBuilder : ITelemetryActivityEventBuilder, ISi { //ignored } - + if (context.IsTerminated) { return null; @@ -40,10 +40,10 @@ public class TelemetryActivityEventBuilder : ITelemetryActivityEventBuilder, ISi return context.Current; } - + private static bool FilterEnricher(ITelemetryActivityEventEnricher enricher) { - return ProxyHelper.GetUnProxiedType(enricher).Assembly.FullName!.StartsWith(TelemetryConsts.VoloNameSpaceFilter) && + return ProxyHelper.GetUnProxiedType(enricher).Assembly.FullName!.StartsWith(TelemetryConsts.VoloNameSpaceFilter) && enricher is not IHasParentTelemetryActivityEventEnricher; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 45fe447e88..9243d4680e 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -287,7 +287,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges) { var ignoredEntity = EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.Any(selector => selector.Predicate(entityEntry.Entity.GetType())); - var onlyForeignKeyModifiedEntity = entityEntry.State == EntityState.Modified && entityEntry.Properties.Where(x => x.IsModified).All(x => x.Metadata.IsForeignKey()); + var onlyForeignKeyModifiedEntity = entityEntry.State == EntityState.Modified && IsOnlyForeignKeysModified(entityEntry); if ((entityEntry.State == EntityState.Unchanged && ignoredEntity) || onlyForeignKeyModifiedEntity && ignoredEntity) { continue; @@ -311,7 +311,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, } else if (entityEntry.Properties.Any(x => x.IsModified && (x.Metadata.ValueGenerated == ValueGenerated.Never || x.Metadata.ValueGenerated == ValueGenerated.OnAdd))) { - if (entityEntry.Properties.Where(x => x.IsModified).All(x => x.Metadata.IsForeignKey())) + if (IsOnlyForeignKeysModified(entityEntry)) { // Skip `PublishEntityDeletedEvent/PublishEntityUpdatedEvent` if only foreign keys have changed. break; @@ -447,13 +447,13 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, case EntityState.Modified: if (entry.Properties.Any(x => x.IsModified && (x.Metadata.ValueGenerated == ValueGenerated.Never || x.Metadata.ValueGenerated == ValueGenerated.OnAdd))) { - var modifiedProperties = entry.Properties.Where(x => x.IsModified).ToList(); - if (modifiedProperties.All(x => x.Metadata.IsForeignKey())) + if (IsOnlyForeignKeysModified(entry)) { // Skip `PublishEntityDeletedEvent/PublishEntityUpdatedEvent` if only foreign keys have changed. break; } + var modifiedProperties = entry.Properties.Where(x => x.IsModified).ToList(); var disableAuditingAttributes = modifiedProperties.Select(x => x.Metadata.PropertyInfo?.GetCustomAttribute()).ToList(); if (disableAuditingAttributes.Any(x => x == null || x.UpdateModificationProps)) { @@ -500,6 +500,12 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, } } + protected virtual bool IsOnlyForeignKeysModified(EntityEntry entry) + { + return entry.Properties.Where(x => x.IsModified).All(x => x.Metadata.IsForeignKey() && + (x.CurrentValue == null || x.OriginalValue?.ToString() == x.CurrentValue?.ToString())); + } + protected virtual void HandlePropertiesBeforeSave() { var entries = ChangeTracker.Entries().ToList(); diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs index b102396cb0..8aeeb2cfac 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs @@ -28,7 +28,7 @@ public class AbpAuditingTestDbContext : AbpDbContext public DbSet AppEntityWithValueObject { get; set; } public DbSet AppEntityWithNavigations { get; set; } - + public DbSet AppEntityWithNavigationChildOneToMany { get; set; } public AbpAuditingTestDbContext(DbContextOptions options) : base(options) { diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs index 22c0b92477..5f905ca062 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs @@ -58,6 +58,11 @@ public class AbpEntityFrameworkCoreTestModule : AbpModule { opt.DefaultWithDetailsFunc = q => q.Include(p => p.BlogPosts); }); + + options.Entity(opt => + { + opt.DefaultWithDetailsFunc = q => q.Include(p => p.OneToMany); + }); }); context.Services.AddAbpDbContext(options => diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs index 6249d588f5..2967e15f16 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs @@ -48,6 +48,7 @@ public class AbpEntityChangeOptions_DomainEvents_IgnoreEntityChangeSelectorList_ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase { protected readonly IRepository AppEntityWithNavigationsRepository; + protected readonly IRepository AppEntityWithNavigationChildOneToManyRepository; protected readonly ILocalEventBus LocalEventBus; protected readonly IRepository PersonRepository; protected bool _loadEntityWithoutDetails = false; @@ -55,6 +56,7 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase public AbpEfCoreDomainEvents_Tests() { AppEntityWithNavigationsRepository = GetRequiredService>(); + AppEntityWithNavigationChildOneToManyRepository = GetRequiredService>(); LocalEventBus = GetRequiredService(); PersonRepository = GetRequiredService>(); } @@ -357,6 +359,22 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase var entityId = Guid.NewGuid(); await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity") + { + OneToMany = new List() + { + new AppEntityWithNavigationChildOneToMany(Guid.NewGuid()) + { + ChildName = "ChildName1" + }, + new AppEntityWithNavigationChildOneToMany(Guid.NewGuid()) + { + ChildName = "ChildName2" + } + } + }); + + var entityId2 = Guid.NewGuid(); + await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId2, "TestEntity") { OneToMany = new List() { @@ -367,6 +385,33 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase } }); + var oneToManyEntity = Guid.NewGuid(); + await AppEntityWithNavigationChildOneToManyRepository.InsertAsync( + new AppEntityWithNavigationChildOneToMany(oneToManyEntity) + { + AppEntityWithNavigationId = entityId, + }); + + LocalEventBus.Subscribe>(data => + { + data.Entity.AppEntityWithNavigationId.ShouldBe(entityId2); + return Task.CompletedTask; + }); + + using (var scope = ServiceProvider.CreateScope()) + { + var uowManager = scope.ServiceProvider.GetRequiredService(); + using (var uow = uowManager.Begin()) + { + var entity = await AppEntityWithNavigationChildOneToManyRepository.GetAsync(oneToManyEntity); + + entity.AppEntityWithNavigationId = entityId2; + await AppEntityWithNavigationChildOneToManyRepository.UpdateAsync(entity); + + await uow.CompleteAsync(); + } + } + var entityUpdatedEventTriggered = false; LocalEventBus.Subscribe>(data => @@ -375,6 +420,11 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase return Task.CompletedTask; }); + LocalEventBus.Subscribe>(data => + { + throw new Exception("Should not trigger this event"); + }); + using (var scope = ServiceProvider.CreateScope()) { var uowManager = scope.ServiceProvider.GetRequiredService(); diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs index 0efef5ba1f..4c6efa5a02 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs @@ -28,6 +28,7 @@ public class TestMigrationsDbContext : AbpDbContext public DbSet Categories { get; set; } public DbSet AppEntityWithNavigations { get; set; } + public DbSet AppEntityWithNavigationChildOneToMany { get; set; } public DbSet AppEntityWithNavigationsForeign { get; set; } @@ -102,7 +103,12 @@ public class TestMigrationsDbContext : AbpDbContext b.HasOne(x => x.OneToOne).WithOne().HasForeignKey(x => x.Id); b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationId); b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity(); - b.HasOne().WithMany().HasForeignKey(x => x.AppEntityWithNavigationForeignId).IsRequired(false); + }); + + modelBuilder.Entity(b => + { + b.ConfigureByConvention(); + b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationForeignId); }); modelBuilder.Entity(b => diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs index b2680abdff..a8e68dfca8 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs @@ -34,6 +34,7 @@ public class TestAppDbContext : AbpDbContext, IThirdDbContext, public DbSet Categories { get; set; } public DbSet AppEntityWithNavigations { get; set; } + public DbSet AppEntityWithNavigationChildOneToMany { get; set; } public DbSet AppEntityWithNavigationsForeign { get; set; } @@ -127,7 +128,12 @@ public class TestAppDbContext : AbpDbContext, IThirdDbContext, b.HasOne(x => x.OneToOne).WithOne().HasForeignKey(x => x.Id); b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationId); b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity(); - b.HasOne().WithMany().HasForeignKey(x => x.AppEntityWithNavigationForeignId).IsRequired(false); + }); + + modelBuilder.Entity(b => + { + b.ConfigureByConvention(); + b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationForeignId); }); modelBuilder.Entity(b => diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs index e7815b7622..c3ab4466a6 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs @@ -65,6 +65,17 @@ public class AppEntityWithNavigationChildOneToOneAndOneToOne : Entity public class AppEntityWithNavigationChildOneToMany : Entity { + public AppEntityWithNavigationChildOneToMany() + { + + } + + public AppEntityWithNavigationChildOneToMany(Guid id) + : base(id) + { + + } + public Guid AppEntityWithNavigationId { get; set; } public string ChildName { get; set; } @@ -107,4 +118,6 @@ public class AppEntityWithNavigationsForeign : AggregateRoot } public string Name { get; set; } + + public virtual List OneToMany { get; set; } } diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs index 032d714390..58010449ff 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs @@ -347,10 +347,20 @@ public abstract class AbpEntityChangeOptions_DomainEvents_Tests var entityWithNavigationForeignId = Guid.NewGuid(); var entityWithNavigationForeignId2 = Guid.NewGuid(); + var entityWithNavigationForeignId3 = Guid.NewGuid(); await AppEntityWithNavigationForeignRepository.InsertAsync(new AppEntityWithNavigationsForeign(entityWithNavigationForeignId, "TestEntityWithNavigationForeign")); await AppEntityWithNavigationForeignRepository.InsertAsync(new AppEntityWithNavigationsForeign(entityWithNavigationForeignId2, "TestEntityWithNavigationForeign2")); + await AppEntityWithNavigationForeignRepository.InsertAsync(new AppEntityWithNavigationsForeign(entityWithNavigationForeignId3, "TestEntityWithNavigationForeign3") + { + OneToMany = new List() + { + new AppEntityWithNavigations(Guid.NewGuid(), "TestEntity2"), + new AppEntityWithNavigations(Guid.NewGuid(), "TestEntity3") + } + }); var entityUpdatedEventTriggered = false; + var entityWithNavigationsForeignUpdatedEventTriggered = false; LocalEventBus.Subscribe>(data => { @@ -358,6 +368,12 @@ public abstract class AbpEntityChangeOptions_DomainEvents_Tests return Task.CompletedTask; }); + LocalEventBus.Subscribe>(data => + { + entityWithNavigationsForeignUpdatedEventTriggered = !entityWithNavigationsForeignUpdatedEventTriggered; + return Task.CompletedTask; + }); + // Test with simple property with foreign key await WithUnitOfWorkAsync(async () => { @@ -368,17 +384,39 @@ public abstract class AbpEntityChangeOptions_DomainEvents_Tests }); entityUpdatedEventTriggered.ShouldBeTrue(); - // Test only foreign key changed + // Test only foreign key change to null entityUpdatedEventTriggered = false; await WithUnitOfWorkAsync(async () => { var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); - entity.AppEntityWithNavigationForeignId = entityWithNavigationForeignId2; + entity.AppEntityWithNavigationForeignId = null; await AppEntityWithNavigationsRepository.UpdateAsync(entity); }); entityUpdatedEventTriggered.ShouldBeFalse(); + // Test only foreign key change to new id + entityUpdatedEventTriggered = false; + await WithUnitOfWorkAsync(async () => + { + var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); + entity.AppEntityWithNavigationForeignId = entityWithNavigationForeignId; + await AppEntityWithNavigationsRepository.UpdateAsync(entity); + }); + entityUpdatedEventTriggered.ShouldBeTrue(); + + // Test only foreign key changed + entityWithNavigationsForeignUpdatedEventTriggered = false; + await WithUnitOfWorkAsync(async () => + { + var entity = await AppEntityWithNavigationForeignRepository.GetAsync(entityWithNavigationForeignId3); + entity.OneToMany.ShouldNotBeEmpty(); + entity.OneToMany.Clear(); + await AppEntityWithNavigationForeignRepository.UpdateAsync(entity); + }); + entityWithNavigationsForeignUpdatedEventTriggered.ShouldBeFalse(); + // Test with simple property with value object + entityUpdatedEventTriggered = false; await WithUnitOfWorkAsync(async () => { var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Contents/addWidgetModal.js b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Contents/addWidgetModal.js index 78628314b2..6786870c6b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Contents/addWidgetModal.js +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Contents/addWidgetModal.js @@ -31,7 +31,14 @@ $(function () { for (var propertyName in properties) { if (!propertyName.includes(']') && !propertyName.includes('[')) { - widgetText += propertyName + "=\"" + properties[propertyName] + "\" "; + var propertyValue = properties[propertyName]; + + //skip default/empty values + if (propertyValue === null || propertyValue === undefined || propertyValue === '') { + continue; + } + + widgetText += propertyName + "=\"" + propertyValue + "\" "; } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Web/CmsKitCommonWebModule.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Web/CmsKitCommonWebModule.cs index 754f2263a0..7d081b09d9 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Common.Web/CmsKitCommonWebModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Web/CmsKitCommonWebModule.cs @@ -7,6 +7,8 @@ using Volo.CmsKit.Reactions; using Volo.CmsKit.Web.Icons; using Markdig; using Microsoft.Extensions.DependencyInjection; +using Volo.CmsKit.GlobalFeatures; +using Volo.CmsKit.Web.Contents; namespace Volo.CmsKit.Web; @@ -54,6 +56,11 @@ public class CmsKitCommonWebModule : AbpModule { options.DisableModule(CmsKitCommonRemoteServiceConsts.ModuleName); }); + + Configure(options => + { + options.AddWidgetIfFeatureEnabled(typeof(CommentsFeature), "Comment", "CmsCommenting", "CmsKitCommentConfiguration"); + }); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Comments/CmsKitCommentConfiguration.cshtml b/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Comments/CmsKitCommentConfiguration.cshtml new file mode 100644 index 0000000000..08b46e4e61 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Comments/CmsKitCommentConfiguration.cshtml @@ -0,0 +1,11 @@ +@using Microsoft.Extensions.Localization +@using Volo.CmsKit.Localization +@using Volo.CmsKit.Web.Pages.CmsKit.Components.Comments +@inject IStringLocalizer L +@model CmsKitCommentConfigurationViewModel + +
+ + + +
\ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Comments/CmsKitCommentConfigurationViewComponent.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Comments/CmsKitCommentConfigurationViewComponent.cs new file mode 100644 index 0000000000..1c4f847bf9 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Comments/CmsKitCommentConfigurationViewComponent.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace Volo.CmsKit.Web.Pages.CmsKit.Components.Comments; + +[Widget] +[ViewComponent(Name = "CmsKitCommentConfiguration")] +public class CmsKitCommentConfigurationViewComponent : AbpViewComponent +{ + public IViewComponentResult Invoke() + { + return View("~/Pages/CmsKit/Components/Comments/CmsKitCommentConfiguration.cshtml", new CmsKitCommentConfigurationViewModel()); + } +} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Comments/CmsKitCommentConfigurationViewModel.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Comments/CmsKitCommentConfigurationViewModel.cs new file mode 100644 index 0000000000..721a4dd2a2 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Comments/CmsKitCommentConfigurationViewModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace Volo.CmsKit.Web.Pages.CmsKit.Components.Comments; + +public class CmsKitCommentConfigurationViewModel +{ + [Required] + public string EntityType { get; set; } + + [Required] + public string EntityId { get; set; } + + public bool IsReadOnly { get; set; } = false; +} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Contents/CmsKitContentWidgetOptions.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Contents/CmsKitContentWidgetOptions.cs index ad643f7770..d879eb9dce 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Contents/CmsKitContentWidgetOptions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Contents/CmsKitContentWidgetOptions.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Reflection; +using Volo.Abp; +using Volo.Abp.GlobalFeatures; namespace Volo.CmsKit.Web.Contents; @@ -16,4 +20,21 @@ public class CmsKitContentWidgetOptions var config = new ContentWidgetConfig(widgetName, parameterWidgetName); WidgetConfigs.Add(widgetType, config); } + + public void AddWidgetIfFeatureEnabled(Type globalFeatureType, string widgetType, string widgetName, string parameterWidgetName = null) + { + Check.NotNull(globalFeatureType, nameof(globalFeatureType)); + + if (globalFeatureType.GetCustomAttribute() == null) + { + throw new ArgumentException($"The type {globalFeatureType.Name} must have a {nameof(GlobalFeatureNameAttribute)} attribute.", nameof(globalFeatureType)); + } + + if (!GlobalFeatureManager.Instance.IsEnabled(globalFeatureType)) + { + return; + } + + AddWidget(widgetType, widgetName, parameterWidgetName); + } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ar.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ar.json index bd2ba476ed..c051fe6f3b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ar.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ar.json @@ -262,6 +262,7 @@ "SelectAnBlogToAssign": "حدد مدونة لتعيين مشاركات المدونة إليها", "DeleteAllBlogPostsOfThisBlog": "حذف جميع مشاركات المدونة", "RequiredPermissionName": "اسم الإذن المطلوب", - "AllPosts": "جميع المشاركات" + "AllPosts": "جميع المشاركات", + "IsReadOnly": "للقراءة فقط" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json index ae03a27173..f7040b3044 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json @@ -281,6 +281,7 @@ "SelectAnBlogToAssign": "Vyberte blog, ke kterému chcete přiřadit blogové příspěvky", "DeleteAllBlogPostsOfThisBlog": "Smazat všechny blogové příspěvky tohoto blogu", "RequiredPermissionName": "Je vyžadováno oprávnění", - "AllPosts": "Všechny příspěvky" + "AllPosts": "Všechny příspěvky", + "IsReadOnly": "Pouze pro čtení" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/de-DE.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/de-DE.json index d04bbf95b7..6d87ffcc99 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/de-DE.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/de-DE.json @@ -169,6 +169,7 @@ "SelectAnBlogToAssign": "Wählen Sie einen Blog aus, um Blogbeiträge zuzuweisen", "DeleteAllBlogPostsOfThisBlog": "Alle Blogbeiträge dieses Blogs löschen", "RequiredPermissionName": "Erforderlicher Berechtigungsname", - "AllPosts": "Alle Beiträge" + "AllPosts": "Alle Beiträge", + "IsReadOnly": "Schreibgeschützt" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/de.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/de.json index 44514f8ba2..d5d9d8d1b0 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/de.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/de.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Wählen Sie einen Blog aus, um Blogbeiträge zuzuweisen", "DeleteAllBlogPostsOfThisBlog": "Alle Blogbeiträge dieses Blogs löschen", "RequiredPermissionName": "Erforderlicher Berechtigungsname", - "AllPosts": "Alle Beiträge" + "AllPosts": "Alle Beiträge", + "IsReadOnly": "Schreibgeschützt" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/el.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/el.json index 5767a7fcf1..897e02a07d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/el.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/el.json @@ -192,6 +192,7 @@ "SelectAnBlogToAssign": "Επιλέξτε ένα ιστολόγιο για να αναθέσετε αναρτήσεις ιστολογίου", "DeleteAllBlogPostsOfThisBlog": "Διαγραφή όλων των αναρτήσεων ιστολογίου αυτού του ιστολογίου", "RequiredPermissionName": "Απαιτούμενο όνομα δικαιώματος", - "AllPosts": "Όλες οι δημοσιεύσεις" + "AllPosts": "Όλες οι δημοσιεύσεις", + "IsReadOnly": "Μόνο για ανάγνωση" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en-GB.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en-GB.json index 896a58ee9f..290dc26fe5 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en-GB.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en-GB.json @@ -34,6 +34,7 @@ "SelectAnBlogToAssign": "Select a blog to assign", "DeleteAllBlogPostsOfThisBlog": "Delete all blog posts of this blog", "RequiredPermissionName": "Required permission name", - "AllPosts": "All posts" + "AllPosts": "All posts", + "IsReadOnly": "Readonly" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json index c47d7626be..e3c3600eb0 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json @@ -282,6 +282,7 @@ "RequiredPermissionName": "Required permission name", "Enum:PageStatus:0": "Draft", "Enum:PageStatus:1": "Publish", - "AllPosts": "All posts" + "AllPosts": "All posts", + "IsReadOnly": "Readonly" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/es.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/es.json index aced3c3227..5396f02fd4 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/es.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/es.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Seleccione un blog para asignar publicaciones de blog", "DeleteAllBlogPostsOfThisBlog": "Eliminar todas las publicaciones de blog de este blog", "RequiredPermissionName": "Nombre de permiso requerido", - "AllPosts": "Todas las publicaciones" + "AllPosts": "Todas las publicaciones", + "IsReadOnly": "Solo lectura" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fa.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fa.json index 9fc765010a..8769a8cb4c 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fa.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fa.json @@ -191,6 +191,7 @@ "SelectAnBlogToAssign": "یک وبلاگ برای اختصاص دادن انتخاب کنید", "DeleteAllBlogPostsOfThisBlog": "تمام پست های وبلاگ این وبلاگ را حذف کنید", "RequiredPermissionName": "نام مجوز مورد نیاز", - "AllPosts": "همه پست ها" + "AllPosts": "همه پست ها", + "IsReadOnly": "فقط خواندنی" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fi.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fi.json index fe22839e13..4e9eb5123f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fi.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fi.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Valitse blogi, johon haluat määrittää", "DeleteAllBlogPostsOfThisBlog": "Poista tämän blogin kaikki blogiviestit", "RequiredPermissionName": "Tarvittava lupa", - "AllPosts": "Kaikki viestit" + "AllPosts": "Kaikki viestit", + "IsReadOnly": "Vain luku" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fr.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fr.json index 6a2dc59d38..2d2ff61a3c 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fr.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fr.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Sélectionnez un blog à attribuer", "DeleteAllBlogPostsOfThisBlog": "Supprimer tous les articles de blog de ce blog", "RequiredPermissionName": "Nom de permission requis", - "AllPosts": "Tous les messages" + "AllPosts": "Tous les messages", + "IsReadOnly": "Lecture seule" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hi.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hi.json index 3522ff8386..d0ba97001c 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hi.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hi.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "असाइन करने के लिए एक ब्लॉग चुनें", "DeleteAllBlogPostsOfThisBlog": "इस ब्लॉग के सभी ब्लॉग पोस्ट हटाएं", "RequiredPermissionName": "आवश्यक अनुमति नाम", - "AllPosts": "सभी पोस्ट" + "AllPosts": "सभी पोस्ट", + "IsReadOnly": "केवल पढ़ने के लिए" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hr.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hr.json index c8c855444e..30f65f12c9 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hr.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hr.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Odaberite blog za dodjelu", "DeleteAllBlogPostsOfThisBlog": "Izbrišite sve postove na blogu", "RequiredPermissionName": "Potrebno ime dozvole", - "AllPosts": "Sve objave" + "AllPosts": "Sve objave", + "IsReadOnly": "Samo za čitanje" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hu.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hu.json index 31d9dc98da..1505e39597 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hu.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hu.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Válasszon egy blogot a hozzárendeléshez", "DeleteAllBlogPostsOfThisBlog": "Ez a művelet törli az összes blogbejegyzést ebből a blogból. Biztos vagy benne?", "RequiredPermissionName": "Szükséges engedély neve", - "AllPosts": "Minden bejegyzés" + "AllPosts": "Minden bejegyzés", + "IsReadOnly": "Csak olvasható" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/is.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/is.json index f31e8b75cb..d31c9316ea 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/is.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/is.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Veldu blogg til að úthluta", "DeleteAllBlogPostsOfThisBlog": "Eyða öllum bloggfærslum þessa bloggs", "RequiredPermissionName": "Nafn á nauðsynlegri leyfi", - "AllPosts": "Allar færslur" + "AllPosts": "Allar færslur", + "IsReadOnly": "Skrifvarið" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/it.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/it.json index 03e4010f5e..da69759dff 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/it.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/it.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Seleziona un blog a cui assegnare i post del blog", "DeleteAllBlogPostsOfThisBlog": "Elimina tutti i post del blog di questo blog", "RequiredPermissionName": "Nome del permesso richiesto", - "AllPosts": "Tutti i post" + "AllPosts": "Tutti i post", + "IsReadOnly": "Sola lettura" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/nl.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/nl.json index 0215dbf6dc..6d95e6aa36 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/nl.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/nl.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Selecteer een blog om toe te wijzen", "DeleteAllBlogPostsOfThisBlog": "Verwijder alle blogberichten van deze blog", "RequiredPermissionName": "Vereiste toestemming", - "AllPosts": "Alle berichten" + "AllPosts": "Alle berichten", + "IsReadOnly": "Alleen-lezen" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pl-PL.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pl-PL.json index 8ef422c412..7bb4e8e471 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pl-PL.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pl-PL.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Wybierz blog, do którego chcesz przypisać posty na blogu", "DeleteAllBlogPostsOfThisBlog": "Usuń wszystkie posty na blogu tego bloga", "RequiredPermissionName": "Wymagana nazwa uprawnienia", - "AllPosts": "Wszystkie posty" + "AllPosts": "Wszystkie posty", + "IsReadOnly": "Tylko do odczytu" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pt-BR.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pt-BR.json index 66b79628e5..3b3fa3db92 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pt-BR.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pt-BR.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Selecione um blog para atribuir", "DeleteAllBlogPostsOfThisBlog": "Excluir todas as postagens de blog deste blog", "RequiredPermissionName": "Nome da permissão necessária", - "AllPosts": "Todas as postagens" + "AllPosts": "Todas as postagens", + "IsReadOnly": "Somente leitura" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ro-RO.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ro-RO.json index 44fd87e2f9..0a283006be 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ro-RO.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ro-RO.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Selectaţi un blog pentru a atribui postările de blog", "DeleteAllBlogPostsOfThisBlog": "Ştergeţi toate postările de blog ale acestui blog", "RequiredPermissionName": "Numele permisiunii necesare", - "AllPosts": "Toate postările" + "AllPosts": "Toate postările", + "IsReadOnly": "Doar citire" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ru.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ru.json index d24f504ccb..8123f37403 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ru.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ru.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Выберите блог для назначения", "DeleteAllBlogPostsOfThisBlog": "Удалить все сообщения в блоге этого блога", "RequiredPermissionName": "Имя требуемого разрешения", - "AllPosts": "Все записи" + "AllPosts": "Все записи", + "IsReadOnly": "Только для чтения" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sk.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sk.json index c8ad091d7b..ddc10c923c 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sk.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sk.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Vyberte blog, na ktorý chcete priradiť blogové príspevky", "DeleteAllBlogPostsOfThisBlog": "Zmazať všetky blogové príspevky tohto blogu", "RequiredPermissionName": "Požadovaný názov oprávnenia", - "AllPosts": "Všetky príspevky" + "AllPosts": "Všetky príspevky", + "IsReadOnly": "Iba na čítanie" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sl.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sl.json index c62fd96e03..83c9ec57b4 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sl.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sl.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Izberite blog, ki mu želite dodeliti objave", "DeleteAllBlogPostsOfThisBlog": "Izbriši vse objave v tem blogu", "RequiredPermissionName": "Ime zahtevane dovoljenja", - "AllPosts": "Vse objave" + "AllPosts": "Vse objave", + "IsReadOnly": "Samo za branje" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sv.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sv.json index 93c549c916..3b6d296da4 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sv.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sv.json @@ -262,6 +262,7 @@ "SelectAnBlogToAssign": "Välj en blogg att tilldela", "DeleteAllBlogPostsOfThisBlog": "Radera alla blogginlägg i denna blogg", "RequiredPermissionName": "Nödvändigt behörighetsnamn", - "AllPosts": "Alla inlägg" + "AllPosts": "Alla inlägg", + "IsReadOnly": "Skrivskyddad" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json index 2c9abaa6d7..e338aaff68 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json @@ -226,6 +226,7 @@ "SelectAnBlogToAssign": "Atanacak bir blog seçin", "DeleteAllBlogPostsOfThisBlog": "Bu blogun tüm blog yazılarını sil", "RequiredPermissionName": "Gerekli izin adı", - "AllPosts": "Tüm gönderiler" + "AllPosts": "Tüm gönderiler", + "IsReadOnly": "Salt okunur" } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/vi.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/vi.json index 1b49874177..d7d608659f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/vi.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/vi.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "Chọn một blog để gán", "DeleteAllBlogPostsOfThisBlog": "Xóa tất cả bài đăng trên blog của blog này", "RequiredPermissionName": "Tên quyền cần thiết", - "AllPosts": "Tất cả bài viết" + "AllPosts": "Tất cả bài viết", + "IsReadOnly": "Chỉ đọc" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hans.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hans.json index ab61627a3c..3b4edf6e8f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hans.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hans.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "选择要分配的博客", "DeleteAllBlogPostsOfThisBlog": "删除此博客的所有博客文章", "RequiredPermissionName": "所需权限名称", - "AllPosts": "所有帖子" + "AllPosts": "所有帖子", + "IsReadOnly": "只读" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hant.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hant.json index 37ec01da26..d8b0ed9f67 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hant.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hant.json @@ -235,6 +235,7 @@ "SelectAnBlogToAssign": "選擇要分配的部落格", "DeleteAllBlogPostsOfThisBlog": "刪除此部落格的所有部落格文章", "RequiredPermissionName": "所需權限名稱", - "AllPosts": "所有文章" + "AllPosts": "所有文章", + "IsReadOnly": "唯讀" } } \ No newline at end of file