@ -1,3 +1,289 @@ |
|||||
# Audit Logging |
# Audit Logging |
||||
|
|
||||
TODO |
[Wikipedia](https://en.wikipedia.org/wiki/Audit_trail): "*An audit trail (also called **audit log**) is a security-relevant chronological record, set of records, and/or destination and source of records that provide documentary evidence of the sequence of activities that have affected at any time a specific operation, procedure, or event*". |
||||
|
|
||||
|
ABP Framework provides an **extensible audit logging system** that automates the audit logging by **convention** and provides **configuration** points to control the level of the audit logs. |
||||
|
|
||||
|
An **audit log object** (see the Audit Log Object section below) is typically created & saved per web request. It includes; |
||||
|
|
||||
|
* **Request & response details** (like URL, Http method, Browser info, HTTP status code... etc.). |
||||
|
* **Performed actions** (controller actions and application service method calls with their parameters). |
||||
|
* **Entity changes** occurred in the web request. |
||||
|
* **Exception** information (if there was an error while executing the request). |
||||
|
* **Request duration** (to measure the performance of the application). |
||||
|
|
||||
|
> [Startup templates](Startup-Templates/Index.md) are configured for the audit logging system which is suitable for most of the applications. Use this document for a detailed control over the audit log system. |
||||
|
|
||||
|
## UseAuditing() |
||||
|
|
||||
|
`UseAuditing()` middleware should be added to the ASP.NET Core request pipeline in order to create and save the audit logs. If you've created your applications using [the startup templates](Startup-Templates/Index.md), it is already added. |
||||
|
|
||||
|
## AbpAuditingOptions |
||||
|
|
||||
|
`AbpAuditingOptions` is the main [options object](Options.md) to configure the audit log system. You can configure it in the `ConfigureServices` method of your [module](Module-Development-Basics.md): |
||||
|
|
||||
|
````csharp |
||||
|
Configure<AbpAuditingOptions>(options => |
||||
|
{ |
||||
|
options.IsEnabled = false; //Disables the auditing system |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
Here, a list of the options you can configure: |
||||
|
|
||||
|
* `IsEnabled` (default: `true`): A root switch to enable or disable the auditing system. Other options is not used if this value is `false`. |
||||
|
* `HideErrors` (default: `true`): Audit log system hides and write regular [logs](Logging.md) if any error occurs while saving the audit log objects. If saving the audit logs is critical for your system, set this to `false` to throw exception in case of hiding the errors. |
||||
|
* `IsEnabledForAnonymousUsers` (default: `true`): If you want to write audit logs only for the authenticated users, set this to `false`. If you save audit logs for anonymous users, you will see `null` for `UserId` values for these users. |
||||
|
* `IsEnabledForGetRequests` (default: `false`): HTTP GET requests should not make any change in the database normally and audit log system doesn't save audit log objects for GET request. Set this to `true` to enable it also for the GET requests. |
||||
|
* `ApplicationName`: If multiple applications saving audit logs into a single database, set this property to your application name, so you can distinguish the logs of different applications. |
||||
|
* `IgnoredTypes`: A list of `Type`s to be ignored for audit logging. If this is an entity type, changes for this type of entities will not be saved. This list is also used while serializing the action parameters. |
||||
|
* `EntityHistorySelectors`: A list of selectors those are used to determine if an entity type is selected for saving the entity change. See the section below for details. |
||||
|
* `Contributors`: A list of `AuditLogContributor` implementations. A contributor is a way of extending the audit log system. See the "Audit Log Contributors" section below. |
||||
|
|
||||
|
### Entity History Selectors |
||||
|
|
||||
|
Saving all changes of all your entities would require a lot of database space. For this reason, **audit log system doesn't save any change for the entities unless you explicitly configure it**. |
||||
|
|
||||
|
To save all changes of all entities, simply use the `AddAllEntities()` extension method. |
||||
|
|
||||
|
````csharp |
||||
|
Configure<AbpAuditingOptions>(options => |
||||
|
{ |
||||
|
options.EntityHistorySelectors.AddAllEntities(); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
`options.EntityHistorySelectors` actually a list of type predicate. You can write a lambda expression to define your filter. |
||||
|
|
||||
|
The example selector below does the same of the `AddAllEntities()` extension method defined above: |
||||
|
|
||||
|
````csharp |
||||
|
Configure<AbpAuditingOptions>(options => |
||||
|
{ |
||||
|
options.EntityHistorySelectors.Add( |
||||
|
new NamedTypeSelector( |
||||
|
"MySelectorName", |
||||
|
type => |
||||
|
{ |
||||
|
if (typeof(IEntity).IsAssignableFrom(type)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
) |
||||
|
); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
The condition `typeof(IEntity).IsAssignableFrom(type)` will be `true` for any class implements the `IEntity` interface (this is technically all the entities in your application). You can conditionally check and return `true` or `false` based on your preference. |
||||
|
|
||||
|
`options.EntityHistorySelectors` is a flexible and dynamic way of selecting the entities for audit logging. Another way is to use the `Audited` and `DisableAuditing` attributes per entity. |
||||
|
|
||||
|
## Enabling/Disabling Audit Logging for Services |
||||
|
|
||||
|
### Enable/Disable for Controllers & Actions |
||||
|
|
||||
|
All the controller actions are logged by default (see `IsEnabledForGetRequests` above for GET requests). |
||||
|
|
||||
|
You can use the `[DisableAuditing]` to disable it for a specific controller type: |
||||
|
|
||||
|
````csharp |
||||
|
[DisableAuditing] |
||||
|
public class HomeController : AbpController |
||||
|
{ |
||||
|
//... |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
Use `[DisableAuditing]` for any action to control it in the action level: |
||||
|
|
||||
|
````csharp |
||||
|
public class HomeController : AbpController |
||||
|
{ |
||||
|
[DisableAuditing] |
||||
|
public async Task<ActionResult> Home() |
||||
|
{ |
||||
|
//... |
||||
|
} |
||||
|
|
||||
|
public async Task<ActionResult> OtherActionLogged() |
||||
|
{ |
||||
|
//... |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
### Enable/Disable for Application Services & Methods |
||||
|
|
||||
|
[Application service](Application-Services.md) method calls also included into the audit log by default. You can use the `[DisableAuditing]` in service or method level. |
||||
|
|
||||
|
#### Enable/Disable for Other Services |
||||
|
|
||||
|
Action audit logging can be enabled for any type of class (registered to and resolved from the [dependency injection](Dependency-Injection.md)) while it is only enabled for the controllers and the application services by default. |
||||
|
|
||||
|
Use `[Audited]` and `[DisableAuditing]` for any class or method that need to be audit logged. In addition, your class can (directly or inherently) implement the `IAuditingEnabled` interface to enable the audit logging for that class by default. |
||||
|
|
||||
|
### Enable/Disable for Entities & Properties |
||||
|
|
||||
|
An entity is ignored on entity change audit logging in the following cases; |
||||
|
|
||||
|
* If you add an entity type to the `AbpAuditingOptions.IgnoredTypes` (as explained before), it is completely ignored in the audit logging system. |
||||
|
* If the object is not an [entity](Entities.md) (not implements `IEntity` directly or inherently - All entities implement this interface by default). |
||||
|
* If entity type is not public. |
||||
|
|
||||
|
Otherwise, you can use `Audited` to enable entity change audit logging for an entity: |
||||
|
|
||||
|
````csharp |
||||
|
[Audited] |
||||
|
public class MyEntity : Entity<Guid> |
||||
|
{ |
||||
|
//... |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
Or disable it for an entity: |
||||
|
|
||||
|
````csharp |
||||
|
[DisableAuditing] |
||||
|
public class MyEntity : Entity<Guid> |
||||
|
{ |
||||
|
//... |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
Disabling audit logging can be necessary only if the entity is being selected by the `AbpAuditingOptions.EntityHistorySelectors` that explained before. |
||||
|
|
||||
|
You can disable auditing only some properties of your entities for a detailed control over the audit logging: |
||||
|
|
||||
|
````csharp |
||||
|
[Audited] |
||||
|
public class MyUser : Entity<Guid> |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
public string Email { get; set; } |
||||
|
|
||||
|
[DisableAuditing] //Ignore the Passoword on audit logging |
||||
|
public string Password { get; set; } |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
Audit log system will save changes for the `MyUser` entity while it ignores the `Password` property which can be dangerous to save for security purposes. |
||||
|
|
||||
|
In some cases, you may want to save a few properties but ignore all others. Writing `[DisableAuditing]` for all the other properties would be tedious. In such cases, use `[Audited]` only for the desired properties and mark the entity with the `[DisableAuditing]` attribute: |
||||
|
|
||||
|
````csharp |
||||
|
[DisableAuditing] |
||||
|
public class MyUser : Entity<Guid> |
||||
|
{ |
||||
|
[Audited] //Only log the Name change |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
public string Email { get; set; } |
||||
|
|
||||
|
public string Password { get; set; } |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
## IAuditingStore |
||||
|
|
||||
|
`IAuditingStore` is an interface that is used to save the audit log objects (explained below) by the ABP Framework. If you need to save the audit log objects to a custom data store, you can implement the `IAuditingStore` in your own application and replace using the [dependency injection system](Dependency-Injection.md). |
||||
|
|
||||
|
`SimpleLogAuditingStore` is used if no audit store was registered. It simply writes the audit object to the standard [logging system](Logging.md). |
||||
|
|
||||
|
[The Audit Logging Module](Modules/Audit-Logging.md) has been configured in [the startup templates](Startup-Templates/Index.md) saves audit log objects to a database (it supports multiple database providers). So, most of the times you don't care about how `IAuditingStore` was implemented and used. |
||||
|
|
||||
|
## Audit Log Object |
||||
|
|
||||
|
An **audit log object** is created for each **web request** by default. An audit log object can be represented by the following relation diagram: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
* **AuditLogInfo**: The root object with the following properties: |
||||
|
* `ApplicationName`: When you save audit logs of different applications to the same database, this property is used to distinguish the logs of the applications. |
||||
|
* `UserId`: Id of the current user, if the user has logged in. |
||||
|
* `UserName`: User name of the current user, if the user has logged in (this value is here to not depend on the identity module/system for lookup). |
||||
|
* `TenantId`: Id of the current tenant, for a multi-tenant application. |
||||
|
* `TenantName`: Name of the current tenant, for a multi-tenant application. |
||||
|
* `ExecutionTime`: The time when this audit log object has been created. |
||||
|
* `ExecutionDuration`: Total execution duration of the request, in milliseconds. This can be used to observe the performance of the application. |
||||
|
* `ClientId`: Id of the current client, if the client has been authenticated. A client is generally a 3rd-party application using the system over an HTTP API. |
||||
|
* `ClientName`: Name of the current client, if available. |
||||
|
* `ClientIpAddress`: IP address of the client/user device. |
||||
|
* `CorrelationId`: Current [Correlation Id]((CorrelationId.md)). Correlation Id is used to relate the audit logs written by different applications (or microservices) in a single logical operation. |
||||
|
* `BrowserInfo`: Browser name/version info of the current user, if available. |
||||
|
* `HttpMethod`: HTTP method of the current request (GET, POST, PUT, DELETE... etc.). |
||||
|
* `HttpStatusCode`: HTTP response status code for this request. |
||||
|
* `Url`: URL of the request. |
||||
|
* **AuditLogActionInfo**: An audit log action is typically a controller action or an [application service](Application-Services.md) method call during the web request. One audit log may contain multiple actions. An action object has the following properties: |
||||
|
* `ServiceName`: Name of the executed controller/service. |
||||
|
* `MethodName`: Name of the executed method of the controller/service. |
||||
|
* `Parameters`: A JSON formatted text representing the parameters passed to the method. |
||||
|
* `ExecutionTime`: The time when this method was executed. |
||||
|
* `ExecutionDuration`: Duration of the method execution, in milliseconds. This can be used to observe the performance of the method. |
||||
|
* **EntityChangeInfo**: Represents a change of an entity in this web request. An audit log may contain zero or more entity changes. An entity change has the following properties: |
||||
|
* `ChangeTime`: The time when the entity was changed. |
||||
|
* `ChangeType`: An enum with the following fields: `Created` (0), `Updated` (1) and `Deleted` (2). |
||||
|
* `EntityId`: Id of the entity that was changed. |
||||
|
* `EntityTenantId`: Id of the tenant this entity belongs to. |
||||
|
* `EntityTypeFullName`: Type (class) name of the entity with full namespace (like *Acme.BookStore.Book* for the Book entity). |
||||
|
* **EntityPropertyChangeInfo**: Represents a change of a property of an entity. An entity change info (explained above) may contain one or more property change with the following properties: |
||||
|
* `NewValue`: New value of the property. It is `null` if the entity was deleted. |
||||
|
* `OriginalValue`: Old/original value before the change. It is `null` if the entity was newly created. |
||||
|
* `PropertyName`: The name of the property on the entity class. |
||||
|
* `PropertyTypeFullName`: Type (class) name of the property with full namespace. |
||||
|
* **Exception**: An audit log object may contain zero or more exception. In this way, you can get a report of the failed requests. |
||||
|
* **Comment**: An arbitrary string value to add custom messages to the audit log entry. An audit log object may contain zero or more comments. |
||||
|
|
||||
|
In addition to the standard properties explained above, `AuditLogInfo`, `AuditLogActionInfo` and `EntityChangeInfo` objects implement the `IHasExtraProperties` interface, so you can add custom properties to these objects. |
||||
|
|
||||
|
## Audit Log Contributors |
||||
|
|
||||
|
You can extend the auditing system by creating a class that is derived from the `AuditLogContributor` class which defines the `PreContribute` and the `PostContribute` methods. |
||||
|
|
||||
|
The only pre-built contributor is the `AspNetCoreAuditLogContributor` class which sets the related properties for an HTTP request. |
||||
|
|
||||
|
A contributor can set properties and collections of the `AuditLogInfo` class to add more information. |
||||
|
|
||||
|
Example: |
||||
|
|
||||
|
````csharp |
||||
|
public class MyAuditLogContributor : AuditLogContributor |
||||
|
{ |
||||
|
public override void PreContribute(AuditLogContributionContext context) |
||||
|
{ |
||||
|
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>(); |
||||
|
context.AuditInfo.SetProperty( |
||||
|
"MyCustomClaimValue", |
||||
|
currentUser.FindClaimValue("MyCustomClaim") |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public override void PostContribute(AuditLogContributionContext context) |
||||
|
{ |
||||
|
context.AuditInfo.Comments.Add("Some comment..."); |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
* `context.ServiceProvider` can be used to resolve services from the [dependency injection](Dependency-Injection.md). |
||||
|
* `context.AuditInfo` can be used to access to the current audit log object to manipulate it. |
||||
|
|
||||
|
After creating such a contributor, you must add it to the `AbpAuditingOptions.Contributors` list: |
||||
|
|
||||
|
````csharp |
||||
|
Configure<AbpAuditingOptions>(options => |
||||
|
{ |
||||
|
options.Contributors.Add(new MyAuditLogContributor()); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
## The Audit Logging Module |
||||
|
|
||||
|
The Audit Logging Module basically implements the `IAuditingStore` to save the audit log objects to a database. It supports multiple database providers. This module is added to the startup templates by default. |
||||
|
|
||||
|
See [the Audit Logging Module document](Modules/Audit-Logging.md) for more about it. |
||||
@ -0,0 +1,164 @@ |
|||||
|
# ABP Framework v2.0 and the ABP Commercial |
||||
|
|
||||
|
ABP Framework v2.0 has been released this week. This post explains why we have released an **early major version** and what is changed with version 2.0. |
||||
|
|
||||
|
In addition to the v2.0 release, we are excited to announce the **ABP Commercial**, which is a set of professional modules, tools, themes, and services built on top of the open-source ABP framework. |
||||
|
|
||||
|
## ABP Framework v2.0 |
||||
|
|
||||
|
### Why 2.0 instead of 1.2? |
||||
|
|
||||
|
It was planned to release v1.2 after the [v1.1.2](https://github.com/abpframework/abp/releases/tag/1.1.2) release. However, [it is reported](https://github.com/abpframework/abp/issues/2026) that v1.x has some **performance** and **stability** issues on Linux, especially when you deploy your application to **Linux** containers with **low CPU and memory** resources. |
||||
|
|
||||
|
We have investigated the problem deeply and have seen that the root cause of the problem was related to the implementation of **intercepting `async` methods**. Besides, there were some **`async` over `sync`** usages that effected the thread pool optimization. |
||||
|
|
||||
|
Finally, we **solved all the problems** with the great help of the **community**. But we also had some important **design decisions** which cause some **breaking changes** and we had to change the major version number of the framework because of the [semantic versioning](https://semver.org/). |
||||
|
|
||||
|
Most of the applications won't be affected by [the breaking changes](https://github.com/abpframework/abp/releases), or it will be trivial to make these necessary changes. |
||||
|
|
||||
|
### Breaking Changes |
||||
|
|
||||
|
#### Removed Some Sync APIs |
||||
|
|
||||
|
Some of the interceptors are required to use `async` APIs. When they intercept `sync` methods, they need to call `async` over `sync`. This eventually ends up with `async` over `sync` problem. That's why we have [removed some sync APIs](https://github.com/abpframework/abp/pull/2464). |
||||
|
|
||||
|
**`Async` over `sync`** pattern is a classical problem of `C#` when you need to **call an `async` method inside a `sync` method**. While there are some workarounds to this problem, they all have **disadvantages** and it is suggested to **not write** such code at all. You can find many documents related to this topic on the web. |
||||
|
|
||||
|
To avoid this problem, we have removed: |
||||
|
|
||||
|
- `sync` [repository](https://docs.abp.io/en/abp/latest/Repositories) methods (like `insert`, `update`, etc...), |
||||
|
- `sync` APIs of the [unit of work](https://docs.abp.io/en/abp/latest/Unit-Of-Work), |
||||
|
- `sync` APIs of the [background jobs](https://docs.abp.io/en/abp/latest/Background-Jobs), |
||||
|
- `sync` APIs of the [audit logging](https://docs.abp.io/en/abp/latest/Audit-Logging), |
||||
|
- some other rarely used `sync` APIs. |
||||
|
|
||||
|
If you get any compile error, just use the `async` versions of these APIs. |
||||
|
|
||||
|
#### Always Async! |
||||
|
|
||||
|
Beginning from the v2.0, the ABP framework assumes that you are writing your application code `async` first. Otherwise, some framework functionalities may not properly work. |
||||
|
|
||||
|
It is suggested to write `async` to all your [application services](https://docs.abp.io/en/abp/latest/Application-Services), [repository methods](https://docs.abp.io/en/abp/latest/Repositories), controller actions, page handlers. |
||||
|
|
||||
|
Even if your application service method doesn't need to be `async` , set it as `async` , because interceptors perform `async` operations (for authorization, unit of work, etc...). You can return `Task.Completed` from a method that doesn't make an `async` call. |
||||
|
|
||||
|
Example: |
||||
|
|
||||
|
````csharp |
||||
|
public Task<int> GetValueAsync() |
||||
|
{ |
||||
|
//this method doesn't make any async call. |
||||
|
return Task.CompletedTask(42); |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
The example above normally doesn't need to be `async` because it doesn't perform an `async` call. However, making it `async` helps the ABP framework to run interceptors without `async` over sync calls. |
||||
|
|
||||
|
This rule doesn't force you to write every method `async` . This would not be good and would be tedious. It is only needed for the intercepted services (especially for [application services](https://docs.abp.io/en/abp/latest/Application-Services) and [repository methods](https://docs.abp.io/en/abp/latest/Repositories)) |
||||
|
|
||||
|
#### Other Breaking Changes |
||||
|
|
||||
|
See [the release notes](https://github.com/abpframework/abp/releases/tag/2.0.0) for the other breaking changes. Most of them will not affect your application code. |
||||
|
|
||||
|
### New Features |
||||
|
|
||||
|
This release also contains some new features and tens of enhancements: |
||||
|
|
||||
|
- [#2597](https://github.com/abpframework/abp/pull/2597) New `Volo.Abp.AspNetCore.Serilog` package. |
||||
|
- [#2526](https://github.com/abpframework/abp/issues/2526) Client-side validation for the dynamic `C#` client proxies. |
||||
|
- [#2374](https://github.com/abpframework/abp/issues/2374) `Async` background jobs. |
||||
|
- [#265](https://github.com/abpframework/abp/issues/265) Managing the application shutdown. |
||||
|
- [#2472](https://github.com/abpframework/abp/issues/2472) Implemented `DeviceFlowCodes` and `TokenCleanupService` for the `IdentityServer` module. |
||||
|
|
||||
|
See [the release notes](https://github.com/abpframework/abp/releases/tag/2.0.0) for the complete list of features, enhancements and bug fixes. |
||||
|
|
||||
|
### Documentation |
||||
|
|
||||
|
We have completed some missing documentation with the v2.0 release. In the following weeks, we will mostly focus on the documentation and tutorials. |
||||
|
|
||||
|
## ABP Commercial |
||||
|
|
||||
|
[ABP Commercial](https://commercial.abp.io/) is a set of professional **modules, tools, themes, and services** built on top of the open-source ABP framework. |
||||
|
|
||||
|
- It provides [professional modules](https://commercial.abp.io/modules) in addition to the ABP Framework's free & [open source modules](https://docs.abp.io/en/abp/latest/Modules/Index). |
||||
|
- It includes a beautiful a [UI theme](https://commercial.abp.io/themes) with 5 different styles. |
||||
|
- It provides the [ABP Suite](https://commercial.abp.io/tools/suite); A tool to assist your development to make you more productive. It currently can create full-stack CRUD pages in a few seconds by configuring your entity properties. More functionalities will be added over time. |
||||
|
- [Premium support](https://commercial.abp.io/support) for enterprise companies. |
||||
|
|
||||
|
In addition to these standard set of features, we will provide customer basis services. See the [commercial.abp.io](https://commercial.abp.io/) web site for other details. |
||||
|
|
||||
|
### ABP Framework vs the ABP Commercial |
||||
|
|
||||
|
The ABP Commercial **is not a paid version** of the ABP Framework. You can consider it as **set of additional benefits** for professional companies. You can use it to save your time and develop your product faster. |
||||
|
|
||||
|
ABP Framework is **open source & free** and will always be like that! |
||||
|
|
||||
|
As a principle, we build the main infrastructure as open-source and sell additional pre-built application features, themes, and tools. The main idea similar to the [ASP.NET Boilerplate](https://aspnetboilerplate.com/) & the [ASP.NET Zero](https://aspnetzero.com/) products. |
||||
|
|
||||
|
Buying a commercial license saves your significant time and effort and you can focus on your own business, besides you get dedicated and high priority support. Also, you will be supporting the ABP core team since we are spending most of our time to develop, maintain and support the open-source ABP Framework. |
||||
|
|
||||
|
With the introduction of the ABP Commercial, now ABP becomes a platform. We call it as the **ABP.IO Platform** which consists of the open source ABP Framework and the ABP Commercial. |
||||
|
|
||||
|
### Demo |
||||
|
|
||||
|
If you are wondering how exactly looks like the ABP Commercial application startup template, you can easily [create a demo](https://commercial.abp.io/demo) and see it in action. The demo includes all the pre-built modules and the theme. |
||||
|
|
||||
|
Here, a screenshot from the IdentityServer management module UI: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
This is another screenshot from a demo application using the material design style of the theme: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
### Pricing |
||||
|
|
||||
|
You can build **unlimited projects/products**, sell to **unlimited customers**, host **unlimited servers** without any restriction. Pricing is mostly based on the **developer count**, **support level** and **source code** requirement. There are three main packages; |
||||
|
|
||||
|
- **Team license**: Includes all the modules, themes and tools. Allows developing your product with up to 3 developers. You can buy additional developer licenses. |
||||
|
- **Business license**: Allows downloading the source code of all the modules and the themes. Also, it includes 5 developer licenses by default. You can buy additional developer licenses. |
||||
|
- **Enterprise license**: Provides unlimited and private support in addition to the benefits of the business license. |
||||
|
|
||||
|
See the [pricing page](https://commercial.abp.io/pricing) for details. In addition to the standard packages, we are also providing custom services and custom licensing. [Contact us](https://commercial.abp.io/contact) if you have any questions. |
||||
|
|
||||
|
#### License Comparison |
||||
|
|
||||
|
The license price changes based on your developer count, support level and source-code access. |
||||
|
|
||||
|
##### The Source-Code |
||||
|
|
||||
|
Team license doesn't include the source-code of the pre-built modules & themes. It uses all these modules as **NuGet & NPM packages**. In this way, you can easily **get new features and bug fixes** by just updating the package dependencies. But you can't access their source-code. So you don't have the possibility to embed a module's source code into your application and freely change the source-code. |
||||
|
|
||||
|
Pre-built modules provide some level of **customization** and **extensibility** and allow you to override services, UI parts and so on. We are working on to make them much more customizable and extensible. If you don't need to make major changes in the pre-built modules, the team license will be ideal for you, because it is cheaper and allows you to easily get new features and bug fixes. |
||||
|
|
||||
|
Business and Enterprise licenses allow you to **download the source-code** of any module or the theme when you need it. They also use the same startup template with the team license, so all modules are used as `NuGet` & `NPM` packages by default. But in case of need, you can remove the package dependencies for a module and embed its source-code into your own solution to completely customize it. In this case, upgrading the module will not be as easy as before when a new version is available. You don't have to upgrade it, surely! But if you want, you should do it yourself using some merge tool or Git branch system. |
||||
|
|
||||
|
#### License Lifetime |
||||
|
|
||||
|
ABP Commercial license is **perpetual**, which means you can **use it forever** and continue to develop your applications. |
||||
|
|
||||
|
However, the following services are covered for one year: |
||||
|
|
||||
|
- Premium **support** ends after one year. You can continue to get community support. |
||||
|
- You can not get **updates** of the modules & the themes after one year. You can continue to use the last obtained version. You can even get bug fixes and enhancements for your current major version. |
||||
|
- You can use the **ABP Suite** tool for one year. |
||||
|
|
||||
|
If you want to continue to get these benefits, you can extend your license period. Renewing price is 20% less than the regular price. |
||||
|
|
||||
|
## NDC London 2020 |
||||
|
|
||||
|
Just like the [previous year](https://medium.com/volosoft/impressions-of-ndc-london-2019-f8f391bb7a9c), we are a partner of the famous software development conference: [NDC London](https://ndc-london.com/)! In the previous year, we were there with the [ASP.NET Boilerplate](https://aspnetboilerplate.com/) & [ASP.NET Zero](https://aspnetzero.com/) theme: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
This year, we will be focusing on the **ABP.IO Platform** (The Open Source ABP Framework and the ABP Commercial). Our booth wall will be like that: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
If you attend to the conference, remember to visit our booth. We would be glad to talk about the ABP platform features, goals and software development in general. |
||||
|
|
||||
|
### Would you like to meet the ABP Team? |
||||
|
|
||||
|
If you are in London and want to have a coffee with us, we will be available at February 1st afternoon. [@hibrahimkalkan](https://twitter.com/hibrahimkalkan) and [@ismcagdas](https://twitter.com/ismcagdas) will be there. |
||||
|
|
||||
|
Just write to info@abp.io if you want to meet :) |
||||
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 2.7 MiB |
@ -0,0 +1,3 @@ |
|||||
|
# Audit Logging Module |
||||
|
|
||||
|
TODO |
||||
|
After Width: | Height: | Size: 28 KiB |
@ -1,3 +1,59 @@ |
|||||
# FluentValidation 集成 |
# FluentValidation 集成 |
||||
|
|
||||
TODO |
ABP[验证](Validation.md)基础设施是可扩展的. [Volo.Abp.FluentValidation](https://www.nuget.org/packages/Volo.Abp.FluentValidation) NuGet 包扩展了验证系统使其与[FluentValidation](https://fluentvalidation.net/)库一起工作. |
||||
|
|
||||
|
## 安装 |
||||
|
|
||||
|
建议使用[ABP CLI](CLI.md)安装包. |
||||
|
|
||||
|
### 使用ABP CLI |
||||
|
|
||||
|
在项目(.csproj文件)的文件夹中打开命令行窗口并输入以下命令: |
||||
|
|
||||
|
````bash |
||||
|
abp add-package Volo.Abp.FluentValidation |
||||
|
```` |
||||
|
|
||||
|
### 手动安装 |
||||
|
|
||||
|
如果你想手动安装; |
||||
|
|
||||
|
1. 添加 [Volo.Abp.FluentValidation](https://www.nuget.org/packages/Volo.Abp.FluentValidation) NuGet包到你的项目: |
||||
|
|
||||
|
```` |
||||
|
Install-Package Volo.Abp.FluentValidation |
||||
|
```` |
||||
|
|
||||
|
2. 添加 `AbpFluentValidationModule` 到你的模块的依赖列表: |
||||
|
|
||||
|
````csharp |
||||
|
[DependsOn( |
||||
|
//...other dependencies |
||||
|
typeof(AbpFluentValidationModule) //Add the FluentValidation module |
||||
|
)] |
||||
|
public class YourModule : AbpModule |
||||
|
{ |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
## 使用 FluentValidation |
||||
|
|
||||
|
按照 [FluentValidation文档](https://fluentvalidation.net/) 创建验证器类. |
||||
|
例如: |
||||
|
|
||||
|
````csharp |
||||
|
public class CreateUpdateBookDtoValidator : AbstractValidator<CreateUpdateBookDto> |
||||
|
{ |
||||
|
public CreateUpdateBookDtoValidator() |
||||
|
{ |
||||
|
RuleFor(x => x.Name).Length(3, 10); |
||||
|
RuleFor(x => x.Price).ExclusiveBetween(0.0f, 999.0f); |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
ABP会自动找到这个类并在对象验证时与 `CreateUpdateBookDto` 关联. |
||||
|
|
||||
|
## 另请参阅 |
||||
|
|
||||
|
* [验证系统](Validation.md) |
||||
|
Before Width: | Height: | Size: 948 B After Width: | Height: | Size: 101 KiB |
@ -1,5 +1,7 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"packages": ["packages/*"], |
"packages": [ |
||||
|
"packages/*" |
||||
|
], |
||||
"npmClient": "yarn" |
"npmClient": "yarn" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/anchor-js", |
"name": "@abp/anchor-js", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"anchor-js": "^4.2.2" |
"anchor-js": "^4.2.2" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,11 +1,11 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/aspnetcore.mvc.ui.theme.basic", |
"name": "@abp/aspnetcore.mvc.ui.theme.basic", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/aspnetcore.mvc.ui.theme.shared": "^1.1.1" |
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.0.0" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,24 +1,24 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/aspnetcore.mvc.ui.theme.shared", |
"name": "@abp/aspnetcore.mvc.ui.theme.shared", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/aspnetcore.mvc.ui": "^1.1.1", |
"@abp/aspnetcore.mvc.ui": "^2.0.0", |
||||
"@abp/bootstrap": "^1.1.1", |
"@abp/bootstrap": "^2.0.0", |
||||
"@abp/bootstrap-datepicker": "^1.1.1", |
"@abp/bootstrap-datepicker": "^2.0.0", |
||||
"@abp/datatables.net-bs4": "^1.1.1", |
"@abp/datatables.net-bs4": "^2.0.0", |
||||
"@abp/font-awesome": "^1.1.1", |
"@abp/font-awesome": "^2.0.0", |
||||
"@abp/jquery-form": "^1.1.1", |
"@abp/jquery-form": "^2.0.0", |
||||
"@abp/jquery-validation-unobtrusive": "^1.1.1", |
"@abp/jquery-validation-unobtrusive": "^2.0.0", |
||||
"@abp/lodash": "^1.1.1", |
"@abp/lodash": "^2.0.0", |
||||
"@abp/luxon": "^1.1.1", |
"@abp/luxon": "^2.0.0", |
||||
"@abp/malihu-custom-scrollbar-plugin": "^1.1.1", |
"@abp/malihu-custom-scrollbar-plugin": "^2.0.0", |
||||
"@abp/select2": "^1.1.1", |
"@abp/select2": "^2.0.0", |
||||
"@abp/sweetalert": "^1.1.1", |
"@abp/sweetalert": "^2.0.0", |
||||
"@abp/timeago": "^1.1.1", |
"@abp/timeago": "^2.0.0", |
||||
"@abp/toastr": "^1.1.1" |
"@abp/toastr": "^2.0.0" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,13 +1,13 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/blogging", |
"name": "@abp/blogging", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/aspnetcore.mvc.ui.theme.shared": "^1.1.1", |
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.0.0", |
||||
"@abp/owl.carousel": "^1.1.1", |
"@abp/owl.carousel": "^2.0.0", |
||||
"@abp/tui-editor": "^1.1.1" |
"@abp/tui-editor": "^2.0.0" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/bootstrap", |
"name": "@abp/bootstrap", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"bootstrap": "^4.3.1" |
"bootstrap": "^4.3.1" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/clipboard", |
"name": "@abp/clipboard", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"clipboard": "^2.0.4" |
"clipboard": "^2.0.4" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/codemirror", |
"name": "@abp/codemirror", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"codemirror": "^5.49.2" |
"codemirror": "^5.49.2" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,8 +1,8 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/core", |
"name": "@abp/core", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/datatables.net-bs4", |
"name": "@abp/datatables.net-bs4", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/datatables.net": "^1.1.1", |
"@abp/datatables.net": "^2.0.0", |
||||
"datatables.net-bs4": "^1.10.20" |
"datatables.net-bs4": "^1.10.20" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/datatables.net", |
"name": "@abp/datatables.net", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"datatables.net": "^1.10.20" |
"datatables.net": "^1.10.20" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,15 +1,15 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/docs", |
"name": "@abp/docs", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/anchor-js": "^1.1.1", |
"@abp/anchor-js": "^2.0.0", |
||||
"@abp/clipboard": "^1.1.1", |
"@abp/clipboard": "^2.0.0", |
||||
"@abp/malihu-custom-scrollbar-plugin": "^1.1.1", |
"@abp/malihu-custom-scrollbar-plugin": "^2.0.0", |
||||
"@abp/popper.js": "^1.1.1", |
"@abp/popper.js": "^2.0.0", |
||||
"@abp/prismjs": "^1.1.1" |
"@abp/prismjs": "^2.0.0" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/font-awesome", |
"name": "@abp/font-awesome", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"@fortawesome/fontawesome-free": "^5.11.2" |
"@fortawesome/fontawesome-free": "^5.11.2" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,11 +1,11 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/highlight.js", |
"name": "@abp/highlight.js", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1" |
"@abp/core": "^2.0.0" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/jquery-form", |
"name": "@abp/jquery-form", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/jquery": "^1.1.1", |
"@abp/jquery": "^2.0.0", |
||||
"jquery-form": "^4.2.2" |
"jquery-form": "^4.2.2" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/jquery-validation-unobtrusive", |
"name": "@abp/jquery-validation-unobtrusive", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/jquery-validation": "^1.1.1", |
"@abp/jquery-validation": "^2.0.0", |
||||
"jquery-validation-unobtrusive": "^3.2.11" |
"jquery-validation-unobtrusive": "^3.2.11" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/jquery-validation", |
"name": "@abp/jquery-validation", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/jquery": "^1.1.1", |
"@abp/jquery": "^2.0.0", |
||||
"jquery-validation": "^1.19.1" |
"jquery-validation": "^1.19.1" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/jquery", |
"name": "@abp/jquery", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"jquery": "^3.4.1" |
"jquery": "^3.4.1" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/lodash", |
"name": "@abp/lodash", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"lodash": "^4.17.15" |
"lodash": "^4.17.15" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/malihu-custom-scrollbar-plugin", |
"name": "@abp/malihu-custom-scrollbar-plugin", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"malihu-custom-scrollbar-plugin": "^3.1.5" |
"malihu-custom-scrollbar-plugin": "^3.1.5" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/markdown-it", |
"name": "@abp/markdown-it", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"markdown-it": "^10.0.0" |
"markdown-it": "^10.0.0" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/owl.carousel", |
"name": "@abp/owl.carousel", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"owl.carousel": "^2.3.4" |
"owl.carousel": "^2.3.4" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/popper.js", |
"name": "@abp/popper.js", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"popper.js": "^1.16.0" |
"popper.js": "^1.16.0" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/prismjs", |
"name": "@abp/prismjs", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"prismjs": "^1.17.1" |
"prismjs": "^1.17.1" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/select2", |
"name": "@abp/select2", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"select2": "^4.0.12" |
"select2": "^4.0.12" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/sweetalert", |
"name": "@abp/sweetalert", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/core": "^1.1.1", |
"@abp/core": "^2.0.0", |
||||
"sweetalert": "^2.1.2" |
"sweetalert": "^2.1.2" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/timeago", |
"name": "@abp/timeago", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/jquery": "^1.1.1", |
"@abp/jquery": "^2.0.0", |
||||
"timeago": "^1.6.7" |
"timeago": "^1.6.7" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/toastr", |
"name": "@abp/toastr", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/jquery": "^1.1.1", |
"@abp/jquery": "^2.0.0", |
||||
"toastr": "^2.1.4" |
"toastr": "^2.1.4" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
@ -1,15 +1,15 @@ |
|||||
{ |
{ |
||||
"version": "1.1.1", |
"version": "2.0.0", |
||||
"name": "@abp/tui-editor", |
"name": "@abp/tui-editor", |
||||
"publishConfig": { |
"publishConfig": { |
||||
"access": "public" |
"access": "public" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/codemirror": "^1.1.1", |
"@abp/codemirror": "^2.0.0", |
||||
"@abp/highlight.js": "^1.1.1", |
"@abp/highlight.js": "^2.0.0", |
||||
"@abp/jquery": "^1.1.1", |
"@abp/jquery": "^2.0.0", |
||||
"@abp/markdown-it": "^1.1.1", |
"@abp/markdown-it": "^2.0.0", |
||||
"tui-editor": "^1.4.8" |
"tui-editor": "^1.4.8" |
||||
}, |
}, |
||||
"gitHead": "b926d7a3fed6365550493ec00bdfe9da9dd6c541" |
"gitHead": "407ad749bb735eb9b6cc1ef6dd310253b9680883" |
||||
} |
} |
||||
|
|||||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 101 KiB |