Browse Source

Merge branch 'dev' into resource-based-auth

pull/24374/head
Halil İbrahim Kalkan 11 months ago
parent
commit
c175cfbd8d
  1. BIN
      docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/cover-image.png
  2. BIN
      docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/abp-structure.png
  3. BIN
      docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/ddd-layers.png
  4. BIN
      docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/money-transfer.png
  5. BIN
      docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/service-comparison.png
  6. 592
      docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/post.md
  7. 1
      docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/summary.md
  8. 3
      docs/en/aspect-oriented-programming.md
  9. 11
      modules/cms-kit/.abpstudio/state.json
  10. 3
      modules/cms-kit/Volo.CmsKit.abpsln
  11. 999
      modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/20251024065316_Status_Field_Added_To_Pages.Designer.cs
  12. 632
      modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/20251024065316_Status_Field_Added_To_Pages.cs
  13. 93
      modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/CmsKitHttpApiHostMigrationsDbContextModelSnapshot.cs
  14. 3
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json
  15. 122
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock
  16. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Menus/PageLookupInputDto.cs
  17. 2
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/CreatePageInputDto.cs
  18. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/GetPagesInputDto.cs
  19. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/PageDto.cs
  20. 2
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/UpdatePageInputDto.cs
  21. 1
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Menus/MenuItemAdminAppService.cs
  22. 6
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs
  23. 40
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/cms-kit-admin-generate-proxy.json
  24. 14
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/Volo.CmsKit.Admin.HttpApi.Client.abppkg
  25. 5
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml
  26. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml.cs
  27. 5
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml
  28. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml.cs
  29. 12
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/create.js
  30. 8
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/index.js
  31. 12
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/update.js
  32. 13
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.abppkg
  33. 4
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/wwwroot/client-proxies/cms-kit-admin-proxy.js
  34. 3
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/PageDto.cs
  35. 14
      modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/Volo.CmsKit.Common.HttpApi.Client.abppkg
  36. 13
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Volo.CmsKit.Common.Web.abppkg
  37. 2
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json
  38. 2
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageCacheItem.cs
  39. 8
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageStatus.cs
  40. 3
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/IPageRepository.cs
  41. 11
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/Page.cs
  42. 12
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageManager.cs
  43. 1
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs
  44. 10
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Pages/EfCorePageRepository.cs
  45. 10
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Pages/MongoPageRepository.cs
  46. 12
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs
  47. 12
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json
  48. 14
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/Volo.CmsKit.Public.HttpApi.Client.abppkg
  49. 13
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.abppkg
  50. 2
      modules/cms-kit/src/Volo.CmsKit.Public.Web/wwwroot/client-proxies/cms-kit-proxy.js
  51. 2
      modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs
  52. 7
      modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs
  53. 3
      modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs
  54. 33
      npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts

BIN
docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/cover-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/abp-structure.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/ddd-layers.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/money-transfer.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/service-comparison.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

592
docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/post.md

@ -0,0 +1,592 @@
# What is That Domain Service in DDD for .NET Developers?
When you start applying **Domain-Driven Design (DDD)** in your .NET projects, you'll quickly meet some core building blocks: **Entities**, **Value Objects**, **Aggregates**, and finally… **Domain Services**.
But what exactly *is* a Domain Service, and when should you use one?
Let's break it down with practical examples and ABP Framework implementation patterns.
---
![Diagram showing layered architecture: UI, Application, Domain (Entities, Value Objects, Domain Services), Infrastructure boundaries](images/ddd-layers.png)
## The Core Idea of Domain Services
A **Domain Service** represents **a domain concept that doesn't naturally belong to a single Entity or Value Object**, but still belongs to the **domain layer** - *not* to the application or infrastructure.
In short:
> If your business logic doesn't fit into a single Entity, but still expresses a business rule, that's a good candidate for a Domain Service.
---
## Example: Money Transfer Between Accounts
Imagine a simple **banking system** where you can transfer money between accounts.
```csharp
public class Account : AggregateRoot<Guid>
{
public decimal Balance { get; private set; }
// Domain model should be created in a valid state.
public Account(decimal openingBalance = 0m)
{
if (openingBalance < 0)
throw new BusinessException("Opening balance cannot be negative.");
Balance = openingBalance;
}
public void Withdraw(decimal amount)
{
if (amount <= 0)
throw new BusinessException("Withdrawal amount must be positive.");
if (Balance < amount)
throw new BusinessException("Insufficient balance.");
Balance -= amount;
}
public void Deposit(decimal amount)
{
if (amount <= 0)
throw new BusinessException("Deposit amount must be positive.");
Balance += amount;
}
}
```
> In a richer domain you might introduce a `Money` value object (amount + currency + rounding rules) instead of a raw `decimal` for stronger invariants.
---
## Implementing a Domain Service
![Conceptual illustration showing how a domain service coordinates two aggregates](images/money-transfer.png)
```csharp
public class MoneyTransferManager : DomainService
{
public void Transfer(Account from, Account to, decimal amount)
{
if (from is null) throw new ArgumentNullException(nameof(from));
if (to is null) throw new ArgumentNullException(nameof(to));
if (ReferenceEquals(from, to))
throw new BusinessException("Cannot transfer to the same account.");
if (amount <= 0)
throw new BusinessException("Transfer amount must be positive.");
from.Withdraw(amount);
to.Deposit(amount);
}
}
```
> **Naming Convention**: ABP suggests using the `Manager` or `Service` suffix for domain services. We typically use `Manager` suffix (e.g., `IssueManager`, `OrderManager`).
> **Note**: This is a synchronous domain operation. The domain service focuses purely on business rules without infrastructure concerns like database access or event publishing. For cross-cutting concerns, use Application Service layer or domain events.
---
## Domain Service vs. Application Service
Here's a quick comparison:
![Side-by-side comparison: Domain Service (pure business rule) vs Application Service (orchestrates repositories, transactions, external systems)](images/service-comparison.png)
| Layer | Responsibility | Example |
| ----------------------- | -------------------------------------------------------------------------------- | ---------------------------- |
| **Domain Service** | Pure business rule spanning entities/aggregates | `MoneyTransferManager` |
| **Application Service** | Orchestrates use cases, handles repositories, transactions, external systems | `BankAppService` |
---
## The Application Service Layer
An **Application Service** orchestrates the domain logic and handles infrastructure concerns:
![ABP solution layout highlighting Domain layer (Entities, Value Objects, Domain Services) separate from Application and Infrastructure layers](images/abp-structure.png)
```csharp
public class BankAppService : ApplicationService
{
private readonly IRepository<Account, Guid> _accountRepository;
private readonly MoneyTransferManager _moneyTransferManager;
public BankAppService(
IRepository<Account, Guid> accountRepository,
MoneyTransferManager moneyTransferManager)
{
_accountRepository = accountRepository;
_moneyTransferManager = moneyTransferManager;
}
public async Task TransferAsync(Guid fromId, Guid toId, decimal amount)
{
var from = await _accountRepository.GetAsync(fromId);
var to = await _accountRepository.GetAsync(toId);
_moneyTransferManager.Transfer(from, to, amount);
await _accountRepository.UpdateAsync(from);
await _accountRepository.UpdateAsync(to);
}
}
```
> **Note**: Domain services are automatically registered to Dependency Injection with a **Transient** lifetime when inheriting from `DomainService`.
---
## Benefits of ABP's DomainService Base Class
The `DomainService` base class gives you access to:
- **Localization** (`IStringLocalizer L`) - Multi-language support for error messages
- **Logging** (`ILogger Logger`) - Built-in logger for tracking operations
- **Local Event Bus** (`ILocalEventBus LocalEventBus`) - Publish local domain events
- **Distributed Event Bus** (`IDistributedEventBus DistributedEventBus`) - Publish distributed events
- **GUID Generator** (`IGuidGenerator GuidGenerator`) - Sequential GUID generation for better database performance
- **Clock** (`IClock Clock`) - Abstraction for date/time operations
### Example with ABP Features
> **Important**: While domain services *can* publish domain events using the event bus, they should remain focused on business rules. Consider whether event publishing belongs in the domain service or the application service based on your consistency boundaries.
```csharp
public class MoneyTransferredEvent
{
public Guid FromAccountId { get; set; }
public Guid ToAccountId { get; set; }
public decimal Amount { get; set; }
}
public class MoneyTransferManager : DomainService
{
public async Task TransferAsync(Account from, Account to, decimal amount)
{
if (from is null) throw new ArgumentNullException(nameof(from));
if (to is null) throw new ArgumentNullException(nameof(to));
if (ReferenceEquals(from, to))
throw new BusinessException(L["SameAccountTransferNotAllowed"]);
if (amount <= 0)
throw new BusinessException(L["InvalidTransferAmount"]);
// Log the operation
Logger.LogInformation(
"Transferring {Amount} from {From} to {To}", amount, from.Id, to.Id);
from.Withdraw(amount);
to.Deposit(amount);
// Publish local event for further policies (limits, notifications, audit, etc.)
await LocalEventBus.PublishAsync(
new MoneyTransferredEvent
{
FromAccountId = from.Id,
ToAccountId = to.Id,
Amount = amount
}
);
}
}
```
> **Local Events**: By default, event handlers are executed within the same Unit of Work. If an event handler throws an exception, the database transaction is rolled back, ensuring consistency.
---
## Best Practices
### 1. Keep Domain Services Pure and Focused on Business Rules
Domain services should only contain business logic. They should not be responsible for application-level concerns like database transactions, authorization, or fetching entities from a repository.
```csharp
// Good ✅ Pure rule: receives aggregates already loaded.
public class MoneyTransferManager : DomainService
{
public void Transfer(Account from, Account to, decimal amount)
{
// Business rules and coordination
from.Withdraw(amount);
to.Deposit(amount);
}
}
// Bad ❌ Mixing application and domain concerns.
// This logic belongs in an Application Service.
public class MoneyTransferManager : DomainService
{
private readonly IRepository<Account, Guid> _accountRepository;
public MoneyTransferManager(IRepository<Account, Guid> accountRepository)
{
_accountRepository = accountRepository;
}
public async Task TransferAsync(Guid fromId, Guid toId, decimal amount)
{
// Don't fetch entities inside a domain service.
var from = await _accountRepository.GetAsync(fromId);
var to = await _accountRepository.GetAsync(toId);
from.Withdraw(amount);
to.Deposit(amount);
}
}
```
### 2. Leverage Entity Methods First
Always prefer encapsulating business logic within an entity's methods when the logic belongs to a single aggregate. A domain service should only be used when a business rule spans multiple aggregates.
```csharp
// Good ✅ - Internal state change belongs in the entity
public class Account : AggregateRoot<Guid>
{
public decimal Balance { get; private set; }
public void Withdraw(decimal amount)
{
if (Balance < amount)
throw new BusinessException("Insufficient balance");
Balance -= amount;
}
}
// Use Domain Service only when logic spans multiple aggregates
public class MoneyTransferManager : DomainService
{
public void Transfer(Account from, Account to, decimal amount)
{
from.Withdraw(amount); // Delegates to entity
to.Deposit(amount); // Delegates to entity
}
}
```
### 3. Prefer Domain Services over Anemic Entities
Avoid placing business logic that coordinates multiple entities directly into an application service. This leads to an "Anemic Domain Model," where entities are just data bags and the business logic is scattered in application services.
```csharp
// Bad ❌ - Business logic is in the Application Service (Anemic Domain)
public class BankAppService : ApplicationService
{
public async Task TransferAsync(Guid fromId, Guid toId, decimal amount)
{
var from = await _accountRepository.GetAsync(fromId);
var to = await _accountRepository.GetAsync(toId);
// This is domain logic and should be in a Domain Service
if (ReferenceEquals(from, to))
throw new BusinessException("Cannot transfer to the same account.");
if (amount <= 0)
throw new BusinessException("Transfer amount must be positive.");
from.Withdraw(amount);
to.Deposit(amount);
}
}
```
### 4. Use Meaningful Names
ABP recommends naming domain services with a `Manager` or `Service` suffix based on the business concept they represent.
```csharp
// Good ✅
MoneyTransferManager
OrderManager
IssueManager
InventoryAllocationService
// Bad ❌
AccountHelper
OrderProcessor
```
---
## Advanced Example: Order Processing with Inventory Check
Here's a more complex scenario showing domain service interaction with domain abstractions:
```csharp
// Domain abstraction - defines contract but implementation is in infrastructure
public interface IInventoryChecker : IDomainService
{
Task<bool> IsAvailableAsync(Guid productId, int quantity);
}
public class OrderManager : DomainService
{
private readonly IInventoryChecker _inventoryChecker;
public OrderManager(IInventoryChecker inventoryChecker)
{
_inventoryChecker = inventoryChecker;
}
// Validates and coordinates order processing with inventory
public async Task ProcessAsync(Order order, Inventory inventory)
{
// First pass: validate availability using domain abstraction
foreach (var item in order.Items)
{
if (!await _inventoryChecker.IsAvailableAsync(item.ProductId, item.Quantity))
{
throw new BusinessException(
L["InsufficientInventory", item.ProductId]);
}
}
// Second pass: perform reservations
foreach (var item in order.Items)
{
inventory.Reserve(item.ProductId, item.Quantity);
}
order.SetStatus(OrderStatus.Processing);
}
}
```
> **Domain Abstractions**: The `IInventoryChecker` interface is a domain service contract. Its implementation can be in the infrastructure layer, but the contract belongs to the domain. This keeps the domain layer independent of infrastructure details while still allowing complex validations.
> **Caution**: Always perform validation and action atomically within a single transaction to avoid race conditions (TOCTOU - Time Of Check Time Of Use).
> **Transaction Boundaries**: When a domain service coordinates multiple aggregates, ensure the Application Service wraps the operation in a Unit of Work to maintain consistency. ABP's `[UnitOfWork]` attribute or Application Services' built-in UoW handling ensures this automatically.
---
## Common Pitfalls and How to Avoid Them
### 1. Bloated Domain Services
Don't let domain services become "god objects" that do everything. Keep them focused on a single business concept.
```csharp
// Bad ❌ - Too many responsibilities
public class AccountManager : DomainService
{
public void Transfer(Account from, Account to, decimal amount) { }
public void CalculateInterest(Account account) { }
public void GenerateStatement(Account account) { }
public void ValidateAddress(Account account) { }
public void SendNotification(Account account) { }
}
// Good ✅ - Split by business concept
public class MoneyTransferManager : DomainService
{
public void Transfer(Account from, Account to, decimal amount) { }
}
public class InterestCalculationManager : DomainService
{
public void Calculate(Account account) { }
}
```
### 2. Circular Dependencies Between Aggregates
When domain services coordinate multiple aggregates, be careful about creating circular dependencies.
```csharp
// Consider using Domain Events instead of direct coupling
public class OrderManager : DomainService
{
public async Task ProcessAsync(Order order)
{
order.SetStatus(OrderStatus.Processing);
// Instead of directly modifying Customer aggregate here,
// publish an event that CustomerManager can handle
await LocalEventBus.PublishAsync(new OrderProcessedEvent
{
OrderId = order.Id,
CustomerId = order.CustomerId
});
}
}
```
### 3. Confusing Domain Service with Domain Event Handlers
Domain services orchestrate business operations. Domain event handlers react to state changes. Don't mix them.
```csharp
// Domain Service - Orchestrates business logic
public class MoneyTransferManager : DomainService
{
public async Task TransferAsync(Account from, Account to, decimal amount)
{
from.Withdraw(amount);
to.Deposit(amount);
await LocalEventBus.PublishAsync(
new MoneyTransferredEvent
{
FromAccountId = from.Id,
ToAccountId = to.Id,
Amount = amount
}
);
}
}
// Domain Event Handler - Reacts to domain events
public class MoneyTransferredEventHandler :
ILocalEventHandler<MoneyTransferredEvent>,
ITransientDependency
{
public async Task HandleEventAsync(MoneyTransferredEvent eventData)
{
// Send notification, update analytics, etc.
}
}
```
---
## Testing Domain Services
Domain services are easy to test because they have minimal dependencies:
```csharp
public class MoneyTransferManager_Tests
{
[Fact]
public void Should_Transfer_Money_Between_Accounts()
{
// Arrange
var fromAccount = new Account(1000m);
var toAccount = new Account(500m);
var manager = new MoneyTransferManager();
// Act
manager.Transfer(fromAccount, toAccount, 200m);
// Assert
fromAccount.Balance.ShouldBe(800m);
toAccount.Balance.ShouldBe(700m);
}
[Fact]
public void Should_Throw_When_Insufficient_Balance()
{
var fromAccount = new Account(100m);
var toAccount = new Account(500m);
var manager = new MoneyTransferManager();
Should.Throw<BusinessException>(() =>
manager.Transfer(fromAccount, toAccount, 200m));
}
[Fact]
public void Should_Throw_When_Amount_Is_NonPositive()
{
var fromAccount = new Account(100m);
var toAccount = new Account(100m);
var manager = new MoneyTransferManager();
Should.Throw<BusinessException>(() =>
manager.Transfer(fromAccount, toAccount, 0m));
Should.Throw<BusinessException>(() =>
manager.Transfer(fromAccount, toAccount, -5m));
}
[Fact]
public void Should_Throw_When_Same_Account()
{
var account = new Account(100m);
var manager = new MoneyTransferManager();
Should.Throw<BusinessException>(() =>
manager.Transfer(account, account, 10m));
}
}
```
### Integration Testing with ABP Test Infrastructure
```csharp
public class MoneyTransferManager_IntegrationTests : BankingDomainTestBase
{
private readonly MoneyTransferManager _transferManager;
private readonly IRepository<Account, Guid> _accountRepository;
public MoneyTransferManager_IntegrationTests()
{
_transferManager = GetRequiredService<MoneyTransferManager>();
_accountRepository = GetRequiredService<IRepository<Account, Guid>>();
}
[Fact]
public async Task Should_Transfer_And_Persist_Changes()
{
// Arrange
var fromAccount = new Account(1000m);
var toAccount = new Account(500m);
await _accountRepository.InsertAsync(fromAccount);
await _accountRepository.InsertAsync(toAccount);
await UnitOfWorkManager.Current.SaveChangesAsync();
// Act
await _transferManager.TransferAsync(fromAccount, toAccount, 200m);
await UnitOfWorkManager.Current.SaveChangesAsync();
// Assert
var updatedFrom = await _accountRepository.GetAsync(fromAccount.Id);
var updatedTo = await _accountRepository.GetAsync(toAccount.Id);
updatedFrom.Balance.ShouldBe(800m);
updatedTo.Balance.ShouldBe(700m);
}
}
```
---
## When NOT to Use a Domain Service
Not every operation needs a domain service. Avoid over-engineering:
1. **Simple CRUD Operations**: Use Application Services directly
2. **Single Aggregate Operations**: Use Entity methods
3. **Infrastructure Concerns**: Use Infrastructure Services
4. **Application Workflow**: Use Application Services
```csharp
// Don't create a domain service for this ❌
public class AccountBalanceReader : DomainService
{
public decimal GetBalance(Account account) => account.Balance;
}
// Just use the property directly ✅
var balance = account.Balance;
```
---
## Summary
- **Domain Services** are domain-level, not application-level
- They encapsulate **business logic that doesn't belong to a single entity**
- They keep your **entities clean** and **business logic consistent**
- In ABP, inherit from `DomainService` to get built-in features
- Keep them **focused**, **pure**, and **testable**
---
## Final Thoughts
Next time you're writing a business rule that doesn't clearly belong to an entity, ask yourself:
> "Is this a Domain Service?"
If it's pure domain logic that coordinates multiple entities or implements a business rule, **put it in the domain layer** - your future self (and your team) will thank you.
Domain Services are a powerful tool in your DDD toolkit. Use them wisely to keep your domain model clean, expressive, and maintainable.
---

1
docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/summary.md

@ -0,0 +1 @@
Learn what Domain Services are in Domain-Driven Design and when to use them in .NET projects. This practical guide covers the difference between Domain and Application Services, features real-world examples including money transfers and order processing, and shows how ABP Framework's DomainService base class simplifies implementation with built-in localization, logging, and event publishing.

3
docs/en/aspect-oriented-programming.md

@ -1,3 +0,0 @@
# Dynamic Proxying / Interceptors
This document is planned to be written later.

11
modules/cms-kit/.abpstudio/state.json

@ -0,0 +1,11 @@
{
"selectedKubernetesProfile": null,
"solutionRunner": {
"selectedProfile": null,
"targetFrameworks": [],
"applicationsStartingWithoutBuild": [],
"applicationsWithoutAutoRefreshBrowserOnRestart": [],
"applicationBatchStartStates": [],
"folderBatchStartStates": []
}
}

3
modules/cms-kit/Volo.CmsKit.abpsln

@ -3,5 +3,6 @@
"Volo.CmsKit": {
"path": "Volo.CmsKit.abpmdl"
}
}
},
"id": "aa47056c-6303-419e-bd08-cfcf1dc93ca0"
}

999
modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/20251024065316_Status_Field_Added_To_Pages.Designer.cs

@ -0,0 +1,999 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Volo.Abp.EntityFrameworkCore;
using Volo.CmsKit.EntityFrameworkCore;
#nullable disable
namespace Volo.CmsKit.Migrations
{
[DbContext(typeof(CmsKitHttpApiHostMigrationsDbContext))]
[Migration("20251024065316_Status_Field_Added_To_Pages")]
partial class Status_Field_Added_To_Pages
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<Guid>("ContainerId")
.HasColumnType("uniqueidentifier");
b.Property<byte[]>("Content")
.HasMaxLength(2147483647)
.HasColumnType("varbinary(max)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("ContainerId");
b.HasIndex("TenantId", "ContainerId", "Name");
b.ToTable("AbpBlobs", (string)null);
});
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("TenantId", "Name");
b.ToTable("AbpBlobContainers", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.Blogs.Blog", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("Slug")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.ToTable("CmsBlogs", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.Blogs.BlogFeature", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<Guid>("BlogId")
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("FeatureName")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsEnabled")
.HasColumnType("bit");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.HasKey("Id");
b.ToTable("CmsBlogFeatures", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.Blogs.BlogPost", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<Guid>("AuthorId")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("BlogId")
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("Content")
.HasMaxLength(2147483647)
.HasColumnType("nvarchar(max)");
b.Property<Guid?>("CoverImageMediaId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<int>("EntityVersion")
.HasColumnType("int");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("ShortDescription")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Slug")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("Slug", "BlogId");
b.ToTable("CmsBlogPosts", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.Comments.Comment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("EntityId")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("EntityType")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("IdempotencyToken")
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.Property<bool?>("IsApproved")
.HasColumnType("bit");
b.Property<Guid?>("RepliedCommentId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("Text")
.IsRequired()
.HasMaxLength(512)
.HasColumnType("nvarchar(512)");
b.Property<string>("Url")
.HasMaxLength(512)
.HasColumnType("nvarchar(512)");
b.HasKey("Id");
b.HasIndex("TenantId", "RepliedCommentId");
b.HasIndex("TenantId", "EntityType", "EntityId");
b.ToTable("CmsComments", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.GlobalResources.GlobalResource", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(2147483647)
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("CmsGlobalResources", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.MarkedItems.UserMarkedItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("EntityId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<string>("EntityType")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("TenantId", "EntityType", "EntityId");
b.HasIndex("TenantId", "CreatorId", "EntityType", "EntityId");
b.ToTable("CmsUserMarkedItems", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.MediaDescriptors.MediaDescriptor", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("EntityType")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("MimeType")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<long>("Size")
.HasMaxLength(2147483647)
.HasColumnType("bigint");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.ToTable("CmsMediaDescriptors", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.Menus.MenuItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("CssClass")
.HasColumnType("nvarchar(max)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("ElementId")
.HasColumnType("nvarchar(max)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Icon")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<int>("Order")
.HasColumnType("int");
b.Property<Guid?>("PageId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("ParentId")
.HasColumnType("uniqueidentifier");
b.Property<string>("RequiredPermissionName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("Target")
.HasColumnType("nvarchar(max)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("Url")
.IsRequired()
.HasMaxLength(1024)
.HasColumnType("nvarchar(1024)");
b.HasKey("Id");
b.ToTable("CmsMenuItems", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.Pages.Page", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("Content")
.HasMaxLength(2147483647)
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<int>("EntityVersion")
.HasColumnType("int");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsHomePage")
.HasColumnType("bit");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("LayoutName")
.HasColumnType("nvarchar(max)");
b.Property<string>("Script")
.HasColumnType("nvarchar(max)");
b.Property<string>("Slug")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("Style")
.HasColumnType("nvarchar(max)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("TenantId", "Slug");
b.ToTable("CmsPages", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.Ratings.Rating", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("EntityId")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("EntityType")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<short>("StarCount")
.HasColumnType("smallint");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("TenantId", "EntityType", "EntityId", "CreatorId");
b.ToTable("CmsRatings", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.Reactions.UserReaction", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("EntityId")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("EntityType")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("ReactionName")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("TenantId", "EntityType", "EntityId", "ReactionName");
b.HasIndex("TenantId", "CreatorId", "EntityType", "EntityId", "ReactionName");
b.ToTable("CmsUserReactions", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.Tags.EntityTag", b =>
{
b.Property<string>("EntityId")
.HasColumnType("nvarchar(450)");
b.Property<Guid>("TagId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("EntityId", "TagId");
b.HasIndex("TenantId", "EntityId", "TagId");
b.ToTable("CmsEntityTags", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.Tags.Tag", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("EntityType")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("TenantId", "Name");
b.ToTable("CmsTags", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.Users.CmsUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("Email")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)")
.HasColumnName("Email");
b.Property<bool>("EmailConfirmed")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("EmailConfirmed");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsActive")
.HasColumnType("bit")
.HasColumnName("IsActive");
b.Property<string>("Name")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)")
.HasColumnName("Name");
b.Property<string>("PhoneNumber")
.HasMaxLength(16)
.HasColumnType("nvarchar(16)")
.HasColumnName("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("PhoneNumberConfirmed");
b.Property<string>("Surname")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)")
.HasColumnName("Surname");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("UserName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)")
.HasColumnName("UserName");
b.HasKey("Id");
b.HasIndex("TenantId", "Email");
b.HasIndex("TenantId", "UserName");
b.ToTable("CmsUsers", (string)null);
});
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b =>
{
b.HasOne("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", null)
.WithMany()
.HasForeignKey("ContainerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Volo.CmsKit.Blogs.BlogPost", b =>
{
b.HasOne("Volo.CmsKit.Users.CmsUser", "Author")
.WithMany()
.HasForeignKey("AuthorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Author");
});
#pragma warning restore 612, 618
}
}
}

632
modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/20251024065316_Status_Field_Added_To_Pages.cs

@ -0,0 +1,632 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Volo.CmsKit.Migrations
{
/// <inheritdoc />
public partial class Status_Field_Added_To_Pages : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsUsers",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsUsers",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsTags",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsTags",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsPages",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsPages",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AddColumn<int>(
name: "EntityVersion",
table: "CmsPages",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<bool>(
name: "IsHomePage",
table: "CmsPages",
type: "bit",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<string>(
name: "LayoutName",
table: "CmsPages",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "Status",
table: "CmsPages",
type: "int",
nullable: false,
defaultValue: 1);
migrationBuilder.AlterColumn<int>(
name: "Status",
table: "CmsPages",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsMenuItems",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsMenuItems",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AddColumn<string>(
name: "RequiredPermissionName",
table: "CmsMenuItems",
type: "nvarchar(128)",
maxLength: 128,
nullable: true);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsMediaDescriptors",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsMediaDescriptors",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsGlobalResources",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsGlobalResources",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsComments",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsComments",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AddColumn<string>(
name: "IdempotencyToken",
table: "CmsComments",
type: "nvarchar(32)",
maxLength: 32,
nullable: true);
migrationBuilder.AddColumn<bool>(
name: "IsApproved",
table: "CmsComments",
type: "bit",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Url",
table: "CmsComments",
type: "nvarchar(512)",
maxLength: 512,
nullable: true);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsBlogs",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsBlogs",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsBlogPosts",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsBlogPosts",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AddColumn<int>(
name: "EntityVersion",
table: "CmsBlogPosts",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsBlogFeatures",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsBlogFeatures",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "AbpBlobs",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "AbpBlobs",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "AbpBlobContainers",
type: "nvarchar(max)",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "AbpBlobContainers",
type: "nvarchar(40)",
maxLength: 40,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.CreateTable(
name: "CmsUserMarkedItems",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
EntityId = table.Column<string>(type: "nvarchar(450)", nullable: false),
EntityType = table.Column<string>(type: "nvarchar(450)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CmsUserMarkedItems", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_CmsUserMarkedItems_TenantId_CreatorId_EntityType_EntityId",
table: "CmsUserMarkedItems",
columns: new[] { "TenantId", "CreatorId", "EntityType", "EntityId" });
migrationBuilder.CreateIndex(
name: "IX_CmsUserMarkedItems_TenantId_EntityType_EntityId",
table: "CmsUserMarkedItems",
columns: new[] { "TenantId", "EntityType", "EntityId" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CmsUserMarkedItems");
migrationBuilder.DropColumn(
name: "EntityVersion",
table: "CmsPages");
migrationBuilder.DropColumn(
name: "IsHomePage",
table: "CmsPages");
migrationBuilder.DropColumn(
name: "LayoutName",
table: "CmsPages");
migrationBuilder.DropColumn(
name: "Status",
table: "CmsPages");
migrationBuilder.DropColumn(
name: "RequiredPermissionName",
table: "CmsMenuItems");
migrationBuilder.DropColumn(
name: "IdempotencyToken",
table: "CmsComments");
migrationBuilder.DropColumn(
name: "IsApproved",
table: "CmsComments");
migrationBuilder.DropColumn(
name: "Url",
table: "CmsComments");
migrationBuilder.DropColumn(
name: "EntityVersion",
table: "CmsBlogPosts");
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsUsers",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsUsers",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsTags",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsTags",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsPages",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsPages",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsMenuItems",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsMenuItems",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsMediaDescriptors",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsMediaDescriptors",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsGlobalResources",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsGlobalResources",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsComments",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsComments",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsBlogs",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsBlogs",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsBlogPosts",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsBlogPosts",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "CmsBlogFeatures",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "CmsBlogFeatures",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "AbpBlobs",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "AbpBlobs",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
migrationBuilder.AlterColumn<string>(
name: "ExtraProperties",
table: "AbpBlobContainers",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(max)");
migrationBuilder.AlterColumn<string>(
name: "ConcurrencyStamp",
table: "AbpBlobContainers",
type: "nvarchar(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(40)",
oldMaxLength: 40);
}
}
}

93
modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/CmsKitHttpApiHostMigrationsDbContextModelSnapshot.cs

@ -19,10 +19,10 @@ namespace Volo.CmsKit.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b =>
{
@ -32,6 +32,7 @@ namespace Volo.CmsKit.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -44,6 +45,7 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("varbinary(max)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
@ -73,11 +75,13 @@ namespace Volo.CmsKit.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
@ -105,6 +109,7 @@ namespace Volo.CmsKit.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -126,6 +131,7 @@ namespace Volo.CmsKit.Migrations
.HasColumnName("DeletionTime");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
@ -173,6 +179,7 @@ namespace Volo.CmsKit.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -194,6 +201,7 @@ namespace Volo.CmsKit.Migrations
.HasColumnName("DeletionTime");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
@ -238,6 +246,7 @@ namespace Volo.CmsKit.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -265,7 +274,11 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<int>("EntityVersion")
.HasColumnType("int");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
@ -321,6 +334,7 @@ namespace Volo.CmsKit.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -344,9 +358,17 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("nvarchar(64)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("IdempotencyToken")
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.Property<bool?>("IsApproved")
.HasColumnType("bit");
b.Property<Guid?>("RepliedCommentId")
.HasColumnType("uniqueidentifier");
@ -359,6 +381,10 @@ namespace Volo.CmsKit.Migrations
.HasMaxLength(512)
.HasColumnType("nvarchar(512)");
b.Property<string>("Url")
.HasMaxLength(512)
.HasColumnType("nvarchar(512)");
b.HasKey("Id");
b.HasIndex("TenantId", "RepliedCommentId");
@ -376,6 +402,7 @@ namespace Volo.CmsKit.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -389,6 +416,7 @@ namespace Volo.CmsKit.Migrations
.HasColumnName("CreatorId");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
@ -419,6 +447,41 @@ namespace Volo.CmsKit.Migrations
b.ToTable("CmsGlobalResources", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.MarkedItems.UserMarkedItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("EntityId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<string>("EntityType")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("TenantId", "EntityType", "EntityId");
b.HasIndex("TenantId", "CreatorId", "EntityType", "EntityId");
b.ToTable("CmsUserMarkedItems", (string)null);
});
modelBuilder.Entity("Volo.CmsKit.MediaDescriptors.MediaDescriptor", b =>
{
b.Property<Guid>("Id")
@ -427,6 +490,7 @@ namespace Volo.CmsKit.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -453,6 +517,7 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("nvarchar(64)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
@ -501,6 +566,7 @@ namespace Volo.CmsKit.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -525,6 +591,7 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("nvarchar(max)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
@ -551,6 +618,10 @@ namespace Volo.CmsKit.Migrations
b.Property<Guid?>("ParentId")
.HasColumnType("uniqueidentifier");
b.Property<string>("RequiredPermissionName")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("Target")
.HasColumnType("nvarchar(max)");
@ -576,6 +647,7 @@ namespace Volo.CmsKit.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -600,7 +672,11 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<int>("EntityVersion")
.HasColumnType("int");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
@ -610,6 +686,9 @@ namespace Volo.CmsKit.Migrations
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsHomePage")
.HasColumnType("bit");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
@ -618,6 +697,9 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("LayoutName")
.HasColumnType("nvarchar(max)");
b.Property<string>("Script")
.HasColumnType("nvarchar(max)");
@ -626,6 +708,9 @@ namespace Volo.CmsKit.Migrations
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("Style")
.HasColumnType("nvarchar(max)");
@ -751,6 +836,7 @@ namespace Volo.CmsKit.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -777,6 +863,7 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("nvarchar(64)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
@ -818,6 +905,7 @@ namespace Volo.CmsKit.Migrations
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
@ -835,6 +923,7 @@ namespace Volo.CmsKit.Migrations
.HasColumnName("EmailConfirmed");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");

3
modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json

@ -5,5 +5,8 @@
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.basic": "~10.0.0-rc.2",
"@abp/cms-kit": "10.0.0-rc.2"
},
"resolutions": {
"codemirror": "^5.65.1"
}
}

122
modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock

@ -884,73 +884,6 @@
resolved "https://registry.yarnpkg.com/@aws/lambda-invoke-store/-/lambda-invoke-store-0.1.1.tgz#2e67f17040b930bde00a79ffb484eb9e77472b06"
integrity sha512-RcLam17LdlbSOSp9VxmUu1eI6Mwxp+OwhD2QhiSNmNCzoDb0EeUXTD2n/WbcnrAYMGlmf05th6QYq23VqvJqpA==
"@codemirror/autocomplete@^6.0.0":
version "6.19.1"
resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.19.1.tgz#355e49c9fd275b42a6e16e9ea0cf4361f67a3ec4"
integrity sha512-q6NenYkEy2fn9+JyjIxMWcNjzTL/IhwqfzOut1/G3PrIFkrbl4AL7Wkse5tLrQUUyqGoAKU5+Pi5jnnXxH5HGw==
dependencies:
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.17.0"
"@lezer/common" "^1.0.0"
"@codemirror/commands@^6.0.0":
version "6.10.0"
resolved "https://registry.yarnpkg.com/@codemirror/commands/-/commands-6.10.0.tgz#b3206984fec8443c4d910565eb2e9d591c7d80b2"
integrity sha512-2xUIc5mHXQzT16JnyOFkh8PvfeXuIut3pslWGfsGOhxP/lpgRm9HOl/mpzLErgt5mXDovqA0d11P21gofRLb9w==
dependencies:
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.4.0"
"@codemirror/view" "^6.27.0"
"@lezer/common" "^1.1.0"
"@codemirror/language@^6.0.0":
version "6.11.3"
resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-6.11.3.tgz#8e6632df566a7ed13a1bd307f9837765bb1abfdd"
integrity sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.23.0"
"@lezer/common" "^1.1.0"
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
style-mod "^4.0.0"
"@codemirror/lint@^6.0.0":
version "6.9.1"
resolved "https://registry.yarnpkg.com/@codemirror/lint/-/lint-6.9.1.tgz#1af2cf32dca1971b9cf8318fba0a935ddde8adf1"
integrity sha512-te7To1EQHePBQQzasDKWmK2xKINIXpk+xAiSYr9ZN+VB4KaT+/Hi2PEkeErTk5BV3PTz1TLyQL4MtJfPkKZ9sw==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.35.0"
crelt "^1.0.5"
"@codemirror/search@^6.0.0":
version "6.5.11"
resolved "https://registry.yarnpkg.com/@codemirror/search/-/search-6.5.11.tgz#a324ffee36e032b7f67aa31c4fb9f3e6f9f3ed63"
integrity sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
crelt "^1.0.5"
"@codemirror/state@^6.0.0", "@codemirror/state@^6.4.0", "@codemirror/state@^6.5.0":
version "6.5.2"
resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.5.2.tgz#8eca3a64212a83367dc85475b7d78d5c9b7076c6"
integrity sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==
dependencies:
"@marijn/find-cluster-break" "^1.0.0"
"@codemirror/view@^6.0.0", "@codemirror/view@^6.17.0", "@codemirror/view@^6.23.0", "@codemirror/view@^6.27.0", "@codemirror/view@^6.35.0":
version "6.38.6"
resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.38.6.tgz#25d9df071393801196c311025d2caa7a5523c26c"
integrity sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==
dependencies:
"@codemirror/state" "^6.5.0"
crelt "^1.0.6"
style-mod "^4.1.0"
w3c-keyname "^2.2.4"
"@fortawesome/fontawesome-free@^7.0.1":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-7.1.0.tgz#8eb76278515341720aa74485266f8be121089529"
@ -961,30 +894,6 @@
resolved "https://registry.yarnpkg.com/@highlightjs/cdn-assets/-/cdn-assets-11.11.1.tgz#136984ae467865e22080b3a4b65398a086e1ae7b"
integrity sha512-VEPdHzwelZ12hEX18BHduqxMZGolcUsrbeokHYxOUIm8X2+M7nx5QPtPeQgRxR9XjhdLv4/7DD5BWOlSrJ3k7Q==
"@lezer/common@^1.0.0", "@lezer/common@^1.1.0", "@lezer/common@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.3.0.tgz#123427ec4c53c2c8367415b4441e555b4f85c696"
integrity sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==
"@lezer/highlight@^1.0.0":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@lezer/highlight/-/highlight-1.2.3.tgz#a20f324b71148a2ea9ba6ff42e58bbfaec702857"
integrity sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==
dependencies:
"@lezer/common" "^1.3.0"
"@lezer/lr@^1.0.0":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-1.4.2.tgz#931ea3dea8e9de84e90781001dae30dea9ff1727"
integrity sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==
dependencies:
"@lezer/common" "^1.0.0"
"@marijn/find-cluster-break@^1.0.0":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz#775374306116d51c0c500b8c4face0f9a04752d8"
integrity sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==
"@sec-ant/readable-stream@^0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz#60de891bb126abfdc5410fdc6166aca065f10a0c"
@ -1942,18 +1851,10 @@ clipboard@^2.0.11:
select "^1.1.2"
tiny-emitter "^2.0.0"
codemirror@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-6.0.2.tgz#4d3fea1ad60b6753f97ca835f2f48c6936a8946e"
integrity sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/commands" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/lint" "^6.0.0"
"@codemirror/search" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
codemirror@^5.65.1, codemirror@^6.0.2:
version "5.65.20"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.20.tgz#8aa77c2dbfeb2df9b0828bf8e7d64fdd566eeff0"
integrity sha512-i5dLDDxwkFCbhjvL2pNjShsojoL3XHyDwsGv1jqETUoW+lzpBKKqNTUWgQwVAOa0tUm4BwekT455ujafi8payA==
combine-errors@^3.0.3:
version "3.0.3"
@ -1983,11 +1884,6 @@ compressorjs@^1.2.1:
blueimp-canvas-to-blob "^3.29.0"
is-blob "^2.1.0"
crelt@^1.0.5, crelt@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.6.tgz#7cc898ea74e190fb6ef9dae57f8f81cf7302df72"
integrity sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==
cropperjs@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/cropperjs/-/cropperjs-1.6.2.tgz#d1a5d627d880581cca41b7901f06923500e4201b"
@ -2611,11 +2507,6 @@ strnum@^2.1.0:
resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.1.1.tgz#cf2a6e0cf903728b8b2c4b971b7e36b4e82d46ab"
integrity sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==
style-mod@^4.0.0, style-mod@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/style-mod/-/style-mod-4.1.3.tgz#6e9012255bb799bdac37e288f7671b5d71bf9f73"
integrity sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==
sweetalert2@^11.23.0:
version "11.26.3"
resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-11.26.3.tgz#6e8188cf71818af34d62fe33a2465690cde9836d"
@ -2739,11 +2630,6 @@ url-parse@^1.5.7:
querystringify "^2.1.1"
requires-port "^1.0.0"
w3c-keyname@^2.2.4:
version "2.2.8"
resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5"
integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==
wildcard@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-1.1.2.tgz#a7020453084d8cd2efe70ba9d3696263de1710a5"

3
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Menus/PageLookupInputDto.cs

@ -1,5 +1,6 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.CmsKit.Pages;
namespace Volo.CmsKit.Admin.Menus;
@ -7,4 +8,6 @@ namespace Volo.CmsKit.Admin.Menus;
public class PageLookupInputDto : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
public PageStatus? Status { get; set; }
}

2
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/CreatePageInputDto.cs

@ -28,4 +28,6 @@ public class CreatePageInputDto: ExtensibleObject
[DynamicMaxLength(typeof(PageConsts), nameof(PageConsts.MaxStyleLength))]
public string Style { get; set; }
public PageStatus Status { get; set; } = PageStatus.Draft;
}

3
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/GetPagesInputDto.cs

@ -1,5 +1,6 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.CmsKit.Pages;
namespace Volo.CmsKit.Admin.Pages;
@ -7,4 +8,6 @@ namespace Volo.CmsKit.Admin.Pages;
public class GetPagesInputDto : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
public PageStatus? Status { get; set; } = null;
}

3
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/PageDto.cs

@ -1,6 +1,7 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Entities;
using Volo.CmsKit.Pages;
namespace Volo.CmsKit.Admin.Pages;
@ -21,5 +22,7 @@ public class PageDto : ExtensibleAuditedEntityDto<Guid>, IHasConcurrencyStamp
public bool IsHomePage { get; set; }
public PageStatus Status { get; set; }
public string ConcurrencyStamp { get; set; }
}

2
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/UpdatePageInputDto.cs

@ -30,5 +30,7 @@ public class UpdatePageInputDto : ExtensibleObject, IHasConcurrencyStamp
[DynamicMaxLength(typeof(PageConsts), nameof(PageConsts.MaxStyleLength))]
public string Style { get; set; }
public PageStatus Status { get; set; }
public string ConcurrencyStamp { get; set; }
}

1
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Menus/MenuItemAdminAppService.cs

@ -135,6 +135,7 @@ public class MenuItemAdminAppService : CmsKitAdminAppServiceBase, IMenuItemAdmin
var pages = await PageRepository.GetListAsync(
input.Filter,
input.Status,
input.MaxResultCount,
input.SkipCount,
input.Sorting

6
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs

@ -49,10 +49,11 @@ public class PageAdminAppService : CmsKitAdminAppServiceBase, IPageAdminAppServi
public virtual async Task<PagedResultDto<PageDto>> GetListAsync(GetPagesInputDto input)
{
var count = await PageRepository.GetCountAsync(input.Filter);
var count = await PageRepository.GetCountAsync(input.Filter, input.Status);
var pages = await PageRepository.GetListAsync(
input.Filter,
input.Status,
input.MaxResultCount,
input.SkipCount,
input.Sorting
@ -67,7 +68,7 @@ public class PageAdminAppService : CmsKitAdminAppServiceBase, IPageAdminAppServi
[Authorize(CmsKitAdminPermissions.Pages.Create)]
public virtual async Task<PageDto> CreateAsync(CreatePageInputDto input)
{
var page = await PageManager.CreateAsync(input.Title, input.Slug, input.Content, input.Script, input.Style, input.LayoutName);
var page = await PageManager.CreateAsync(input.Title, input.Slug, input.Content, input.Script, input.Style, input.LayoutName, input.Status);
input.MapExtraPropertiesTo(page);
await PageRepository.InsertAsync(page);
@ -94,6 +95,7 @@ public class PageAdminAppService : CmsKitAdminAppServiceBase, IPageAdminAppServi
page.SetScript(input.Script);
page.SetStyle(input.Style);
page.SetLayoutName(input.LayoutName);
await PageManager.SetStatusAsync(page, input.Status);
page.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);
input.MapExtraPropertiesTo(page);

40
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/cms-kit-admin-generate-proxy.json

@ -37,7 +37,7 @@
},
{
"name": "assignToBlogId",
"typeAsString": "System.Nullable`1[[System.Guid, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib",
"typeAsString": "System.Nullable`1[[System.Guid, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib",
"type": "System.Guid?",
"typeSimple": "string?",
"isOptional": true,
@ -419,7 +419,7 @@
},
{
"name": "assignToBlogId",
"typeAsString": "System.Nullable`1[[System.Guid, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib",
"typeAsString": "System.Nullable`1[[System.Guid, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib",
"type": "System.Guid?",
"typeSimple": "string?",
"isOptional": false,
@ -1011,8 +1011,8 @@
"nameOnMethod": "input",
"name": "Status",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"type": "Volo.CmsKit.Blogs.BlogPostStatus?",
"typeSimple": "enum?",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
@ -1532,8 +1532,8 @@
"nameOnMethod": "input",
"name": "CommentApproveState",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"type": "Volo.CmsKit.Comments.CommentApproveState",
"typeSimple": "enum",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
@ -2190,7 +2190,7 @@
"parametersOnMethod": [
{
"name": "parentId",
"typeAsString": "System.Nullable`1[[System.Guid, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib",
"typeAsString": "System.Nullable`1[[System.Guid, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib",
"type": "System.Guid?",
"typeSimple": "string?",
"isOptional": true,
@ -2475,6 +2475,18 @@
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "Status",
"jsonName": null,
"type": "Volo.CmsKit.Pages.PageStatus?",
"typeSimple": "enum?",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "Sorting",
@ -2565,7 +2577,7 @@
"parametersOnMethod": [
{
"name": "parentId",
"typeAsString": "System.Nullable`1[[System.Guid, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib",
"typeAsString": "System.Nullable`1[[System.Guid, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib",
"type": "System.Guid?",
"typeSimple": "string?",
"isOptional": true,
@ -2787,6 +2799,18 @@
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "Status",
"jsonName": null,
"type": "Volo.CmsKit.Pages.PageStatus?",
"typeSimple": "enum?",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "Sorting",

14
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/Volo.CmsKit.Admin.HttpApi.Client.abppkg

@ -1,3 +1,15 @@
{
"role": "lib.http-api-client"
"role": "lib.http-api-client",
"proxies": {
"csharp": {
"Volo.CmsKit.Web.Unified-cms-kit-admin": {
"applicationName": "Volo.CmsKit.Web.Unified",
"module": "cms-kit-admin",
"url": "https://localhost:44349",
"folder": "ClientProxies",
"serviceType": "all",
"withoutContracts": true
}
}
}
}

5
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml

@ -64,6 +64,8 @@
<abp-select id="Layout" asp-for="ViewModel.LayoutName" asp-items="@LayoutConstants.GetLayoutsSelectList()" label="@L["SelectLayout"]"></abp-select>
<abp-input asp-for="@Model.ViewModel.Status" />
<abp-input asp-for="@Model.ViewModel.Content" />
<abp-tabs tab-style="Tab">
@ -119,7 +121,8 @@
</form>
</abp-card-body>
<abp-card-footer>
<abp-button button-type="Primary" type="submit" text="@L["Submit"].Value" id="button-page-create" />
<abp-button button-type="Secondary" type="button" text="@L["SaveAsDraft"].Value" id="button-page-save-draft" />
<abp-button button-type="Primary" type="button" text="@L["Publish"].Value" id="button-page-publish" />
</abp-card-footer>
</abp-card>
<input hidden id="GeneratedWidgetText" />

3
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml.cs

@ -56,5 +56,8 @@ public class CreateModel : CmsKitAdminPageModel
[TextArea(Rows = 6)]
[DynamicMaxLength(typeof(PageConsts), nameof(PageConsts.MaxStyleLength))]
public string Style { get; set; }
[HiddenInput]
public PageStatus Status { get; set; } = PageStatus.Draft;
}
}

5
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml

@ -69,6 +69,8 @@
<abp-select id="Layout" asp-for="ViewModel.LayoutName" asp-items="@LayoutConstants.GetLayoutsSelectList()" label="@L["SelectLayout"]"></abp-select>
<abp-input asp-for="@Model.ViewModel.Status" />
<abp-input asp-for="@Model.ViewModel.Content" />
<abp-tabs tab-style="Tab">
@ -125,7 +127,8 @@
</form>
</abp-card-body>
<abp-card-footer>
<abp-button button-type="Primary" type="submit" text="@L["Submit"].Value" id="button-page-update" />
<abp-button button-type="Secondary" type="button" text="@L["SaveAsDraft"].Value" id="button-page-save-draft" />
<abp-button button-type="Primary" type="button" text="@L["Publish"].Value" id="button-page-publish" />
</abp-card-footer>
</abp-card>
<input hidden id="GeneratedWidgetText" />

3
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml.cs

@ -69,6 +69,9 @@ public class UpdateModel : CmsKitAdminPageModel
[DynamicMaxLength(typeof(PageConsts), nameof(PageConsts.MaxStyleLength))]
public string Style { get; set; }
[HiddenInput]
public PageStatus Status { get; set; }
[HiddenInput]
public string ConcurrencyStamp { get; set; }
}

12
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/create.js

@ -4,7 +4,8 @@ $(function () {
var $createForm = $('#form-page-create');
var $title = $('#ViewModel_Title');
var $slug = $('#ViewModel_Slug');
var $buttonSubmit = $('#button-page-create');
var $buttonSaveDraft = $('#button-page-save-draft');
var $buttonPublish = $('#button-page-publish');
var widgetModal = new abp.ModalManager({ viewUrl: abp.appPath + "CmsKit/Contents/AddWidgetModal", modalClass: "addWidgetModal" });
@ -49,8 +50,15 @@ $(function () {
}
});
$buttonSubmit.click(function (e) {
$buttonSaveDraft.click(function (e) {
e.preventDefault();
$('#ViewModel_Status').val(0); // Draft = 0
$createForm.submit();
});
$buttonPublish.click(function (e) {
e.preventDefault();
$('#ViewModel_Status').val(1); // Published = 1
$createForm.submit();
});

8
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/index.js

@ -75,6 +75,14 @@ $(function () {
orderable: true,
data: "slug"
},
{
title: l("Status"),
orderable: true,
data: "status",
render: function (data) {
return l('Enum:PageStatus:' + data);
}
},
{
title: l("IsHomePage"),
orderable: true,

12
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/update.js

@ -3,7 +3,8 @@ $(function () {
var l = abp.localization.getResource("CmsKit");
var $formUpdate = $('#form-page-update');
var $buttonSubmit = $('#button-page-update');
var $buttonSaveDraft = $('#button-page-save-draft');
var $buttonPublish = $('#button-page-publish');
var widgetModal = new abp.ModalManager({ viewUrl: abp.appPath + "CmsKit/Contents/AddWidgetModal", modalClass: "addWidgetModal" });
$formUpdate.data('validator').settings.ignore = ":hidden, [contenteditable='true']:not([name]), .tui-popup-wrapper";
@ -43,8 +44,15 @@ $(function () {
}
});
$buttonSubmit.click(function (e) {
$buttonSaveDraft.click(function (e) {
e.preventDefault();
$('#ViewModel_Status').val(0); // Draft = 0
$formUpdate.submit();
});
$buttonPublish.click(function (e) {
e.preventDefault();
$('#ViewModel_Status').val(1); // Published = 1
$formUpdate.submit();
});

13
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.abppkg

@ -2,7 +2,18 @@
"role": "lib.mvc",
"npmDependencies": {
"@abp/cms-kit.admin": {
"version": ""
"version": ""
}
},
"proxies": {
"Javascript": {
"Volo.CmsKit.Web.Unified-cms-kit-admin": {
"applicationName": "Volo.CmsKit.Web.Unified",
"module": "cms-kit-admin",
"url": "https://localhost:44349",
"output": "wwwroot/client-proxies/",
"serviceType": "application"
}
}
}
}

4
modules/cms-kit/src/Volo.CmsKit.Admin.Web/wwwroot/client-proxies/cms-kit-admin-proxy.js

@ -340,7 +340,7 @@
volo.cmsKit.admin.menus.menuItemAdmin.getPageLookup = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/cms-kit-admin/menu-items/lookup/pages' + abp.utils.buildQueryString([{ name: 'filter', value: input.filter }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '',
url: abp.appPath + 'api/cms-kit-admin/menu-items/lookup/pages' + abp.utils.buildQueryString([{ name: 'filter', value: input.filter }, { name: 'status', value: input.status }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '',
type: 'GET'
}, ajaxParams));
};
@ -376,7 +376,7 @@
volo.cmsKit.admin.pages.pageAdmin.getList = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/cms-kit-admin/pages' + abp.utils.buildQueryString([{ name: 'filter', value: input.filter }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '',
url: abp.appPath + 'api/cms-kit-admin/pages' + abp.utils.buildQueryString([{ name: 'filter', value: input.filter }, { name: 'status', value: input.status }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '',
type: 'GET'
}, ajaxParams));
};

3
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/PageDto.cs

@ -1,5 +1,6 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.CmsKit.Pages;
namespace Volo.CmsKit.Contents;
@ -16,4 +17,6 @@ public class PageDto : ExtensibleEntityDto<Guid>
public string Script { get; set; }
public string Style { get; set; }
public PageStatus Status { get; set; }
}

14
modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/Volo.CmsKit.Common.HttpApi.Client.abppkg

@ -1,3 +1,15 @@
{
"role": "lib.http-api-client"
"role": "lib.http-api-client",
"proxies": {
"csharp": {
"Volo.CmsKit.Web.Unified-cms-kit-common": {
"applicationName": "Volo.CmsKit.Web.Unified",
"module": "cms-kit-common",
"url": "https://localhost:44349",
"folder": "ClientProxies",
"serviceType": "all",
"withoutContracts": true
}
}
}
}

13
modules/cms-kit/src/Volo.CmsKit.Common.Web/Volo.CmsKit.Common.Web.abppkg

@ -1,3 +1,14 @@
{
"role": "lib.mvc"
"role": "lib.mvc",
"proxies": {
"Javascript": {
"Volo.CmsKit.Web.Unified-cms-kit-common": {
"applicationName": "Volo.CmsKit.Web.Unified",
"module": "cms-kit-common",
"url": "https://localhost:44349",
"output": "wwwroot/client-proxies/",
"serviceType": "application"
}
}
}
}

2
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json

@ -280,6 +280,8 @@
"SelectAnBlogToAssign": "Select a blog to assign",
"DeleteAllBlogPostsOfThisBlog": "Delete all blog posts of this blog",
"RequiredPermissionName": "Required permission name",
"Enum:PageStatus:0": "Draft",
"Enum:PageStatus:1": "Publish",
"AllPosts": "All posts",
"IsReadOnly": "Readonly"
}

2
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageCacheItem.cs

@ -20,6 +20,8 @@ public class PageCacheItem : ExtensibleObject
public string Style { get; set; }
public PageStatus Status { get; set; }
public static string GetKey(string slug)
{
return $"CmsPage_{slug}";

8
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageStatus.cs

@ -0,0 +1,8 @@
namespace Volo.CmsKit.Pages;
public enum PageStatus
{
Draft = 0,
Publish = 1
}

3
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/IPageRepository.cs

@ -8,10 +8,11 @@ namespace Volo.CmsKit.Pages;
public interface IPageRepository : IBasicRepository<Page, Guid>
{
Task<int> GetCountAsync(string filter = null, CancellationToken cancellationToken = default);
Task<int> GetCountAsync(string filter = null, PageStatus? status = null, CancellationToken cancellationToken = default);
Task<List<Page>> GetListAsync(
string filter = null,
PageStatus? status = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string sorting = null,

11
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/Page.cs

@ -27,6 +27,8 @@ public class Page : FullAuditedAggregateRoot<Guid>, IMultiTenant, IHasEntityVers
public virtual string LayoutName { get; protected set; }
public virtual PageStatus Status { get; protected set; }
protected Page()
{
}
@ -39,7 +41,8 @@ public class Page : FullAuditedAggregateRoot<Guid>, IMultiTenant, IHasEntityVers
string script = null,
string style = null,
string layoutName = null,
Guid? tenantId = null) : base(id)
Guid? tenantId = null,
PageStatus status = PageStatus.Draft) : base(id)
{
TenantId = tenantId;
@ -49,6 +52,7 @@ public class Page : FullAuditedAggregateRoot<Guid>, IMultiTenant, IHasEntityVers
SetScript(script);
SetStyle(style);
SetLayoutName(layoutName);
SetStatus(status);
}
public virtual void SetTitle(string title)
@ -85,4 +89,9 @@ public class Page : FullAuditedAggregateRoot<Guid>, IMultiTenant, IHasEntityVers
{
IsHomePage = isHomePage;
}
public virtual void SetStatus(PageStatus status)
{
Status = status;
}
}

12
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageManager.cs

@ -21,7 +21,8 @@ public class PageManager : DomainService
[CanBeNull] string content = null,
[CanBeNull] string script = null,
[CanBeNull] string style = null,
[CanBeNull] string layoutName = null)
[CanBeNull] string layoutName = null,
PageStatus status = PageStatus.Draft)
{
Check.NotNullOrEmpty(title, nameof(title));
Check.NotNullOrEmpty(slug, nameof(slug));
@ -36,7 +37,8 @@ public class PageManager : DomainService
script,
style,
layoutName,
CurrentTenant.Id);
CurrentTenant.Id,
status);
}
public virtual async Task SetSlugAsync(Page page, [NotNull] string newSlug)
@ -48,6 +50,12 @@ public class PageManager : DomainService
}
}
public virtual Task SetStatusAsync(Page page, PageStatus status)
{
page.SetStatus(status);
return Task.CompletedTask;
}
public virtual async Task SetHomePageAsync(Page page)
{
var homePage = await GetHomePageAsync();

1
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs

@ -168,6 +168,7 @@ public static class CmsKitDbContextModelCreatingExtensions
b.Property(x => x.Title).IsRequired().HasMaxLength(PageConsts.MaxTitleLength);
b.Property(x => x.Slug).IsRequired().HasMaxLength(PageConsts.MaxSlugLength);
b.Property(x => x.Content).HasMaxLength(PageConsts.MaxContentLength);
b.Property(x => x.Status).IsRequired();
b.HasIndex(x => new { x.TenantId, Url = x.Slug });

10
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Pages/EfCorePageRepository.cs

@ -20,17 +20,20 @@ public class EfCorePageRepository : EfCoreRepository<ICmsKitDbContext, Page, Gui
}
public virtual async Task<int> GetCountAsync(string filter = null,
PageStatus? status = null,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync()).WhereIf(
!filter.IsNullOrWhiteSpace(),
x =>
x.Title.ToLower().Contains(filter.ToLower()) || x.Slug.Contains(filter)
).CountAsync(GetCancellationToken(cancellationToken));
x.Title.ToLower().Contains(filter.ToLower()) || x.Slug.ToLower().Contains(filter.ToLower())
).WhereIf(status.HasValue, x => x.Status == status)
.CountAsync(GetCancellationToken(cancellationToken));
}
public virtual async Task<List<Page>> GetListAsync(
string filter = null,
PageStatus? status = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string sorting = null,
@ -39,7 +42,8 @@ public class EfCorePageRepository : EfCoreRepository<ICmsKitDbContext, Page, Gui
return await (await GetDbSetAsync()).WhereIf(
!filter.IsNullOrWhiteSpace(),
x =>
x.Title.ToLower().Contains(filter.ToLower()) || x.Slug.Contains(filter))
x.Title.ToLower().Contains(filter.ToLower()) || x.Slug.ToLower().Contains(filter.ToLower()))
.WhereIf(status.HasValue, x => x.Status == status)
.OrderBy(sorting.IsNullOrEmpty() ? nameof(Page.Title) : sorting)
.PageBy(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));

10
modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Pages/MongoPageRepository.cs

@ -22,6 +22,7 @@ public class MongoPageRepository : MongoDbRepository<ICmsKitMongoDbContext, Page
}
public virtual async Task<int> GetCountAsync(string filter = null,
PageStatus? status = null,
CancellationToken cancellationToken = default)
{
var cancellation = GetCancellationToken(cancellationToken);
@ -30,12 +31,14 @@ public class MongoPageRepository : MongoDbRepository<ICmsKitMongoDbContext, Page
.WhereIf(
!filter.IsNullOrWhiteSpace(),
u =>
u.Title.ToLower().Contains(filter.ToLower()) || u.Slug.Contains(filter)
).CountAsync(cancellation);
u.Title.ToLower().Contains(filter.ToLower()) || u.Slug.ToLower().Contains(filter.ToLower())
).WhereIf(status.HasValue, u => u.Status == status)
.CountAsync(cancellation);
}
public virtual async Task<List<Page>> GetListAsync(
string filter = null,
PageStatus? status = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string sorting = null,
@ -46,7 +49,8 @@ public class MongoPageRepository : MongoDbRepository<ICmsKitMongoDbContext, Page
return await (await GetQueryableAsync(cancellation))
.WhereIf(
!filter.IsNullOrWhiteSpace(),
u => u.Title.ToLower().Contains(filter) || u.Slug.Contains(filter))
u => u.Title.ToLower().Contains(filter.ToLower()) || u.Slug.ToLower().Contains(filter.ToLower()))
.WhereIf(status.HasValue, u => u.Status == status)
.OrderBy(sorting.IsNullOrEmpty() ? nameof(Page.Title) : sorting)
.PageBy(skipCount, maxResultCount)
.ToListAsync(cancellation);

12
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs

@ -54,6 +54,12 @@ public class PagePublicAppService : CmsKitPublicAppServiceBase, IPagePublicAppSe
return null;
}
// Only return published home page
if (page.Status != PageStatus.Publish)
{
return null;
}
pageCacheItem = ObjectMapper.Map<Page, PageCacheItem>(page);
await PageCache.SetAsync(PageCacheItem.GetKey(PageConsts.DefaultHomePageCacheKey), pageCacheItem,
@ -81,6 +87,12 @@ public class PagePublicAppService : CmsKitPublicAppServiceBase, IPagePublicAppSe
return null;
}
// Only return published pages
if (page.Status != PageStatus.Publish)
{
return null;
}
return ObjectMapper.Map<Page, PageCacheItem>(page);
});

12
modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json

@ -256,6 +256,18 @@
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "FilterOnFavorites",
"jsonName": null,
"type": "System.Boolean?",
"typeSimple": "boolean?",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "Sorting",

14
modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/Volo.CmsKit.Public.HttpApi.Client.abppkg

@ -1,3 +1,15 @@
{
"role": "lib.http-api-client"
"role": "lib.http-api-client",
"proxies": {
"csharp": {
"Volo.CmsKit.Web.Unified-cms-kit": {
"applicationName": "Volo.CmsKit.Web.Unified",
"module": "cms-kit",
"url": "https://localhost:44349",
"folder": "ClientProxies",
"serviceType": "all",
"withoutContracts": true
}
}
}
}

13
modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.abppkg

@ -2,7 +2,18 @@
"role": "lib.mvc",
"npmDependencies": {
"@abp/cms-kit.public": {
"version": ""
"version": ""
}
},
"proxies": {
"Javascript": {
"Volo.CmsKit.Web.Unified-cms-kit": {
"applicationName": "Volo.CmsKit.Web.Unified",
"module": "cms-kit",
"url": "https://localhost:44349",
"output": "wwwroot/client-proxies/",
"serviceType": "application"
}
}
}
}

2
modules/cms-kit/src/Volo.CmsKit.Public.Web/wwwroot/client-proxies/cms-kit-proxy.js

@ -20,7 +20,7 @@
volo.cmsKit.public.blogs.blogPostPublic.getList = function(blogSlug, input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/cms-kit-public/blog-posts/' + blogSlug + '' + abp.utils.buildQueryString([{ name: 'authorId', value: input.authorId }, { name: 'tagId', value: input.tagId }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '',
url: abp.appPath + 'api/cms-kit-public/blog-posts/' + blogSlug + '' + abp.utils.buildQueryString([{ name: 'authorId', value: input.authorId }, { name: 'tagId', value: input.tagId }, { name: 'filterOnFavorites', value: input.filterOnFavorites }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '',
type: 'GET'
}, ajaxParams));
};

2
modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs

@ -358,7 +358,7 @@ public class CmsKitDataSeedContributor : IDataSeedContributor, ITransientDepende
private async Task SeedPagesAsync()
{
var page1 = new Page(_cmsKitTestData.Page_1_Id, _cmsKitTestData.Page_1_Title, _cmsKitTestData.Page_1_Slug, _cmsKitTestData.Content_1);
var page1 = new Page(_cmsKitTestData.Page_1_Id, _cmsKitTestData.Page_1_Title, _cmsKitTestData.Page_1_Slug, _cmsKitTestData.Content_1, status:_cmsKitTestData.Page_1_Status);
await _pageRepository.InsertAsync(page1);
var page2 = new Page(_cmsKitTestData.Page_2_Id, _cmsKitTestData.Page_2_Title, _cmsKitTestData.Page_2_Slug, _cmsKitTestData.Content_2);

7
modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using System.Globalization;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Volo.Abp;
using Volo.Abp.Authorization;
@ -23,6 +24,10 @@ public class CmsKitTestBaseModule : AbpModule
public override void PreConfigureServices(ServiceConfigurationContext context)
{
// Set culture to InvariantCulture to avoid Turkish-I problem and other culture-specific issues in tests
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
OneTimeRunner.Run(() =>
{
GlobalFeatureManager.Instance.Modules.CmsKit().EnableAll();

3
modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs

@ -2,6 +2,7 @@
using JetBrains.Annotations;
using Volo.Abp.DependencyInjection;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Pages;
namespace Volo.CmsKit;
@ -47,6 +48,8 @@ public class CmsKitTestData : ISingletonDependency
public string Page_1_Title { get; } = "Imagine Dragons - Believer Lyrics";
public string Page_1_Slug { get; } = "imagine-dragons-believer-lyrics";
public PageStatus Page_1_Status { get; } = PageStatus.Publish;
public Guid Page_1_Id { get; } = Guid.NewGuid();

33
npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts

@ -303,7 +303,7 @@ export function addRoutingToAppRoutingModule(
? `() => import('${routePath}').then(m => m.${macroName}_ROUTES)`
: `() => import('${routePath}').then(m => m.${moduleName}.forLazy())`;
const routeToAdd = `{ path: '${routePath}', loadChildren: ${routeExpr} }`;
const change = addRouteToRoutesArray(source, 'APP_ROUTES', routeToAdd);
const change = addRouteToRoutesArray(source, routeToAdd);
if (change instanceof InsertChange) {
const recorder = tree.beginUpdate(appRoutesPath);
@ -355,29 +355,30 @@ export function addRoutingToAppRoutingModule(
export function addRouteToRoutesArray(
source: ts.SourceFile,
arrayName: string,
routeToAdd: string,
): InsertChange | null {
const routesVar = source.statements.find(
stmt =>
ts.isVariableStatement(stmt) &&
stmt.declarationList.declarations.some(
// Find all variable declarations that are array literals and contain route definitions
const routeArrays = source.statements
.filter(ts.isVariableStatement)
.flatMap(stmt =>
stmt.declarationList.declarations.filter(
decl =>
ts.isVariableDeclaration(decl) &&
(decl.name.getText() === arrayName || decl.name.getText() === arrayName.toUpperCase()) &&
decl.initializer !== undefined &&
ts.isArrayLiteralExpression(decl.initializer),
decl.initializer &&
ts.isArrayLiteralExpression(decl.initializer) &&
decl.initializer.elements.some(el =>
el.getText().includes('path:')
),
),
);
);
if (!routesVar || !ts.isVariableStatement(routesVar)) {
throw new Error(`Could not find routes array named "${arrayName}".`);
if (routeArrays.length === 0) {
throw new Error('No routes array found in source file.');
}
const declaration = routesVar.declarationList.declarations.find(
decl => decl.name.getText() === arrayName,
) as ts.VariableDeclaration;
// Select the first routes array found
const declaration = routeArrays[0];
const arrayLiteral = declaration.initializer as ts.ArrayLiteralExpression;
const getPathValue = (routeText: string): string | null => {

Loading…
Cancel
Save