diff --git a/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/cover-image.png b/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/cover-image.png new file mode 100644 index 0000000000..5ee2f50934 Binary files /dev/null and b/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/cover-image.png differ diff --git a/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/abp-structure.png b/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/abp-structure.png new file mode 100644 index 0000000000..5c5639839c Binary files /dev/null and b/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/abp-structure.png differ diff --git a/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/ddd-layers.png b/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/ddd-layers.png new file mode 100644 index 0000000000..7307f1cbab Binary files /dev/null and b/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/ddd-layers.png differ diff --git a/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/money-transfer.png b/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/money-transfer.png new file mode 100644 index 0000000000..2ebf3b4fef Binary files /dev/null and b/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/money-transfer.png differ diff --git a/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/service-comparison.png b/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/service-comparison.png new file mode 100644 index 0000000000..498a438502 Binary files /dev/null and b/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/images/service-comparison.png differ diff --git a/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/post.md b/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/post.md new file mode 100644 index 0000000000..7eca19a652 --- /dev/null +++ b/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 +{ + 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 _accountRepository; + private readonly MoneyTransferManager _moneyTransferManager; + + public BankAppService( + IRepository 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 _accountRepository; + + public MoneyTransferManager(IRepository 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 +{ + 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 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, + 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(() => + 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(() => + manager.Transfer(fromAccount, toAccount, 0m)); + Should.Throw(() => + manager.Transfer(fromAccount, toAccount, -5m)); + } + + [Fact] + public void Should_Throw_When_Same_Account() + { + var account = new Account(100m); + var manager = new MoneyTransferManager(); + + Should.Throw(() => + manager.Transfer(account, account, 10m)); + } +} +``` + +### Integration Testing with ABP Test Infrastructure + +```csharp +public class MoneyTransferManager_IntegrationTests : BankingDomainTestBase +{ + private readonly MoneyTransferManager _transferManager; + private readonly IRepository _accountRepository; + + public MoneyTransferManager_IntegrationTests() + { + _transferManager = GetRequiredService(); + _accountRepository = GetRequiredService>(); + } + + [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. + +--- diff --git a/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/summary.md b/docs/en/Community-Articles/2025-11-08-what-is-that-domain-service-in-ddd-for-net-developers/summary.md new file mode 100644 index 0000000000..a047be4def --- /dev/null +++ b/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. diff --git a/docs/en/aspect-oriented-programming.md b/docs/en/aspect-oriented-programming.md deleted file mode 100644 index c578f1e89c..0000000000 --- a/docs/en/aspect-oriented-programming.md +++ /dev/null @@ -1,3 +0,0 @@ -# Dynamic Proxying / Interceptors - -This document is planned to be written later. \ No newline at end of file diff --git a/modules/cms-kit/.abpstudio/state.json b/modules/cms-kit/.abpstudio/state.json new file mode 100644 index 0000000000..b0ef88d2f2 --- /dev/null +++ b/modules/cms-kit/.abpstudio/state.json @@ -0,0 +1,11 @@ +{ + "selectedKubernetesProfile": null, + "solutionRunner": { + "selectedProfile": null, + "targetFrameworks": [], + "applicationsStartingWithoutBuild": [], + "applicationsWithoutAutoRefreshBrowserOnRestart": [], + "applicationBatchStartStates": [], + "folderBatchStartStates": [] + } +} \ No newline at end of file diff --git a/modules/cms-kit/Volo.CmsKit.abpsln b/modules/cms-kit/Volo.CmsKit.abpsln index 3448e82475..ede7cc2be4 100644 --- a/modules/cms-kit/Volo.CmsKit.abpsln +++ b/modules/cms-kit/Volo.CmsKit.abpsln @@ -3,5 +3,6 @@ "Volo.CmsKit": { "path": "Volo.CmsKit.abpmdl" } - } + }, + "id": "aa47056c-6303-419e-bd08-cfcf1dc93ca0" } \ No newline at end of file diff --git a/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/20251024065316_Status_Field_Added_To_Pages.Designer.cs b/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/20251024065316_Status_Field_Added_To_Pages.Designer.cs new file mode 100644 index 0000000000..d7e470083f --- /dev/null +++ b/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/20251024065316_Status_Field_Added_To_Pages.Designer.cs @@ -0,0 +1,999 @@ +// +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 + { + /// + 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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContainerId") + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .HasMaxLength(2147483647) + .HasColumnType("varbinary(max)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("CmsBlogs", (string)null); + }); + + modelBuilder.Entity("Volo.CmsKit.Blogs.BlogFeature", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BlogId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FeatureName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.HasKey("Id"); + + b.ToTable("CmsBlogFeatures", (string)null); + }); + + modelBuilder.Entity("Volo.CmsKit.Blogs.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorId") + .HasColumnType("uniqueidentifier"); + + b.Property("BlogId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("CoverImageMediaId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ShortDescription") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IdempotencyToken") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("IsApproved") + .HasColumnType("bit"); + + b.Property("RepliedCommentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("CmsGlobalResources", (string)null); + }); + + modelBuilder.Entity("Volo.CmsKit.MarkedItems.UserMarkedItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("EntityId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("EntityType") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Size") + .HasMaxLength(2147483647) + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("CmsMediaDescriptors", (string)null); + }); + + modelBuilder.Entity("Volo.CmsKit.Menus.MenuItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CssClass") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ElementId") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Icon") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("PageId") + .HasColumnType("uniqueidentifier"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("RequiredPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.HasKey("Id"); + + b.ToTable("CmsMenuItems", (string)null); + }); + + modelBuilder.Entity("Volo.CmsKit.Pages.Page", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsHomePage") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LayoutName") + .HasColumnType("nvarchar(max)"); + + b.Property("Script") + .HasColumnType("nvarchar(max)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Style") + .HasColumnType("nvarchar(max)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("StarCount") + .HasColumnType("smallint"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ReactionName") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("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("EntityId") + .HasColumnType("nvarchar(450)"); + + b.Property("TagId") + .HasColumnType("uniqueidentifier"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("bit") + .HasColumnName("IsActive"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("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 + } + } +} diff --git a/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/20251024065316_Status_Field_Added_To_Pages.cs b/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/20251024065316_Status_Field_Added_To_Pages.cs new file mode 100644 index 0000000000..059824f63a --- /dev/null +++ b/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 +{ + /// + public partial class Status_Field_Added_To_Pages : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsUsers", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsUsers", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsTags", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsTags", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsPages", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsPages", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40, + oldNullable: true); + + migrationBuilder.AddColumn( + name: "EntityVersion", + table: "CmsPages", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "IsHomePage", + table: "CmsPages", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "LayoutName", + table: "CmsPages", + type: "nvarchar(max)", + nullable: true); + + migrationBuilder.AddColumn( + name: "Status", + table: "CmsPages", + type: "int", + nullable: false, + defaultValue: 1); + + migrationBuilder.AlterColumn( + name: "Status", + table: "CmsPages", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsMenuItems", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsMenuItems", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40, + oldNullable: true); + + migrationBuilder.AddColumn( + name: "RequiredPermissionName", + table: "CmsMenuItems", + type: "nvarchar(128)", + maxLength: 128, + nullable: true); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsMediaDescriptors", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsMediaDescriptors", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsGlobalResources", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsGlobalResources", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsComments", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsComments", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40, + oldNullable: true); + + migrationBuilder.AddColumn( + name: "IdempotencyToken", + table: "CmsComments", + type: "nvarchar(32)", + maxLength: 32, + nullable: true); + + migrationBuilder.AddColumn( + name: "IsApproved", + table: "CmsComments", + type: "bit", + nullable: true); + + migrationBuilder.AddColumn( + name: "Url", + table: "CmsComments", + type: "nvarchar(512)", + maxLength: 512, + nullable: true); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsBlogs", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsBlogs", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsBlogPosts", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsBlogPosts", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40, + oldNullable: true); + + migrationBuilder.AddColumn( + name: "EntityVersion", + table: "CmsBlogPosts", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsBlogFeatures", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsBlogFeatures", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "AbpBlobs", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "AbpBlobs", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "AbpBlobContainers", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + 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(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + CreatorId = table.Column(type: "uniqueidentifier", nullable: false), + CreationTime = table.Column(type: "datetime2", nullable: false), + EntityId = table.Column(type: "nvarchar(450)", nullable: false), + EntityType = table.Column(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" }); + } + + /// + 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( + name: "ExtraProperties", + table: "CmsUsers", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsUsers", + type: "nvarchar(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsTags", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsTags", + type: "nvarchar(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsPages", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsPages", + type: "nvarchar(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsMenuItems", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsMenuItems", + type: "nvarchar(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsMediaDescriptors", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsMediaDescriptors", + type: "nvarchar(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsGlobalResources", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsGlobalResources", + type: "nvarchar(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsComments", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsComments", + type: "nvarchar(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsBlogs", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsBlogs", + type: "nvarchar(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsBlogPosts", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsBlogPosts", + type: "nvarchar(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "CmsBlogFeatures", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "CmsBlogFeatures", + type: "nvarchar(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "AbpBlobs", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "AbpBlobs", + type: "nvarchar(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + + migrationBuilder.AlterColumn( + name: "ExtraProperties", + table: "AbpBlobContainers", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "AbpBlobContainers", + type: "nvarchar(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + } + } +} diff --git a/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/CmsKitHttpApiHostMigrationsDbContextModelSnapshot.cs b/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/CmsKitHttpApiHostMigrationsDbContextModelSnapshot.cs index 13ddfd47b2..09c7b6f4c2 100644 --- a/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Migrations/CmsKitHttpApiHostMigrationsDbContextModelSnapshot.cs +++ b/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("ConcurrencyStamp") .IsConcurrencyToken() + .IsRequired() .HasMaxLength(40) .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); @@ -44,6 +45,7 @@ namespace Volo.CmsKit.Migrations .HasColumnType("varbinary(max)"); b.Property("ExtraProperties") + .IsRequired() .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -73,11 +75,13 @@ namespace Volo.CmsKit.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() + .IsRequired() .HasMaxLength(40) .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); b.Property("ExtraProperties") + .IsRequired() .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -105,6 +109,7 @@ namespace Volo.CmsKit.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() + .IsRequired() .HasMaxLength(40) .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); @@ -126,6 +131,7 @@ namespace Volo.CmsKit.Migrations .HasColumnName("DeletionTime"); b.Property("ExtraProperties") + .IsRequired() .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -173,6 +179,7 @@ namespace Volo.CmsKit.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() + .IsRequired() .HasMaxLength(40) .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); @@ -194,6 +201,7 @@ namespace Volo.CmsKit.Migrations .HasColumnName("DeletionTime"); b.Property("ExtraProperties") + .IsRequired() .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -238,6 +246,7 @@ namespace Volo.CmsKit.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() + .IsRequired() .HasMaxLength(40) .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); @@ -265,7 +274,11 @@ namespace Volo.CmsKit.Migrations .HasColumnType("datetime2") .HasColumnName("DeletionTime"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") + .IsRequired() .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -321,6 +334,7 @@ namespace Volo.CmsKit.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() + .IsRequired() .HasMaxLength(40) .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); @@ -344,9 +358,17 @@ namespace Volo.CmsKit.Migrations .HasColumnType("nvarchar(64)"); b.Property("ExtraProperties") + .IsRequired() .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("IdempotencyToken") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("IsApproved") + .HasColumnType("bit"); + b.Property("RepliedCommentId") .HasColumnType("uniqueidentifier"); @@ -359,6 +381,10 @@ namespace Volo.CmsKit.Migrations .HasMaxLength(512) .HasColumnType("nvarchar(512)"); + b.Property("Url") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + b.HasKey("Id"); b.HasIndex("TenantId", "RepliedCommentId"); @@ -376,6 +402,7 @@ namespace Volo.CmsKit.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() + .IsRequired() .HasMaxLength(40) .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); @@ -389,6 +416,7 @@ namespace Volo.CmsKit.Migrations .HasColumnName("CreatorId"); b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("EntityId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("EntityType") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("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("Id") @@ -427,6 +490,7 @@ namespace Volo.CmsKit.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() + .IsRequired() .HasMaxLength(40) .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); @@ -453,6 +517,7 @@ namespace Volo.CmsKit.Migrations .HasColumnType("nvarchar(64)"); b.Property("ExtraProperties") + .IsRequired() .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -501,6 +566,7 @@ namespace Volo.CmsKit.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() + .IsRequired() .HasMaxLength(40) .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); @@ -525,6 +591,7 @@ namespace Volo.CmsKit.Migrations .HasColumnType("nvarchar(max)"); b.Property("ExtraProperties") + .IsRequired() .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -551,6 +618,10 @@ namespace Volo.CmsKit.Migrations b.Property("ParentId") .HasColumnType("uniqueidentifier"); + b.Property("RequiredPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("Target") .HasColumnType("nvarchar(max)"); @@ -576,6 +647,7 @@ namespace Volo.CmsKit.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() + .IsRequired() .HasMaxLength(40) .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); @@ -600,7 +672,11 @@ namespace Volo.CmsKit.Migrations .HasColumnType("datetime2") .HasColumnName("DeletionTime"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") + .IsRequired() .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -610,6 +686,9 @@ namespace Volo.CmsKit.Migrations .HasDefaultValue(false) .HasColumnName("IsDeleted"); + b.Property("IsHomePage") + .HasColumnType("bit"); + b.Property("LastModificationTime") .HasColumnType("datetime2") .HasColumnName("LastModificationTime"); @@ -618,6 +697,9 @@ namespace Volo.CmsKit.Migrations .HasColumnType("uniqueidentifier") .HasColumnName("LastModifierId"); + b.Property("LayoutName") + .HasColumnType("nvarchar(max)"); + b.Property("Script") .HasColumnType("nvarchar(max)"); @@ -626,6 +708,9 @@ namespace Volo.CmsKit.Migrations .HasMaxLength(256) .HasColumnType("nvarchar(256)"); + b.Property("Status") + .HasColumnType("int"); + b.Property("Style") .HasColumnType("nvarchar(max)"); @@ -751,6 +836,7 @@ namespace Volo.CmsKit.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() + .IsRequired() .HasMaxLength(40) .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); @@ -777,6 +863,7 @@ namespace Volo.CmsKit.Migrations .HasColumnType("nvarchar(64)"); b.Property("ExtraProperties") + .IsRequired() .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -818,6 +905,7 @@ namespace Volo.CmsKit.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() + .IsRequired() .HasMaxLength(40) .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); @@ -835,6 +923,7 @@ namespace Volo.CmsKit.Migrations .HasColumnName("EmailConfirmed"); b.Property("ExtraProperties") + .IsRequired() .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json index 4fdfb5b94f..52f18838d4 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json +++ b/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" } } diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock index 04e625105e..270be47fb7 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock +++ b/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" diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Menus/PageLookupInputDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Menus/PageLookupInputDto.cs index 12fc847917..299c116ece 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Menus/PageLookupInputDto.cs +++ b/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; } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/CreatePageInputDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/CreatePageInputDto.cs index a62ede1f05..c5b92d5e6d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/CreatePageInputDto.cs +++ b/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; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/GetPagesInputDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/GetPagesInputDto.cs index 7275db931e..1fe3440ef2 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/GetPagesInputDto.cs +++ b/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; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/PageDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/PageDto.cs index 6fbcbd1866..c5e52e5369 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/PageDto.cs +++ b/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, IHasConcurrencyStamp public bool IsHomePage { get; set; } + public PageStatus Status { get; set; } + public string ConcurrencyStamp { get; set; } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/UpdatePageInputDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/UpdatePageInputDto.cs index 9e47ce54f0..5bca78fe1e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/UpdatePageInputDto.cs +++ b/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; } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Menus/MenuItemAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Menus/MenuItemAdminAppService.cs index d35579c6cf..5cca8553da 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Menus/MenuItemAdminAppService.cs +++ b/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 diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs index 2860946490..f3fed0642f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs +++ b/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> 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 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); diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/cms-kit-admin-generate-proxy.json b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/cms-kit-admin-generate-proxy.json index 0b3a4ffb71..dc870f6878 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/cms-kit-admin-generate-proxy.json +++ b/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", diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/Volo.CmsKit.Admin.HttpApi.Client.abppkg b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/Volo.CmsKit.Admin.HttpApi.Client.abppkg index 7deef5e383..6e9b2ad635 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/Volo.CmsKit.Admin.HttpApi.Client.abppkg +++ b/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 + } + } + } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml index 6ddbae5883..b609f83b8a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml @@ -64,6 +64,8 @@ + + @@ -119,7 +121,8 @@ - + + \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml.cs index 96d3f218c6..86c855ca26 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml.cs +++ b/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; } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml index 4d4c9d9454..7a825deb31 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml @@ -69,6 +69,8 @@ + + @@ -125,7 +127,8 @@ - + + \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml.cs index fff1f814e5..09f431938f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml.cs +++ b/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; } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/create.js b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/create.js index 1b3ccea05f..ca7ab72cdc 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/create.js +++ b/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(); }); diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/index.js b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/index.js index 30d807df48..c2d3d2afd0 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/index.js +++ b/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, diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/update.js b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/update.js index d75fb9bdda..a58e601135 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/update.js +++ b/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(); }); diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.abppkg b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.abppkg index 33a45483d7..6a728eb0d6 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.abppkg +++ b/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" + } } } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/wwwroot/client-proxies/cms-kit-admin-proxy.js b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/wwwroot/client-proxies/cms-kit-admin-proxy.js index 928d7c11b9..875fd5c948 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/wwwroot/client-proxies/cms-kit-admin-proxy.js +++ b/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)); }; diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/PageDto.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/PageDto.cs index e81a882baa..e00ec6bb34 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/PageDto.cs +++ b/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 public string Script { get; set; } public string Style { get; set; } + + public PageStatus Status { get; set; } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/Volo.CmsKit.Common.HttpApi.Client.abppkg b/modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/Volo.CmsKit.Common.HttpApi.Client.abppkg index 7deef5e383..97cf7d524f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/Volo.CmsKit.Common.HttpApi.Client.abppkg +++ b/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 + } + } + } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Web/Volo.CmsKit.Common.Web.abppkg b/modules/cms-kit/src/Volo.CmsKit.Common.Web/Volo.CmsKit.Common.Web.abppkg index 930c4018b3..1b83a52bfe 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Common.Web/Volo.CmsKit.Common.Web.abppkg +++ b/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" + } + } + } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json index e4e5a8aa2c..e3c3600eb0 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json @@ -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" } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageCacheItem.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageCacheItem.cs index d4d662a8a6..5696b2566e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageCacheItem.cs +++ b/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}"; diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageStatus.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageStatus.cs new file mode 100644 index 0000000000..037b4f8d48 --- /dev/null +++ b/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 +} + diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/IPageRepository.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/IPageRepository.cs index 2add03f1fd..e2a95d0b03 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/IPageRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/IPageRepository.cs @@ -8,10 +8,11 @@ namespace Volo.CmsKit.Pages; public interface IPageRepository : IBasicRepository { - Task GetCountAsync(string filter = null, CancellationToken cancellationToken = default); + Task GetCountAsync(string filter = null, PageStatus? status = null, CancellationToken cancellationToken = default); Task> GetListAsync( string filter = null, + PageStatus? status = null, int maxResultCount = int.MaxValue, int skipCount = 0, string sorting = null, diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/Page.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/Page.cs index b1f611abc6..f73031fbfe 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/Page.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/Page.cs @@ -27,6 +27,8 @@ public class Page : FullAuditedAggregateRoot, 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, 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, IMultiTenant, IHasEntityVers SetScript(script); SetStyle(style); SetLayoutName(layoutName); + SetStatus(status); } public virtual void SetTitle(string title) @@ -85,4 +89,9 @@ public class Page : FullAuditedAggregateRoot, IMultiTenant, IHasEntityVers { IsHomePage = isHomePage; } + + public virtual void SetStatus(PageStatus status) + { + Status = status; + } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageManager.cs index a94602c54a..73393ba56b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageManager.cs +++ b/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(); diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs index c5b93022c6..91a5da82b8 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs +++ b/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 }); diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Pages/EfCorePageRepository.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Pages/EfCorePageRepository.cs index 4af6bf18f3..a8fdc22f84 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Pages/EfCorePageRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Pages/EfCorePageRepository.cs @@ -20,17 +20,20 @@ public class EfCorePageRepository : EfCoreRepository 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> 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 - 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)); diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Pages/MongoPageRepository.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Pages/MongoPageRepository.cs index 7c2f9a3891..7f51c79b3d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Pages/MongoPageRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Pages/MongoPageRepository.cs @@ -22,6 +22,7 @@ public class MongoPageRepository : MongoDbRepository GetCountAsync(string filter = null, + PageStatus? status = null, CancellationToken cancellationToken = default) { var cancellation = GetCancellationToken(cancellationToken); @@ -30,12 +31,14 @@ public class MongoPageRepository : MongoDbRepository - 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> 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 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); diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs index cd0191e20d..b69a2e18d6 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs +++ b/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); 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); }); diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json index 3bee77cf96..84211158e3 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json +++ b/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", diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/Volo.CmsKit.Public.HttpApi.Client.abppkg b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/Volo.CmsKit.Public.HttpApi.Client.abppkg index 7deef5e383..07b3df7494 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/Volo.CmsKit.Public.HttpApi.Client.abppkg +++ b/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 + } + } + } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.abppkg b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.abppkg index 1fe9750915..f4e39aa4c0 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.abppkg +++ b/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" + } } } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/wwwroot/client-proxies/cms-kit-proxy.js b/modules/cms-kit/src/Volo.CmsKit.Public.Web/wwwroot/client-proxies/cms-kit-proxy.js index 5f5ea08ffa..cf26e01532 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/wwwroot/client-proxies/cms-kit-proxy.js +++ b/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)); }; diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs index 452c221311..2da2365021 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs +++ b/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); diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs index 1c2a33ccdf..131c9ac135 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs +++ b/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(); diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs index c9837f0af9..e3a2d78cab 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs +++ b/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(); diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts b/npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts index 49645c3e45..69eea99c9a 100644 --- a/npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts +++ b/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 => {