mirror of https://github.com/abpframework/abp.git
162 changed files with 5958 additions and 241 deletions
@ -0,0 +1,13 @@ |
|||
{ |
|||
"culture": "sl", |
|||
"texts": { |
|||
"Account": "Račun", |
|||
"Welcome": "Dobrodošli", |
|||
"UseOneOfTheFollowingLinksToContinue": "Za nadaljevanje uporabite eno od naslednjih povezav", |
|||
"FrameworkHomePage": "Domača stran razvojnega okolja", |
|||
"FrameworkDocumentation": "Dokumentacija razvojnega okolja", |
|||
"OfficialBlog": "Uradni blog", |
|||
"CommercialHomePage": "Domača stran različice Commercial", |
|||
"CommercialSupportWebSite": "Spletna stran za komercialno podporo" |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
{ |
|||
"culture": "sl", |
|||
"texts": { |
|||
"Permission:Organizations": "Organizacije", |
|||
"Permission:Manage": "Upravljaj organizacije", |
|||
"Permission:NpmPackages": "NPM paketi", |
|||
"Permission:NugetPackages": "Nuget paketi", |
|||
"Permission:Maintenance": "Vzdrževanje", |
|||
"Permission:Maintain": "Vzdrževanje", |
|||
"Permission:ClearCaches": "Počisti predpomnilnike", |
|||
"Permission:Modules": "Moduli", |
|||
"Permission:Packages": "Paketi", |
|||
"Permission:Edit": "Urejanje", |
|||
"Permission:Delete": "Brisanje", |
|||
"Permission:Create": "Ustvarjanje", |
|||
"Menu:Organizations": "Organizacije", |
|||
"Menu:Packages": "Paketi", |
|||
"NpmPackageDeletionWarningMessage": "Ta paket NPM bo izbrisan. Ali to potrjujete?", |
|||
"NugetPackageDeletionWarningMessage": "Ta Nuget paket bo izbrisan. Ali to potrjujete?", |
|||
"ModuleDeletionWarningMessage": "Ta modul bo izbrisan. Ali to potrjujete?", |
|||
"Name": "Naziv", |
|||
"DisplayName": "Naziv za prikaz", |
|||
"ShortDescription": "Kratek opis", |
|||
"NameFilter": "Naziv", |
|||
"CreationTime": "Čas nastanka", |
|||
"IsPro": "Je pro", |
|||
"EfCoreConfigureMethodName": "Konfiguriraj ime metode", |
|||
"IsProFilter": "Je pro", |
|||
"ApplicationType": "Tip aplikacije", |
|||
"Target": "Cilj", |
|||
"TargetFilter": "Cilj", |
|||
"ModuleClass": "Razred modula", |
|||
"NugetPackageTarget.DomainShared": "Domain Shared", |
|||
"NugetPackageTarget.Domain": "Domain", |
|||
"NugetPackageTarget.Application": "Application", |
|||
"NugetPackageTarget.ApplicationContracts": "Application Contracts", |
|||
"NugetPackageTarget.HttpApi": "Http Api", |
|||
"NugetPackageTarget.HttpApiClient": "Http Api Client", |
|||
"NugetPackageTarget.Web": "Web", |
|||
"NugetPackageTarget.EntityFrameworkCore": "DeleteAllEntityFramework Core", |
|||
"NugetPackageTarget.MongoDB": "MongoDB", |
|||
"Edit": "Uredi", |
|||
"Delete": "Izbriši", |
|||
"Refresh": "Osveži", |
|||
"NpmPackages": "NPM paketi", |
|||
"NugetPackages": "Nuget paketi", |
|||
"NpmPackageCount": "Število NPM paketov", |
|||
"NugetPackageCount": "Število Nuget paketov", |
|||
"Module": "Moduli", |
|||
"ModuleInfo": "Informacije o modulu", |
|||
"CreateANpmPackage": "Ustvari NPM paket", |
|||
"CreateAModule": "Ustvari modul", |
|||
"CreateANugetPackage": "Ustvari Nuget paket", |
|||
"AddNew": "Dodaj novega", |
|||
"PackageAlreadyExist{0}": "Paket \"{0}\" je že dodan.", |
|||
"ModuleAlreadyExist{0}": "Modul \"{0}\" je že dodan.", |
|||
"ClearCache": "Počisti predpomnilnik", |
|||
"SuccessfullyCleared": "Uspešno izbrisano", |
|||
"Menu:NpmPackages": "NPM paketi", |
|||
"Menu:Modules": "Moduli", |
|||
"Menu:Maintenance": "Vzdrževanje", |
|||
"Menu:NugetPackages": "Nuget paketi", |
|||
"CreateAnOrganization": "Ustvari organizacijo", |
|||
"Organizations": "Organizacije", |
|||
"LongName": "Dolg naziv", |
|||
"LicenseType": "Tip licence", |
|||
"LicenseStartTime": "Čas začetka licence", |
|||
"LicenseEndTime": "Čas konca licence", |
|||
"AllowedDeveloperCount": "Dovoljeno število razvijalcev", |
|||
"UserNameOrEmailAddress": "Uporabniško ime ali e-poštni naslov", |
|||
"AddOwner": "Dodaj lastnika", |
|||
"UserName": "Uporabniško ime", |
|||
"Email": "E-poštni naslov", |
|||
"Developers": "Razvijalci", |
|||
"AddDeveloper": "Dodaj razvijalca", |
|||
"Create": "Ustvari", |
|||
"UserNotFound": "Uporabnika ni mogoče najti", |
|||
"{0}WillBeRemovedFromMembers": "{0} bo odstranjen iz članov", |
|||
"Computers": "Računalniki", |
|||
"UniqueComputerId": "Unikatni id računalnika", |
|||
"LastSeenDate": "Zadnjič viden", |
|||
"{0}Computer{1}WillBeRemovedFromRecords": "Računalnik {0} ({1}) bo odstranjen iz zapisov", |
|||
"OrganizationDeletionWarningMessage": "Organizacija bo izbrisana", |
|||
"This{0}AlreadyExistInThisOrganization": "{0} že obstaja v tej organizaciji", |
|||
"AreYouSureYouWantToDeleteAllComputers": "Ali ste prepričani, da želite izbrisati vse računalnike?", |
|||
"DeleteAll": "Izbriši vse", |
|||
"DoYouWantToCreateNewUser": "Ali želite ustvariti novega uporabnika?", |
|||
"MasterModules": "Glavni moduli" |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
{ |
|||
"culture": "sl", |
|||
"texts": { |
|||
"Volo.AbpIo.Domain:010004": "Doseženo je največje število članov!", |
|||
"Volo.AbpIo.Domain:010005": "Doseženo je največje število lastnikov!", |
|||
"Volo.AbpIo.Domain:010006": "Ta uporabnik je že lastnik te organizacije!", |
|||
"Volo.AbpIo.Domain:010007": "Ta uporabnik je že razvijalec v tej organizaciji!", |
|||
"Volo.AbpIo.Domain:010008": "Dovoljeno število razvijalcev ne sme biti manjše od trenutnega števila razvijalcev!", |
|||
"Volo.AbpIo.Domain:010009": "Dovoljeno število razvijalcev ne sme biti manjše od 0!", |
|||
"Volo.AbpIo.Domain:010010": "Največje število mac naslovov je preseženo!", |
|||
"Volo.AbpIo.Domain:010011": "Osebne licence ne sme imeti več kot 1 razvijalec!", |
|||
"Volo.AbpIo.Domain:010012": "Licence ni mogoče podaljšati en mesec po poteku licence!", |
|||
"Volo.AbpIo.Domain:020001": "Tega paketa NPM ni mogoče izbrisati, ker so \ "{NugetPackages} \" Nuget paketi odvisni od tega paketa.", |
|||
"Volo.AbpIo.Domain:020002": "Tega paketa NPM ni mogoče izbrisati, ker \ "{Modules} \" moduli uporabljajo ta paket.", |
|||
"Volo.AbpIo.Domain:020003": "Tega paketa NPM ni mogoče izbrisati, ker \ "{Modules} \" moduli uporabljajo ta paket in \ "{NugetPackages} \" Nuget paketi so odvisni od tega paketa.", |
|||
"Volo.AbpIo.Domain:020004": "Nuget paketa ni mogoče izbrisati, ker \ "{Modules} \" moduli uporabljajo ta paket.", |
|||
"WantToLearn?": "Se želite naučiti?", |
|||
"ReadyToGetStarted?": "Pripravljeni, da bi začeli?", |
|||
"JoinOurCommunity": "Pridružite se naši skupnosti", |
|||
"GetStartedUpper": "ZAČNI", |
|||
"ForkMeOnGitHub": "Naredi vejitev na GitHub", |
|||
"Features": "Funkcionalnosti", |
|||
"GetStarted": "Začni", |
|||
"Documents": "Dokumenti", |
|||
"Community": "Skupnost", |
|||
"ContributionGuide": "Vodič za prispevke", |
|||
"Blog": "Blog", |
|||
"Commercial": "Commercial", |
|||
"SeeDocuments": "Poglej dokumente" |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
{ |
|||
"culture": "sl", |
|||
"texts": { |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
{ |
|||
"culture": "sl", |
|||
"texts": { |
|||
"OrganizationManagement": "Upravljanje organizacije", |
|||
"OrganizationList": "Seznam organizacij", |
|||
"Volo.AbpIo.Commercial:010003": "Niste lastnik te organizacije!", |
|||
"OrganizationNotFoundMessage": "Nobene organizacije ni bilo mogoče najti!", |
|||
"DeveloperCount": "Dodeljeni / skupno število razvijalcev", |
|||
"QuestionCount": "Preostala / skupno število vprašanj", |
|||
"Unlimited": "Neomejeno", |
|||
"Owners": "Lastniki", |
|||
"AddMember": "Dodaj člana", |
|||
"AddOwner": "Dodaj lastnika", |
|||
"AddDeveloper": "Dodaj razvijalce", |
|||
"UserName": "Uporabniško ime", |
|||
"Name": "Ime", |
|||
"EmailAddress": "E-poštni naslov", |
|||
"Developers": "Razvijalci", |
|||
"LicenseType": "Tip licence", |
|||
"Manage": "Upravljaj", |
|||
"StartDate": "Datum začetka", |
|||
"EndDate": "Datum konca", |
|||
"Modules": "Moduli", |
|||
"LicenseExtendMessage": "Končni datum veljavnosti licence je podaljšan na {0}", |
|||
"LicenseUpgradeMessage": "Vaša licenca je nadgrajena na {0}", |
|||
"LicenseAddDeveloperMessage": "{0} razvijalcev je bilo dodanih k vaši licenci", |
|||
"Volo.AbpIo.Commercial:010004": "Navedenega uporabnika ni mogoče najti! Uporabnik mora biti registriran.", |
|||
"MyOrganizations": "Moje organizacije", |
|||
"ApiKey": "Ključ API", |
|||
"UserNameNotFound": "Ni uporabnika z uporabniškim imenom {0}", |
|||
"SuccessfullyAddedToNewsletter": "Hvala, ker ste se naročili na naše novice!", |
|||
"ManageProfile": "Upravljaj svoj profil", |
|||
"EmailNotValid": "Vnesite veljaven e-poštni naslov." |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
{ |
|||
"culture": "sl", |
|||
"texts": { |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
{ |
|||
"culture": "sl", |
|||
"texts": { |
|||
} |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
|
|||
This document has moved. |
|||
|
|||
[Click to navigate to ASP.NET Core MVC Client Side Package Management document](../UI/AspNetCore/Client-Side-Package-Management.md) |
|||
[Click to navigate to ASP.NET Core MVC Client Side Package Management document](../UI/AspNetCore/Client-Side-Package-Management.md) |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
This document has moved. |
|||
|
|||
[Click to navigate to Dynamic C# API Clients document](../API/Dynamic-CSharp-API-Clients.md) |
|||
[Click to navigate to Dynamic C# API Clients document](../API/Dynamic-CSharp-API-Clients.md) |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
This document has moved. |
|||
|
|||
[Click to navigate to ABP Tag Helpers document](../../UI/AspNetCore/Tag-Helpers/Index.md) |
|||
[Click to navigate to ABP Tag Helpers document](../../UI/AspNetCore/Tag-Helpers/Index.md) |
|||
|
|||
@ -1,4 +1,4 @@ |
|||
|
|||
This document has moved. |
|||
|
|||
[Click to navigate to Widgets document](../UI/AspNetCore/Widgets.md) |
|||
[Click to navigate to Widgets document](../UI/AspNetCore/Widgets.md) |
|||
|
|||
@ -0,0 +1,90 @@ |
|||
# Customizing the Existing Modules |
|||
|
|||
ABP Framework provides was designed to support to build fully [modular applications](Module-Development-Basics.md) and systems. It also provides some [pre-built application modules](Modules/Index.md) those are **ready to use** in any kind of application. |
|||
|
|||
For example, you can **re-use** the [Identity Management Module](Modules/Identity.md) to add user, role and permission management to your application. The [application startup template](Startup-Templates/Application.md) already comes with Identity and some other modules **pre-installed**. |
|||
|
|||
## Re-Using an Application Module |
|||
|
|||
You have two options to re-use an application module. |
|||
|
|||
### As Package References |
|||
|
|||
You can add **NuGet** & **NPM** package references of the related module to your application and configure the module (based on its documentation) to integrate to your application. |
|||
|
|||
As mentioned before, the [application startup template](Startup-Templates/Application.md) already comes with some **fundamental modules pre-installed**. It uses the modules as NuGet & NPM package references. |
|||
|
|||
This approach has the following benefits: |
|||
|
|||
* Your solution will be **clean** and only contains your **own application code**. |
|||
* You can **easily upgrade** a module when a new version is available. `abp update` [CLI](CLI.md) command makes it even easier. In this way, you can continue to get **new features and bug fixes**. |
|||
|
|||
However, there is a drawback: |
|||
|
|||
* You may not able to **customize** the module source code as it is in your own solution. |
|||
|
|||
This document explains **how to customize or extend** a depended module without need to change its source code. While it is limited compared to a full source code change opportunity, there are still some good ways to make some customizations. |
|||
|
|||
If you don't think to make huge changes on the pre-built modules, re-using them as package reference is the recommended way. |
|||
|
|||
### Including the Source Code |
|||
|
|||
If you want to make **huge changes** or add **major features** on a pre-built module, but the available extension points are not enough, you can consider to directly work the source code of the depended module. |
|||
|
|||
In this case, you typically **add the source code** of the module to your solution and **replace package references** by local project references. **[ABP CLI](CLI.md)** automates this process for you. |
|||
|
|||
#### Separating the Module Solution |
|||
|
|||
You may prefer to not include the module source code **directly into your solution**. Every module consists of 10+ project files and adding **multiple modules** may impact on the **size** of your solution **load & development time.** Also, you may have different development teams working on different modules, so you don't want to make the module code available to the application development team. |
|||
|
|||
In any case, you can create a **separate solution** for the desired module and depend on the module as project references out of the solution. We do it like that for the [abp repository](https://github.com/abpframework/abp/). |
|||
|
|||
> One problem we see is Visual Studio doesn't play nice with this kind of approach (it doesn't support well to have references to local projects out of the solution directory). If you get error while building the application (depends on an external module), run `dotnet restore` in the command line after opening the application's solution in the Visual Studio. |
|||
|
|||
#### Publishing the Customized Module as Packages |
|||
|
|||
One alternative scenario could be re-packaging the module source code (as NuGet/NPM packages) and using as package references. You can use a local private NuGet/NPM server for your company. |
|||
|
|||
## Module Customization / Extending Approaches |
|||
|
|||
This section suggests some approaches if you decided to use pre-built application modules as NuGet/NPM package references. |
|||
|
|||
### Extending Entities |
|||
|
|||
In some cases, you may want to add some additional properties (and database fields) for an entity defined in a depended module. This section will cover some different approaches to make this possible. |
|||
|
|||
#### Extra Properties |
|||
|
|||
[Extra properties](Entities.md) is a way of storing some additional data on an entity without changing it. The entity should implement the `IHasExtraProperties` interface to allow it. All the aggregate root entities defined in the pre-built modules implement the `IHasExtraProperties` interface, so you can store extra properties on these entities. |
|||
|
|||
Example: |
|||
|
|||
````csharp |
|||
//SET AN EXTRA PROPERTY |
|||
var user = await _identityUserRepository.GetAsync(userId); |
|||
user.SetProperty("Title", "My custom title value!"); |
|||
await _identityUserRepository.UpdateAsync(user); |
|||
|
|||
//GET AN EXTRA PROPERTY |
|||
var user = await _identityUserRepository.GetAsync(userId); |
|||
return user.GetProperty<string>("Title"); |
|||
```` |
|||
|
|||
This approach is very easy to use and available out of the box. No extra code needed. You can store more than one property at the same time by using different property names (like `Title` here). |
|||
|
|||
Extra properties are stored as a single `JSON` formatted string value in the database for the EF Core. For MongoDB, they are stored as separate fields of the document. |
|||
|
|||
See the [entities document](Entities.md) for more about the extra properties system. |
|||
|
|||
> It is possible to perform a **business logic** based on the value of an extra property. You can **override** a service method and get or set the value as shown above. Overriding services will be discussed below. |
|||
|
|||
#### Creating a New Entity Maps to the Same Database Table/Collection |
|||
|
|||
While using the extra properties approach is easy to use and suitable for some scenarios, it has some drawbacks described in the [entities document](Entities.md). |
|||
|
|||
Another approach can be creating your own entity mapped to the same database table (or collection for a MongoDB database). |
|||
|
|||
`AppUser` entity in the [application startup template](Startup-Templates/Application.md) already implements this approach. [EF Core Migrations document](Entity-Framework-Core-Migrations.md) describes how to implement it and manage database migrations in such a case. It is also possible for MongoDB, while this time you won't deal with the database migration problems. |
|||
|
|||
#### Creating a New Entity with Its Own Database Table/Collection |
|||
|
|||
@ -0,0 +1,94 @@ |
|||
# Buttons |
|||
|
|||
ABP framework has a special Tag Helper to create bootstrap button easily. |
|||
|
|||
`<abp-button>` |
|||
|
|||
## Attributes |
|||
|
|||
`<abp-button>` has 7 different attribute. |
|||
|
|||
* [`button-type`](#button-type) |
|||
* [`size`](#size) |
|||
* [`busy-text`](#busy-text) |
|||
* [`text`](#text) |
|||
* [`icon`](#icon) |
|||
* [`disabled`](#disabled) |
|||
* [`icon-type`](#icon-type) |
|||
|
|||
### `button-type` |
|||
|
|||
`button-type` is a selectable parameter. It's default value is `Default`. |
|||
|
|||
`<abp-button button-type="Primary">Button</abp-button>` |
|||
|
|||
You can choose one of the button type listed below. |
|||
|
|||
* `Default` |
|||
* `Primary` |
|||
* `Secondary` |
|||
* `Success` |
|||
* `Danger` |
|||
* `Warning` |
|||
* `Info` |
|||
* `Light` |
|||
* `Dark` |
|||
* `Outline_Primary` |
|||
* `Outline_Secondary` |
|||
* `Outline_Success` |
|||
* `Outline_Danger` |
|||
* `Outline_Warning` |
|||
* `Outline_Info` |
|||
* `Outline_Light` |
|||
* `Outline_Dark` |
|||
* `Link` |
|||
|
|||
### `size` |
|||
|
|||
`size` is a selectable parameter. It's default value is `Default`. |
|||
|
|||
`<abp-button size="Default">Button</abp-button>` |
|||
|
|||
You can choose one of the size type listed below. |
|||
|
|||
* `Default` |
|||
* `Small` |
|||
* `Medium` |
|||
* `Large` |
|||
* `Block` |
|||
* `Block_Small` |
|||
* `Block_Medium` |
|||
* `Block_Large` |
|||
|
|||
### `busy-text` |
|||
|
|||
`busy-text` is a string parameter. It shows the text while the button is busy. |
|||
|
|||
### `text` |
|||
|
|||
`text` is a string parameter that displaying on button. |
|||
|
|||
### `icon` |
|||
|
|||
`icon` is a string parameter. It is depending to [`icon-type`](#`icon-type`). For default, we use [Font Awesome](https://fontawesome.com/) for icons. To use it, you need to set `icon` parameter as a icon name. |
|||
|
|||
##### Example |
|||
|
|||
[fa-address-card](https://fontawesome.com/icons/address-card):  |
|||
|
|||
`<abp-button icon="address-card" text="Address" />` |
|||
|
|||
> Don't forget: You dont need to write prefix! It will add automatically "fa" prefix for [Font Awesome](https://fontawesome.com/) icons while you did not change `icon-type`. |
|||
|
|||
### `disabled` |
|||
|
|||
`disabled` is a boolean parameter. If you set it `true`, your button will be disabled. |
|||
|
|||
### `icon-type` |
|||
|
|||
`icon-type` is a selectable parameter. It's default value is `FontAwesome`. You can create your own icon type provider and change it. |
|||
|
|||
You can choose one of the icon type listed below. |
|||
|
|||
* `FontAwesome` |
|||
* `Other` |
|||
@ -1,3 +1,3 @@ |
|||
## ABP Tag Helpers |
|||
|
|||
"ABP tag helpers" is not documented yet. You can see a [demo of components](http://bootstrap-taghelpers.abp.io/) for now. |
|||
"ABP tag helpers" documentation is creating now. You can see a [demo of components](http://bootstrap-taghelpers.abp.io/) for now. |
|||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 330 KiB |
|
After Width: | Height: | Size: 38 KiB |
@ -0,0 +1,94 @@ |
|||
# 按钮 |
|||
|
|||
ABP框架定义了Tag Helper用于简单的创建bootstrap按钮. |
|||
|
|||
`<abp-button>` |
|||
|
|||
## 属性 |
|||
|
|||
`<abp-button>` 有7个不同的属性. |
|||
|
|||
* [`button-type`](#button-type) |
|||
* [`size`](#size) |
|||
* [`busy-text`](#busy-text) |
|||
* [`text`](#text) |
|||
* [`icon`](#icon) |
|||
* [`disabled`](#disabled) |
|||
* [`icon-type`](#icon-type) |
|||
|
|||
### `button-type` |
|||
|
|||
`button-type` 是一个可选参数. 它的默认值是 `Default`. |
|||
|
|||
`<abp-button button-type="Primary">Button</abp-button>` |
|||
|
|||
你可以为按钮选择以下按钮类型: |
|||
|
|||
* `Default` |
|||
* `Primary` |
|||
* `Secondary` |
|||
* `Success` |
|||
* `Danger` |
|||
* `Warning` |
|||
* `Info` |
|||
* `Light` |
|||
* `Dark` |
|||
* `Outline_Primary` |
|||
* `Outline_Secondary` |
|||
* `Outline_Success` |
|||
* `Outline_Danger` |
|||
* `Outline_Warning` |
|||
* `Outline_Info` |
|||
* `Outline_Light` |
|||
* `Outline_Dark` |
|||
* `Link` |
|||
|
|||
### `size` |
|||
|
|||
`size` 是一个可选参数. 它的默认值是 `Default`. |
|||
|
|||
`<abp-button size="Default">Button</abp-button>` |
|||
|
|||
你可以为按钮选择以下size类型: |
|||
|
|||
* `Default` |
|||
* `Small` |
|||
* `Medium` |
|||
* `Large` |
|||
* `Block` |
|||
* `Block_Small` |
|||
* `Block_Medium` |
|||
* `Block_Large` |
|||
|
|||
### `busy-text` |
|||
|
|||
`busy-text` 是一个字符串类型参数. 当按钮繁忙时设置该文本. |
|||
|
|||
### `text` |
|||
|
|||
`text` 是一个字符串类型参数,显示在按钮上. |
|||
|
|||
### `icon` |
|||
|
|||
`icon` 是一个字符串类型参数. 它的值取决于[`icon-type`](#`icon-type`). 默认情况下,我们对图标使用[Font Awesome](https://fontawesome.com/). 要使用它,你需要将 `icon` 参数设置为图标名称. |
|||
|
|||
##### 示例 |
|||
|
|||
[fa-address-card](https://fontawesome.com/icons/address-card):  |
|||
|
|||
`<abp-button icon="address-card" text="Address" />` |
|||
|
|||
> 不要忘记: 你不需要写前缀,如果你没有更改 `icon-type` ,它会为[Font Awesome](https://fontawesome.com/)图标自动添加 `fa` 前缀. |
|||
|
|||
### `disabled` |
|||
|
|||
`disabled` 是一个布尔类型参数. 如果你设值为 `true`, 按钮会被禁用. |
|||
|
|||
### `icon-type` |
|||
|
|||
`icon-type` 是一个可选参数。它的默认值是 `FontAwesome`. 你可以创建自己的图标类型提供程序并更改它. |
|||
|
|||
你可以为按钮选择以下图标类型: |
|||
|
|||
* `FontAwesome` |
|||
* `Other` |
|||
@ -1,3 +1,3 @@ |
|||
## ABP Tag Helpers |
|||
|
|||
"ABP tag helpers" 目前还没有文档. 你现在可以看到[组件演示](http://bootstrap-taghelpers.abp.io/). |
|||
"ABP tag helpers" 文档还在创建中. 你现在可以参阅[组件演示](http://bootstrap-taghelpers.abp.io/). |
|||
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,12 @@ |
|||
{ |
|||
"culture": "sl", |
|||
"texts": { |
|||
"GivenTenantIsNotAvailable": "Najemnik ni na voljo: {0}", |
|||
"Tenant": "Najemnik", |
|||
"Switch": "Zamenjaj", |
|||
"Name": "Naziv", |
|||
"SwitchTenantHint": "Pustite polje z imenom prazno, da preklopite na stran za upravnike.", |
|||
"SwitchTenant": "Zamenjaj najemnika", |
|||
"NotSelected": "Ni izbran" |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
{ |
|||
"culture": "sl", |
|||
"texts": { |
|||
"DisplayName:Abp.Mailing.DefaultFromAddress": "Privzeti naslov za pošiljanje", |
|||
"DisplayName:Abp.Mailing.DefaultFromDisplayName": "Privzeto ime za prikaz pri pošiljanju", |
|||
"DisplayName:Abp.Mailing.Smtp.Host": "Gostitelj", |
|||
"DisplayName:Abp.Mailing.Smtp.Port": "Vrata", |
|||
"DisplayName:Abp.Mailing.Smtp.UserName": "Uporabniško ime", |
|||
"DisplayName:Abp.Mailing.Smtp.Password": "Geslo", |
|||
"DisplayName:Abp.Mailing.Smtp.Domain": "Domena", |
|||
"DisplayName:Abp.Mailing.Smtp.EnableSsl": "Omogoči SSL", |
|||
"DisplayName:Abp.Mailing.Smtp.UseDefaultCredentials": "Uporabi privzete poverilnice", |
|||
"Description:Abp.Mailing.DefaultFromAddress": "Privzeti naslov za pošiljanje", |
|||
"Description:Abp.Mailing.DefaultFromDisplayName": "Privzeto ime za prikaz pri pošiljanju", |
|||
"Description:Abp.Mailing.Smtp.Host": "Naziv ali IP naslov gostitelja za uporabo SMTP transakcij.", |
|||
"Description:Abp.Mailing.Smtp.Port": "Vrata za uporabo SMTP transakcij.", |
|||
"Description:Abp.Mailing.Smtp.UserName": "Uporabniško ime, povezano s poverilnicami.", |
|||
"Description:Abp.Mailing.Smtp.Password": "The password for the user name associated with the credentials.", |
|||
"Description:Abp.Mailing.Smtp.Domain": "Geslo za uporabniško ime, povezano s poverilnicami.", |
|||
"Description:Abp.Mailing.Smtp.EnableSsl": "Ali SmtpClient uporablja sloj varnih vtičnic (SSL) za šifriranje povezave.", |
|||
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "Ali so privzete poverilnice poslane skupaj z zahtevki." |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"culture": "sl", |
|||
"texts": { |
|||
"DisplayName:Abp.Localization.DefaultLanguage": "Privzeti jezik", |
|||
"Description:Abp.Localization.DefaultLanguage": "Privzeti jezik aplikacije." |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "sl", |
|||
"texts": { |
|||
"Menu:Administration": "Administracija" |
|||
} |
|||
} |
|||
@ -1,4 +1,7 @@ |
|||
{ |
|||
"ConnectionString": "Server=localhost;Database=VoloDocs;Trusted_Connection=True;MultipleActiveResultSets=true", |
|||
"LogoUrl": "/assets/images/Logo.png" |
|||
"LogoUrl": "/assets/images/Logo.png", |
|||
"ElasticSearch": { |
|||
"Url": "http://localhost:9200" |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Docs.Documents |
|||
{ |
|||
public class DocumentSearchInput |
|||
{ |
|||
public string Context { get; set; } |
|||
|
|||
public Guid ProjectId { get; set; } |
|||
|
|||
public string LanguageCode { get; set; } |
|||
|
|||
public string Version { get; set; } |
|||
|
|||
public int? SkipCount { get; set; } |
|||
|
|||
public int? MaxResultCount { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace Volo.Docs.Documents |
|||
{ |
|||
public class DocumentSearchOutput |
|||
{ |
|||
public DocumentSearchOutput() |
|||
{ |
|||
Highlight = new List<string>(); |
|||
} |
|||
|
|||
public string Name { get; set; } |
|||
|
|||
public string FileName { get; set; } |
|||
|
|||
public string Version { get; set; } |
|||
|
|||
public string LanguageCode { get; set; } |
|||
|
|||
public List<string> Highlight { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Docs |
|||
{ |
|||
public static class DocsDomainErrorCodes |
|||
{ |
|||
public const string ElasticSearchNotEnabled = "Volo.Docs.Domain:010001"; |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.Options; |
|||
using Nest; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Docs.Documents.FullSearch.Elastic |
|||
{ |
|||
public class DefaultElasticClientProvider : IElasticClientProvider, ISingletonDependency |
|||
{ |
|||
protected readonly DocsElasticSearchOptions Options; |
|||
protected readonly IConfiguration Configuration; |
|||
|
|||
public DefaultElasticClientProvider(IOptions<DocsElasticSearchOptions> options, IConfiguration configuration) |
|||
{ |
|||
Configuration = configuration; |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public virtual IElasticClient GetClient() |
|||
{ |
|||
var node = new Uri(Configuration["ElasticSearch:Url"]); |
|||
var settings = new ConnectionSettings(node).DefaultIndex(Options.IndexName); |
|||
return new ElasticClient(settings); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
namespace Volo.Docs.Documents.FullSearch.Elastic |
|||
{ |
|||
public class DocsElasticSearchOptions |
|||
{ |
|||
public DocsElasticSearchOptions() |
|||
{ |
|||
Enable = false; |
|||
IndexName = "abp_documents"; |
|||
} |
|||
|
|||
public bool Enable { get; set; } |
|||
|
|||
public string IndexName { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Entities.Events; |
|||
using Volo.Abp.EventBus; |
|||
|
|||
namespace Volo.Docs.Documents.FullSearch.Elastic |
|||
{ |
|||
public class DocumentChangedEventHandler : ILocalEventHandler<EntityCreatedEventData<Document>>, |
|||
ILocalEventHandler<EntityUpdatedEventData<Document>>, |
|||
ILocalEventHandler<EntityDeletedEventData<Document>>, |
|||
ITransientDependency |
|||
{ |
|||
private readonly DocsElasticSearchOptions _options; |
|||
private readonly IDocumentFullSearch _documentFullSearch; |
|||
|
|||
public DocumentChangedEventHandler(IDocumentFullSearch documentFullSearch, IOptions<DocsElasticSearchOptions> options) |
|||
{ |
|||
_documentFullSearch = documentFullSearch; |
|||
_options = options.Value; |
|||
} |
|||
|
|||
public async Task HandleEventAsync(EntityCreatedEventData<Document> eventData) |
|||
{ |
|||
await AddOrUpdate(eventData.Entity); |
|||
} |
|||
|
|||
public async Task HandleEventAsync(EntityUpdatedEventData<Document> eventData) |
|||
{ |
|||
await AddOrUpdate(eventData.Entity); |
|||
} |
|||
|
|||
private async Task AddOrUpdate(Document document) |
|||
{ |
|||
if (_options.Enable) |
|||
{ |
|||
await _documentFullSearch.AddOrUpdateAsync(document); |
|||
} |
|||
} |
|||
|
|||
public async Task HandleEventAsync(EntityDeletedEventData<Document> eventData) |
|||
{ |
|||
if (_options.Enable) |
|||
{ |
|||
await _documentFullSearch.DeleteAsync(eventData.Entity.Id); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,189 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Elasticsearch.Net; |
|||
using Microsoft.Extensions.Options; |
|||
using Nest; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Services; |
|||
|
|||
namespace Volo.Docs.Documents.FullSearch.Elastic |
|||
{ |
|||
public class ElasticDocumentFullSearch : DomainService, IDocumentFullSearch |
|||
{ |
|||
private readonly IElasticClientProvider _clientProvider; |
|||
private readonly DocsElasticSearchOptions _options; |
|||
|
|||
public ElasticDocumentFullSearch(IElasticClientProvider clientProvider, IOptions<DocsElasticSearchOptions> options) |
|||
{ |
|||
_clientProvider = clientProvider; |
|||
_options = options.Value; |
|||
} |
|||
|
|||
public async Task CreateIndexIfNeededAsync(CancellationToken cancellationToken = default) |
|||
{ |
|||
CheckEsEnabled(); |
|||
|
|||
var client = _clientProvider.GetClient(); |
|||
|
|||
var existsResponse = await client.Indices.ExistsAsync(_options.IndexName, ct: cancellationToken); |
|||
|
|||
HandleError(existsResponse); |
|||
|
|||
if (!existsResponse.Exists) |
|||
{ |
|||
HandleError(await client.Indices.CreateAsync(_options.IndexName, c => c |
|||
.Map<EsDocument>(m => |
|||
{ |
|||
return m |
|||
.Properties(p => p |
|||
.Keyword(x => x.Name(d => d.Id)) |
|||
.Keyword(x => x.Name(d => d.ProjectId)) |
|||
.Keyword(x => x.Name(d => d.Name)) |
|||
.Keyword(x => x.Name(d => d.FileName)) |
|||
.Keyword(x => x.Name(d => d.Version)) |
|||
.Keyword(x => x.Name(d => d.LanguageCode)) |
|||
.Text(x => x.Name(d => d.Content))); |
|||
}), cancellationToken)); |
|||
} |
|||
} |
|||
|
|||
public async Task AddOrUpdateAsync(Document document, CancellationToken cancellationToken = default) |
|||
{ |
|||
CheckEsEnabled(); |
|||
|
|||
var client = _clientProvider.GetClient(); |
|||
|
|||
var existsResponse = await client.DocumentExistsAsync<EsDocument>(DocumentPath<EsDocument>.Id(document.Id), |
|||
x => x.Index(_options.IndexName), cancellationToken); |
|||
|
|||
HandleError(existsResponse); |
|||
|
|||
var esDocument = new EsDocument |
|||
{ |
|||
Id = document.Id, |
|||
ProjectId = document.ProjectId, |
|||
Name = document.Name, |
|||
FileName = document.FileName, |
|||
Content = document.Content, |
|||
LanguageCode = document.LanguageCode, |
|||
Version = document.Version |
|||
}; |
|||
|
|||
if (!existsResponse.Exists) |
|||
{ |
|||
HandleError(await client.IndexAsync<EsDocument>(esDocument, |
|||
x => x.Id(document.Id).Index(_options.IndexName), cancellationToken)); |
|||
} |
|||
else |
|||
{ |
|||
HandleError(await client.UpdateAsync<EsDocument>(DocumentPath<EsDocument>.Id(document.Id), |
|||
x => x.Doc(esDocument).Index(_options.IndexName), cancellationToken)); |
|||
} |
|||
|
|||
} |
|||
|
|||
public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) |
|||
{ |
|||
CheckEsEnabled(); |
|||
|
|||
HandleError(await _clientProvider.GetClient() |
|||
.DeleteAsync(DocumentPath<Document>.Id(id), x => x.Index(_options.IndexName), cancellationToken)); |
|||
} |
|||
|
|||
public async Task<List<EsDocument>> SearchAsync(string context, Guid projectId, string languageCode, |
|||
string version, int? skipCount = null, int? maxResultCount = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
CheckEsEnabled(); |
|||
|
|||
var request = new SearchRequest |
|||
{ |
|||
Size = maxResultCount ?? 10, |
|||
From = skipCount ?? 0, |
|||
Query = new BoolQuery |
|||
{ |
|||
Must = new QueryContainer[] |
|||
{ |
|||
new MatchQuery |
|||
{ |
|||
Field = "content", |
|||
Query = context |
|||
} |
|||
}, |
|||
Filter = new QueryContainer[] |
|||
{ |
|||
new BoolQuery |
|||
{ |
|||
Must = new QueryContainer[] |
|||
{ |
|||
new TermQuery |
|||
{ |
|||
Field = "projectId", |
|||
Value = projectId |
|||
}, |
|||
new TermQuery |
|||
{ |
|||
Field = "version", |
|||
Value = version |
|||
}, |
|||
new TermQuery |
|||
{ |
|||
Field = "languageCode", |
|||
Value = languageCode |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
Highlight = new Highlight |
|||
{ |
|||
PreTags = new[] { "<highlight>" }, |
|||
PostTags = new[] { "</highlight>" }, |
|||
Fields = new Dictionary<Field, IHighlightField> |
|||
{ |
|||
{ |
|||
"content", new HighlightField() |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
//var json = _clientProvider.GetClient().RequestResponseSerializer.SerializeToString(request);
|
|||
var response = await _clientProvider.GetClient().SearchAsync<EsDocument>(request, cancellationToken); |
|||
|
|||
HandleError(response); |
|||
|
|||
var docs = new List<EsDocument>(); |
|||
foreach (var hit in response.Hits) |
|||
{ |
|||
var doc = hit.Source; |
|||
if (hit.Highlight.ContainsKey("content")) |
|||
{ |
|||
doc.Highlight = new List<string>(); |
|||
doc.Highlight.AddRange(hit.Highlight["content"]); |
|||
} |
|||
docs.Add(doc); |
|||
} |
|||
|
|||
return docs; |
|||
} |
|||
|
|||
protected void HandleError(IElasticsearchResponse response) |
|||
{ |
|||
if (!response.ApiCall.Success) |
|||
{ |
|||
throw response.ApiCall.OriginalException; |
|||
} |
|||
} |
|||
|
|||
protected void CheckEsEnabled() |
|||
{ |
|||
if (!_options.Enable) |
|||
{ |
|||
throw new BusinessException(DocsDomainErrorCodes.ElasticSearchNotEnabled); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Docs.Documents.FullSearch.Elastic |
|||
{ |
|||
public class EsDocument |
|||
{ |
|||
public EsDocument() |
|||
{ |
|||
Highlight = new List<string>(); |
|||
} |
|||
|
|||
public Guid Id { get; set; } |
|||
|
|||
public Guid ProjectId { get; set; } |
|||
|
|||
public string Name { get; set; } |
|||
|
|||
public string FileName { get; set; } |
|||
|
|||
public string Version { get; set; } |
|||
|
|||
public string LanguageCode { get; set; } |
|||
|
|||
public string Content { get; set; } |
|||
|
|||
public List<string> Highlight { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Docs.Documents.FullSearch.Elastic |
|||
{ |
|||
public interface IDocumentFullSearch |
|||
{ |
|||
Task CreateIndexIfNeededAsync(CancellationToken cancellationToken = default); |
|||
|
|||
Task AddOrUpdateAsync(Document document, CancellationToken cancellationToken = default); |
|||
|
|||
Task DeleteAsync(Guid id, CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<EsDocument>> SearchAsync(string context, Guid projectId, string languageCode, |
|||
string version, int? skipCount = null, int? maxResultCount = null, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Nest; |
|||
|
|||
namespace Volo.Docs.Documents.FullSearch.Elastic |
|||
{ |
|||
public interface IElasticClientProvider |
|||
{ |
|||
IElasticClient GetClient(); |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
@page |
|||
@using Microsoft.AspNetCore.Mvc.Localization |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Layout |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Theming |
|||
@using Volo.Docs.Localization |
|||
@using Volo.Docs.Pages.Documents |
|||
@inject IHtmlLocalizer<DocsResource> L |
|||
@inject IThemeManager ThemeManager |
|||
@inject IPageLayout PageLayout |
|||
@model SearchModel |
|||
@{ |
|||
Layout = ThemeManager.CurrentTheme.GetEmptyLayout(); |
|||
} |
|||
@section styles { |
|||
<style> |
|||
highlight { |
|||
font-weight: bold; |
|||
color: red; |
|||
font-style: italic; |
|||
} |
|||
</style> |
|||
} |
|||
|
|||
<div class="container"> |
|||
<form method="get" action="/search/@Model.LanguageCode/@Model.ProjectName/@Model.Version/" class="mt-4"> |
|||
<input type="text" asp-for="@Model.KeyWord" class="form-control" /> |
|||
<button type="submit" class="btn-block btn-primary btn-lg mt-3">Search</button> |
|||
</form> |
|||
|
|||
<div class="my-3 p-3 bg-white rounded"> |
|||
<h6 class="border-bottom pb-4 mb-0">Search results</h6> |
|||
|
|||
@foreach (var docs in Model.SearchOutputs) |
|||
{ |
|||
<div class="media text-muted pt-3"> |
|||
<div class="media-body pb-3 mb-0 small border-bottom"> |
|||
<div class="list-group"> |
|||
|
|||
@functions |
|||
{ |
|||
string RemoveFileExtensionFromPath(string path) |
|||
{ |
|||
if (path == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return path.EndsWith("." + @Model.Project.Format) |
|||
? path.Left(path.Length - Model.Project.Format.Length - 1) |
|||
: path; |
|||
} |
|||
} |
|||
|
|||
<a href="/@Model.LanguageCode/@Model.ProjectName/@Model.Version/@RemoveFileExtensionFromPath(docs.Name)"> |
|||
<h3>@RemoveFileExtensionFromPath(docs.Name)</h3></a> |
|||
|
|||
@foreach (var highlight in docs.Highlight) |
|||
{ |
|||
<p class="list-group-item list-group-item-action">@Html.Raw(highlight)</p> |
|||
} |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
} |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,73 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.RazorPages; |
|||
using Volo.Docs.Documents; |
|||
using Volo.Docs.HtmlConverting; |
|||
using Volo.Docs.Models; |
|||
using Volo.Docs.Projects; |
|||
|
|||
namespace Volo.Docs.Pages.Documents |
|||
{ |
|||
public class SearchModel : PageModel |
|||
{ |
|||
[BindProperty(SupportsGet = true)] |
|||
public string ProjectName { get; set; } |
|||
|
|||
[BindProperty(SupportsGet = true)] |
|||
public string Version { get; set; } |
|||
|
|||
[BindProperty(SupportsGet = true)] |
|||
public string LanguageCode { get; set; } |
|||
|
|||
[BindProperty(SupportsGet = true)] |
|||
public string KeyWord { get; set; } |
|||
|
|||
public ProjectDto Project { get; set; } |
|||
|
|||
private readonly IProjectAppService _projectAppService; |
|||
private readonly IDocumentAppService _documentAppService; |
|||
|
|||
public SearchModel(IProjectAppService projectAppService, |
|||
IDocumentAppService documentAppService) |
|||
{ |
|||
_projectAppService = projectAppService; |
|||
_documentAppService = documentAppService; |
|||
} |
|||
|
|||
public List<DocumentSearchOutput> SearchOutputs { get; set; } = new List<DocumentSearchOutput>(); |
|||
|
|||
public async Task<IActionResult> OnGetAsync(string keyword) |
|||
{ |
|||
if (!await _documentAppService.FullSearchEnabledAsync()) |
|||
{ |
|||
return RedirectToPage("Index"); |
|||
} |
|||
|
|||
KeyWord = keyword; |
|||
|
|||
Project = await _projectAppService.GetAsync(ProjectName); |
|||
|
|||
var output = await _projectAppService.GetVersionsAsync(Project.ShortName); |
|||
|
|||
var versions = output.Items.ToList(); |
|||
|
|||
if (versions.Any() && string.Equals(Version, DocsAppConsts.Latest, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
Version = versions.First().Name; |
|||
} |
|||
|
|||
SearchOutputs = await _documentAppService.SearchAsync(new DocumentSearchInput |
|||
{ |
|||
ProjectId = Project.Id, |
|||
Context = KeyWord, |
|||
LanguageCode = LanguageCode, |
|||
Version = Version |
|||
}); |
|||
|
|||
return Page(); |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue