Browse Source

Merge branch 'dev' into issue-#23616

pull/24053/head
Mansur Besleney 10 months ago
committed by GitHub
parent
commit
857d3ad416
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. BIN
      docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/bento.png
  2. BIN
      docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/dark-mode.png
  3. BIN
      docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/large.png
  4. 112
      docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/post.md
  5. 21
      docs/en/modules/cms-kit/dynamic-widget.md
  6. 17
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs
  7. 3
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
  8. 10
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetryActivityEventBuilder.cs
  9. 14
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  10. 2
      framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs
  11. 5
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs
  12. 50
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs
  13. 8
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs
  14. 8
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs
  15. 13
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs
  16. 42
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs
  17. 9
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Contents/addWidgetModal.js
  18. 7
      modules/cms-kit/src/Volo.CmsKit.Common.Web/CmsKitCommonWebModule.cs
  19. 11
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Comments/CmsKitCommentConfiguration.cshtml
  20. 15
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Comments/CmsKitCommentConfigurationViewComponent.cs
  21. 14
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Comments/CmsKitCommentConfigurationViewModel.cs
  22. 23
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Components/Contents/CmsKitContentWidgetOptions.cs
  23. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ar.json
  24. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json
  25. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/de-DE.json
  26. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/de.json
  27. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/el.json
  28. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en-GB.json
  29. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json
  30. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/es.json
  31. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fa.json
  32. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fi.json
  33. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fr.json
  34. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hi.json
  35. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hr.json
  36. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hu.json
  37. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/is.json
  38. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/it.json
  39. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/nl.json
  40. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pl-PL.json
  41. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pt-BR.json
  42. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ro-RO.json
  43. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ru.json
  44. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sk.json
  45. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sl.json
  46. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sv.json
  47. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json
  48. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/vi.json
  49. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hans.json
  50. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hant.json

BIN
docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/bento.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/dark-mode.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/en/Community-Articles/2025-11-05-UI-UX-Trends-That-Will-Shape-2026/large.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

112
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.

21
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<CmsKitContentWidgetOptions>(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
- **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)`).

17
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; }
/// <summary>
@ -16,11 +13,11 @@ public interface IPermissionDefinitionContext
/// </summary>
/// <param name="name">Name of the group</param>
/// <returns></returns>
PermissionGroupDefinition GetGroup([NotNull] string name);
PermissionGroupDefinition GetGroup(string name);
/// <summary>
/// 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.
/// </summary>
/// <param name="name">Name of the group</param>
/// <returns></returns>
@ -33,7 +30,7 @@ public interface IPermissionDefinitionContext
/// <param name="displayName">Localized display name of the group</param>
/// </summary>
PermissionGroupDefinition AddGroup(
[NotNull] string name,
string name,
ILocalizableString? displayName = null);
/// <summary>
@ -44,9 +41,9 @@ public interface IPermissionDefinitionContext
void RemoveGroup(string name);
/// <summary>
/// Tries to get a pre-defined permission group.
/// Returns null if can not find the given group.
/// <param name="name">Name of the group</param>
/// Tries to get a pre-defined permission from all defined groups.
/// Returns null if it cannot find the given permission.
/// <param name="name">Name of the permission</param>
/// </summary>
PermissionDefinition? GetPermissionOrNull([NotNull] string name);
PermissionDefinition? GetPermissionOrNull(string name);
}

3
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<ILogger<AbpApplicationBase>>();
var logger = Services.GetInitLogger<AbpApplicationBase>();
logger.LogException(ex, LogLevel.Trace);
}
catch

10
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<ITelemetryActivityEventEnricher> _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<TelemetryActivityEventEnricher>;
}
}
}

14
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -287,7 +287,7 @@ public abstract class AbpDbContext<TDbContext> : 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<TDbContext> : 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<TDbContext> : 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<DisableAuditingAttribute>()).ToList();
if (disableAuditingAttributes.Any(x => x == null || x.UpdateModificationProps))
{
@ -500,6 +500,12 @@ public abstract class AbpDbContext<TDbContext> : 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();

2
framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs

@ -28,7 +28,7 @@ public class AbpAuditingTestDbContext : AbpDbContext<AbpAuditingTestDbContext>
public DbSet<AppEntityWithValueObject> AppEntityWithValueObject { get; set; }
public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; }
public DbSet<AppEntityWithNavigationChildOneToMany> AppEntityWithNavigationChildOneToMany { get; set; }
public AbpAuditingTestDbContext(DbContextOptions<AbpAuditingTestDbContext> options)
: base(options)
{

5
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<AppEntityWithNavigationsForeign>(opt =>
{
opt.DefaultWithDetailsFunc = q => q.Include(p => p.OneToMany);
});
});
context.Services.AddAbpDbContext<HostTestAppDbContext>(options =>

50
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<AppEntityWithNavigations, Guid> AppEntityWithNavigationsRepository;
protected readonly IRepository<AppEntityWithNavigationChildOneToMany, Guid> AppEntityWithNavigationChildOneToManyRepository;
protected readonly ILocalEventBus LocalEventBus;
protected readonly IRepository<Person, Guid> PersonRepository;
protected bool _loadEntityWithoutDetails = false;
@ -55,6 +56,7 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase
public AbpEfCoreDomainEvents_Tests()
{
AppEntityWithNavigationsRepository = GetRequiredService<IRepository<AppEntityWithNavigations, Guid>>();
AppEntityWithNavigationChildOneToManyRepository = GetRequiredService<IRepository<AppEntityWithNavigationChildOneToMany, Guid>>();
LocalEventBus = GetRequiredService<ILocalEventBus>();
PersonRepository = GetRequiredService<IRepository<Person, Guid>>();
}
@ -357,6 +359,22 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase
var entityId = Guid.NewGuid();
await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity")
{
OneToMany = new List<AppEntityWithNavigationChildOneToMany>()
{
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<AppEntityWithNavigationChildOneToMany>()
{
@ -367,6 +385,33 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase
}
});
var oneToManyEntity = Guid.NewGuid();
await AppEntityWithNavigationChildOneToManyRepository.InsertAsync(
new AppEntityWithNavigationChildOneToMany(oneToManyEntity)
{
AppEntityWithNavigationId = entityId,
});
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigationChildOneToMany>>(data =>
{
data.Entity.AppEntityWithNavigationId.ShouldBe(entityId2);
return Task.CompletedTask;
});
using (var scope = ServiceProvider.CreateScope())
{
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
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<EntityUpdatedEventData<AppEntityWithNavigations>>(data =>
@ -375,6 +420,11 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase
return Task.CompletedTask;
});
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigationChildOneToMany>>(data =>
{
throw new Exception("Should not trigger this event");
});
using (var scope = ServiceProvider.CreateScope())
{
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();

8
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs

@ -28,6 +28,7 @@ public class TestMigrationsDbContext : AbpDbContext<TestMigrationsDbContext>
public DbSet<Category> Categories { get; set; }
public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; }
public DbSet<AppEntityWithNavigationChildOneToMany> AppEntityWithNavigationChildOneToMany { get; set; }
public DbSet<AppEntityWithNavigationsForeign> AppEntityWithNavigationsForeign { get; set; }
@ -102,7 +103,12 @@ public class TestMigrationsDbContext : AbpDbContext<TestMigrationsDbContext>
b.HasOne(x => x.OneToOne).WithOne().HasForeignKey<AppEntityWithNavigationChildOneToOne>(x => x.Id);
b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationId);
b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity<AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany>();
b.HasOne<AppEntityWithNavigationsForeign>().WithMany().HasForeignKey(x => x.AppEntityWithNavigationForeignId).IsRequired(false);
});
modelBuilder.Entity<AppEntityWithNavigationsForeign>(b =>
{
b.ConfigureByConvention();
b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationForeignId);
});
modelBuilder.Entity<AppEntityWithNavigationChildOneToOne>(b =>

8
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs

@ -34,6 +34,7 @@ public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext,
public DbSet<Category> Categories { get; set; }
public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; }
public DbSet<AppEntityWithNavigationChildOneToMany> AppEntityWithNavigationChildOneToMany { get; set; }
public DbSet<AppEntityWithNavigationsForeign> AppEntityWithNavigationsForeign { get; set; }
@ -127,7 +128,12 @@ public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext,
b.HasOne(x => x.OneToOne).WithOne().HasForeignKey<AppEntityWithNavigationChildOneToOne>(x => x.Id);
b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationId);
b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity<AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany>();
b.HasOne<AppEntityWithNavigationsForeign>().WithMany().HasForeignKey(x => x.AppEntityWithNavigationForeignId).IsRequired(false);
});
modelBuilder.Entity<AppEntityWithNavigationsForeign>(b =>
{
b.ConfigureByConvention();
b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationForeignId);
});
modelBuilder.Entity<AppEntityWithNavigationChildOneToOne>(b =>

13
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs

@ -65,6 +65,17 @@ public class AppEntityWithNavigationChildOneToOneAndOneToOne : Entity<Guid>
public class AppEntityWithNavigationChildOneToMany : Entity<Guid>
{
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<Guid>
}
public string Name { get; set; }
public virtual List<AppEntityWithNavigations> OneToMany { get; set; }
}

42
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs

@ -347,10 +347,20 @@ public abstract class AbpEntityChangeOptions_DomainEvents_Tests<TStartupModule>
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<AppEntityWithNavigations>()
{
new AppEntityWithNavigations(Guid.NewGuid(), "TestEntity2"),
new AppEntityWithNavigations(Guid.NewGuid(), "TestEntity3")
}
});
var entityUpdatedEventTriggered = false;
var entityWithNavigationsForeignUpdatedEventTriggered = false;
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigations>>(data =>
{
@ -358,6 +368,12 @@ public abstract class AbpEntityChangeOptions_DomainEvents_Tests<TStartupModule>
return Task.CompletedTask;
});
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigationsForeign>>(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<TStartupModule>
});
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);

9
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 + "\" ";
}
}

7
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<CmsKitContentWidgetOptions>(options =>
{
options.AddWidgetIfFeatureEnabled(typeof(CommentsFeature), "Comment", "CmsCommenting", "CmsKitCommentConfiguration");
});
}
}

11
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<CmsKitResource> L
@model CmsKitCommentConfigurationViewModel
<div>
<abp-input asp-for="EntityType" label="@L["EntityType"]" />
<abp-input asp-for="EntityId" label="@L["EntityId"]" />
<abp-input asp-for="IsReadOnly" label="@L["IsReadOnly"]" />
</div>

15
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());
}
}

14
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;
}

23
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<GlobalFeatureNameAttribute>() == 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);
}
}

3
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ar.json

@ -262,6 +262,7 @@
"SelectAnBlogToAssign": "حدد مدونة لتعيين مشاركات المدونة إليها",
"DeleteAllBlogPostsOfThisBlog": "حذف جميع مشاركات المدونة",
"RequiredPermissionName": "اسم الإذن المطلوب",
"AllPosts": "جميع المشاركات"
"AllPosts": "جميع المشاركات",
"IsReadOnly": "للقراءة فقط"
}
}

3
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í"
}
}

3
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"
}
}

3
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"
}
}

3
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/el.json

@ -192,6 +192,7 @@
"SelectAnBlogToAssign": "Επιλέξτε ένα ιστολόγιο για να αναθέσετε αναρτήσεις ιστολογίου",
"DeleteAllBlogPostsOfThisBlog": "Διαγραφή όλων των αναρτήσεων ιστολογίου αυτού του ιστολογίου",
"RequiredPermissionName": "Απαιτούμενο όνομα δικαιώματος",
"AllPosts": "Όλες οι δημοσιεύσεις"
"AllPosts": "Όλες οι δημοσιεύσεις",
"IsReadOnly": "Μόνο για ανάγνωση"
}
}

3
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"
}
}

3
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"
}
}

3
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"
}
}

3
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fa.json

@ -191,6 +191,7 @@
"SelectAnBlogToAssign": "یک وبلاگ برای اختصاص دادن انتخاب کنید",
"DeleteAllBlogPostsOfThisBlog": "تمام پست های وبلاگ این وبلاگ را حذف کنید",
"RequiredPermissionName": "نام مجوز مورد نیاز",
"AllPosts": "همه پست ها"
"AllPosts": "همه پست ها",
"IsReadOnly": "فقط خواندنی"
}
}

3
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"
}
}

3
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"
}
}

3
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hi.json

@ -235,6 +235,7 @@
"SelectAnBlogToAssign": "असाइन करने के लिए एक ब्लॉग चुनें",
"DeleteAllBlogPostsOfThisBlog": "इस ब्लॉग के सभी ब्लॉग पोस्ट हटाएं",
"RequiredPermissionName": "आवश्यक अनुमति नाम",
"AllPosts": "सभी पोस्ट"
"AllPosts": "सभी पोस्ट",
"IsReadOnly": "केवल पढ़ने के लिए"
}
}

3
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"
}
}

3
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ó"
}
}

3
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ð"
}
}

3
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"
}
}

3
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"
}
}

3
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"
}
}

3
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"
}
}

3
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"
}
}

3
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ru.json

@ -235,6 +235,7 @@
"SelectAnBlogToAssign": "Выберите блог для назначения",
"DeleteAllBlogPostsOfThisBlog": "Удалить все сообщения в блоге этого блога",
"RequiredPermissionName": "Имя требуемого разрешения",
"AllPosts": "Все записи"
"AllPosts": "Все записи",
"IsReadOnly": "Только для чтения"
}
}

3
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"
}
}

3
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"
}
}

3
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"
}
}

3
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"
}
}

3
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"
}
}

3
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": "只读"
}
}

3
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": "唯讀"
}
}
Loading…
Cancel
Save