Merge branch 'dev' into auto-merge/rel-5-3/1137

pull/12860/head
Engincan VESKE 3 years ago
commit afc215d4a3

2
.gitignore vendored

@ -301,7 +301,7 @@ samples/MicroserviceDemo/microservices/TenantManagementService.Host/Logs/logs.tx
/npm/packs/bootstrap/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package-lock.json
modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Logs/
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package-lock.json

@ -7,7 +7,9 @@
[![MyGet (nightly builds)](https://img.shields.io/myget/abp-nightly/vpre/Volo.Abp.svg?style=flat-square)](https://docs.abp.io/en/abp/latest/Nightly-Builds)
[![NuGet Download](https://img.shields.io/nuget/dt/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core)
ABP Framework is a complete **infrastructure** based on the **ASP.NET Core** to create **modern web applications** and **APIs** by following the software development **best practices** and the **latest technologies**.
[![ABP Discord server](https://img.shields.io/discord/951497912645476422?label=Discord)](https://discord.gg/abp)
ABP Framework is a complete **infrastructure** based on the **ASP.NET Core** to create **modern web applications** and **APIs** by following the software development **best practices** and the **latest technologies**. Check out https://abp.io
## Getting Started
@ -65,7 +67,7 @@ ABP is a modular framework and the Application Modules provide **pre-built appli
- [**Account**](https://docs.abp.io/en/abp/latest/Modules/Account): Provides UI for the account management and allows user to login/register to the application.
- **[Identity](https://docs.abp.io/en/abp/latest/Modules/Identity)**: Manages organization units, roles, users and their permissions, based on the Microsoft Identity library.
- [**IdentityServer**](https://docs.abp.io/en/abp/latest/Modules/IdentityServer): Integrates to IdentityServer4.
- [**OpenIddict**](https://docs.abp.io/en/abp/latest/Modules/OpenIddict): Integrates to OpenIddict.
- [**Tenant Management**](https://docs.abp.io/en/abp/latest/Modules/Tenant-Management): Manages tenants for a [multi-tenant](https://docs.abp.io/en/abp/latest/Multi-Tenancy) (SaaS) application.
See the [Application Modules](https://docs.abp.io/en/abp/latest/Modules/Index) document for all pre-built modules.

@ -393,6 +393,7 @@
"StartTime": "Start Time",
"EndTime": "End Time",
"CreateABookDiscount": "Create a book discount",
"BookDiscountDeletionConfirmationMessage": "Are you sure you want to delete this book discount?"
"BookDiscountDeletionConfirmationMessage": "Are you sure you want to delete this book discount?",
"CustomPaymentFlexSwitchDescription": "With license"
}
}

@ -26,6 +26,8 @@
"Volo.AbpIo.Domain:030009": "User not found!",
"Volo.AbpIo.Domain:030010": "To purchase the trial license, you first need to activate your trial license!",
"Volo.AbpIo.Domain:030011": "You cannot delete a trial license when it is purchased!",
"Volo.AbpIo.Domain:070000": "The organization name can only contain latin letters, numbers, dots and hyphens!",
"Volo.AbpIo.Domain:070001": "The company name can only contain latin letters, numbers, dots, space and hyphens!",
"WantToLearn?": "Want to learn?",
"ReadyToGetStarted?": "Ready to get started?",
"JoinOurCommunity": "Join our community",
@ -125,6 +127,40 @@
"Volo.AbpIo.Domain:080001": "Start Time can not be greater than End Time",
"Enum:BookType:0": "Mastering ABP Framework",
"Enum:PurchasePlatform:0": "Amazon",
"Enum:PurchasePlatform:1": "Packt"
"Enum:PurchasePlatform:1": "Packt",
"Copied": "Copied!",
"CouldNotCopy": "Could not copy!",
"CopyNotSupportByYourBrowser": "This feature does not work in the browser you are using.",
"City": "City",
"ZipCode": "Zip Code",
"Address": "Address",
"Homepage": "Homepage",
"Year": "Year",
"Copyright": "Copyright © <a href=\"{0}\" target=\"_blank\">{1}</a>",
"DomainDrivenDesign": "Domain Driven Design",
"CrossCuttingConcerns": "Cross Cutting Concerns",
"AbpCommunity": "ABP Community",
"Footer_GithubStarCount": "{0} Star on GitHub",
"Footer_NugetDownloadCount": "{0} Downloads on NuGet",
"AbpDescription": "ABP is an open source application framework focused on AspNet Core based web application development. Don't repeat yourself, focus on your own business code.",
"Layout_AbpFramework_MetaTitle": "ABP Framework - Open Source Web Application Framework",
"CommunityTalks_CountdownDays": "Days",
"CommunityTalks_CountdownHours": "Hrs",
"CommunityTalks_CountdownMinutes": "Min",
"CommunityTalks_CountdownSeconds": "Sec",
"SeePreviousEvents": "See Previous Events",
"CookieConsent_Accept": "Accept",
"CookieConsent_Explanation_1": "We use cookies to give you the best experience on our website.",
"CookieConsent_Explanation_2": "If you continue to browse, then you agree to our <a href=\"@Url.Page(\"/Privacy\")\">Privacy policy and cookie policy.</a>.",
"Error_Page_400_Title": "There was a problem serving the requested page.",
"Error_Page_400_Description_1": "Usually this means that an unexpected error happened while processing your request.",
"Error_Page_400_Description_2": "If the problem persists, contact us at <a href=\"mailto:info@abp.io\">info@abp.io</a> and we'll help get you on your way.",
"GoToHomepage": "Go to Homepage",
"Error_Page_404_Title": "Page Not Found!",
"Error_Page_404_Description_1": "This is not the web page you're looking for.",
"Error_Page_500_Title": "Looks like something went wrong!",
"Error_Page_500_Description_1": "We track these errors automatically, but if the problem persists feel free to <br /> contact us. In the meantime, try refreshing.",
"Error_Page_500_Description_2": "Contact with us at <a href=\"mailto:info@abp.io\" target=\"_blank\">info@abp.io</a>.",
"Books": "Books"
}
}

@ -1,5 +1,7 @@
{
"culture": "en",
"texts": {
"AbpTitle": "ABP Framework - Open Source Web Application Framework",
"AbpDescription": "ABP is an open source application framework focused on AspNet Core based web application development. Don't repeat yourself, focus on your own business code."
}
}

@ -10,7 +10,7 @@
"Unlimited": "Unlimited",
"Owners": "Owners",
"Owner": "Owner",
"AddMember": "Add member",
"AddMember": "Add Member",
"AddNewOwner": "Add New Owner",
"AddNewDeveloper": "Add New Developer",
"UserName": "Username",
@ -30,7 +30,7 @@
"LicenseAddDeveloperMessage": "{0} developers added to your license",
"Volo.AbpIo.Commercial:010004": "Can not find the specified user! The user must have already been registered.",
"MyOrganizations": "My organizations",
"ApiKey": "API key",
"ApiKey": "API Key",
"UserNameNotFound": "There is no user with the username {0}",
"SuccessfullyAddedToNewsletter": "Thank you for subscribing to our newsletter!",
"MyProfile": "My profile",
@ -128,6 +128,8 @@
"TellUsWhatYouNeed": "Tell us what you need.",
"YourMessage": "Your Message",
"YourFullName": "Your full name",
"FirstNameField": "First Name",
"LastNameField": "Last Name",
"EmailField": "E-mail Address",
"YourEmailAddress": "Your e-mail address",
"HowMayWeHelpYou": "How may we help you?",
@ -158,6 +160,8 @@
"SearchQuestionPlaceholder": "Search in frequently asked questions",
"WhatIsTheABPCommercial": "What is ABP Commercial?",
"WhatAreDifferencesThanAbpFramework": "What are the differences between the open source ABP Framework and ABP Commercial?",
"AbpCommercialMetaTitle": "ABP Commercial - Complete Web Development Platform : {0} | ABP Commercial",
"AbpCommercialMetaDescription": "ABP Commercial is a set of pre-built application modules, rapid development tooling, UI themes and services built on top of the open-source ABP framework.",
"ABPCommercialExplanation": "ABP Commercial is a set of premium modules, tools, themes and services that are built on top of the open source <a target=\"_blank\" href=\"{0}\">ABP framework</a>. ABP Commercial is being developed and supported by the same team behind the ABP framework.",
"WhatAreDifferencesThanABPFrameworkExplanation": "<p> <a target=\"_blank\" href=\"{0}\">ABP framework</a> is a modular, themeable, micro-service compatible application development framework for ASP.NET Core. It provides a complete architecture and a strong infrastructure to let you focus on your own business code rather than repeating yourself for every new project. It is based on the best practices of software development and popular tools you already know. </p> <p> ABP framework is completely free, open source and community-driven. It also provides a free theme and some pre-built modules (e.g. identity management and tenant management).</p>",
"VisitTheFrameworkVSCommercialDocument": "Visit the following link for more information <a href=\"{0}\" target=\"_blank\"> {1} </a>",
@ -199,6 +203,7 @@
"WhatHappensWhenLicenseEndsExplanation5": "You can not use the ABP Suite.",
"WhatHappensWhenLicenseEndsExplanation6": "You can not get the <a href=\"{0}\">premium support</a> anymore.",
"WhatHappensWhenLicenseEndsExplanation7": "You can extend (renew) your license if you want to continue getting these benefits. If you extend your license within <strong>1 month</strong> after your license expires, the following discounts will be applied: Team License {0}% discount, Business License {1}% discount, Enterprise License {2}% discount.",
"WhatHappensWhenLicenseEndsExplanation8": "The ABP projects you generated are not stored on our servers. Therefore, it is your responsibility to keep the source code you download. When your license expires, there's no way to get your generated ABP project source code.",
"WhenShouldIRenewMyLicense": "When should I renew my license?",
"WhenShouldIRenewMyLicenseExplanation": "If you renew your license within <strong>1 month</strong> after your license expires, the following discounts will be applied: Team License {0}% discount, Business License {1}% discount, Enterprise License {2}% discount. However, if you renew your license after <strong>1 month</strong> since the expiry date of your license, the renewal price will be the same as the license purchase price and there will be no discount on your renewal.",
"TrialPlan": "Do you have a trial plan?",
@ -378,13 +383,11 @@
"TaxNoValidationMessage": "TAX/VAT No is too long!",
"NotesValidationMessage": "Notes field is too long!",
"CheckYourBillingInfo": "You can create your invoice only once! Check your billing information before creating your invoice.",
"Volo.AbpIo.Commercial:030000": "You already used your trial period.",
"Volo.AbpIo.Commercial:030001": "This organization name already exists.",
"StartYourFreeTrial": "Start your free trial",
"TrialLicenseModelInvalidErrorMessage": "One of the following fields is invalid: Country Name, Company Size, Industry or Purpose Of Usage.",
"Trial": "Trial",
"Purchased": "Purchased",
"PurchaseLicense": "Purchase Now",
"PurchaseNow": "Purchase Now",
"PurchaseTrialLicenseMessage": "Your license expiration date is {0}. <br> If you want to continue using the projects you created during your free trial period, you need to change the license keys in your <code>appsettings.secrets.json</code> files. Here is your license key:",
"TrialLicenseExpireMessage": "You are using the trial license and your trial license will expire on {0}.",
"TryForFree": "Try For Free",
@ -487,25 +490,239 @@
"BackOfficeApplicationExplanation": "The actual web application of your system, with multiple UI framework options. You can create any kind of business application.",
"LandingWebsite": "Landing Website",
"LandingWebsiteExplanation": "A generic landing/public website that can be used for several purposes, like introducing your company, selling your products, etc.",
"ABPFrameworkEBook": "E-Book: Mastering ABP Framework",
"ABPFrameworkEBook": "Mastering ABP Framework e-book",
"MasteringAbpFrameworkEBookDescription": "Included within your ABP Commercial license",
"Volo.AbpIo.Domain:070000": "The organization name can only contain latin letters, numbers, dots and hyphens!",
"Volo.AbpIo.Domain:070001": "The company name can only contain latin letters, numbers, dots, space and hyphens!",
"FullName": "Full Name",
"LicenseTypeNotCorrect": "The license type is not correct!",
"Trainings": "Trainings",
"ChoseTrainingPlaceholder": "Chose the training...",
"ChooseTrainingPlaceholder": "Choose the training...",
"DoYouNeedTrainings": "Do you need one of these trainings?",
"DoYouNeedTraining": "Do you need {0} training?",
"GetInTouchUs": "Get in touch with us",
"ForMoreInformationClickHere": "For more information, click <a href='{0}'>here.</a>",
"IsGetOnboardingTraining": "Would you like to get onboarding & web application development training?",
"OnboardingWebApplicationDevelopmentTrainingMessage": "To schedule your training calendar, please contact {0} after creating the organization",
"CustomPurchaseMessage": "For the next step, click {0} to contact us.",
"Note": "Note",
"AdditionalNote": "Additional Note",
"OnboardingTrainingFaqTitle": "Do you have ABP onboarding training?",
"OnboardingTrainingFaqExplanation": "Yes, we have ABP Training Services to help you get your ABP project started fast. You will learn about ABP from an ABP core team member and you will get the skills to begin your ABP project. In the onboarding training, we will explain how to set up your development environment, install the required tools, create a fully functional CRUD page. The training will be live and the Zoom application will be used, and we are open to using other online meeting platforms. The language of the training will be English. You can also ask your questions about ABP during the sessions. A convenient time and date will be planned for both parties. To get more information, contact us at <a href=\"mailto:info@abp.io\">info@abp.io</a>.",
"AddBasket": "Add to Basket",
"SendTrainingRequest": "Send Training Request"
"SendTrainingRequest": "Send Training Request",
"OnlyEnglishVersionOfThisDocumentIsTheRecentAndValid": "* The English version of this document is the most up-to-date and the English version will prevail in any dispute.",
"Pricing_Page_Title": "Plans & Pricing",
"Pricing_Page_Description": "Choose the features and functionality your business needs today. Buy an ABP Commercial license and create unlimited projects.",
"Pricing_Page_HurryUp": "Hurry Up!",
"Pricing_Page_BuyLicense": "Buy a license at <strong>2021 prices</strong> until January 16!",
"Pricing_Page_ValidForExistingCustomers": "Also valid for existing customers and license renewals.",
"Pricing_Page_Hint1": "The license price includes a certain number of developer seats. If you have more developers, you can always purchase additional seats.",
"Pricing_Page_Hint2": "You can purchase more developer licenses now or in the future. Licenses are seat based, so you can transfer a seat from a developer to another.",
"Pricing_Page_Hint3": "You can develop unlimited count of different products with your license.",
"Pricing_Page_Hint4": "ABP Suite is a tool to assist your development to improve your productivity. It supports generating CRUD pages and creating new projects.",
"Pricing_Page_Hint5": "You can use all the pre-built modules in your applications.",
"Pricing_Page_Hint6": "You can use all the pre-built themes in your applications.",
"Pricing_Page_Hint7": "A startup template is a Visual Studio solution to make you jump-start to your project. All fundamental modules are added and pre-configured for you.",
"Pricing_Page_Hint8": "Mastering ABP Framework e-book explains how to implement .NET solutions with best practices. It is sold on Amazon.com and you can download the book for free within your license.",
"Pricing_Page_Hint9": "You can download the source-code of any module. You may want to add the source code to your solution to make radical changes or just keep it for yourself for security reasons.",
"Pricing_Page_Hint10": "Licenses are for lifetime. That means you can continue to develop your application forever. Accessing to the latest version and getting support are granted within the license period (1 year unless you renew it).",
"Pricing_Page_Hint11": "No restrictions on deployment! You can deploy to as many servers as you want, including the cloud services or on-premises.",
"Pricing_Page_Hint12": "You can update the modules, themes and tools to the latest version within your active license period. After your license expires, you need to renew it, to continue to get updates for bug fixes, new features and enhancements.",
"Pricing_Page_Hint13": "You can get the premium support for one year (you can renew your license to extend it).",
"Pricing_Page_Hint14": "Team and Business licenses have incident/question count limit. If you buy additional developer licenses, your incident limit increases by {0} (for the Team License) or {1} (for the Business License) per developer.",
"Pricing_Page_Hint15": "Only Enterprise License includes private support. You can send e-mail directly to the ABP Team or ask questions on support.abp.io with private ticket option. The private tickets are not visible to the public.",
"Pricing_Page_Hint16": "You can download the source-code of all ABP themes. You may want to add the source code to your solution to make radical changes or just keep it for yourself for security reasons.",
"Pricing_Page_Testimonial_1": "ABP Commercial allowed SC Ventures to deliver a bank-grade multi-tenant silo-database SaaS platform in 9 months to support the accounts receivable / accounts payable supply chain financing of significant value invoices from multiple integrated anchors. The modularity of ABP made it possible for the team to deliver in record time, pass all VAPT, and deploy the containerized micro-services stack via full CI/CD and pipelines into production.",
"Pricing_Page_Testimonial_2": "We are seeing the value of using ABP Commercial to reduce the overhead of custom development projects. And the team is able to unify the code pattern in different project streams. We see more potential in the framework for us to build new features faster than before. We trust we will be constantly seeing the value of leveraging ABP Commercial.",
"Pricing_Page_Testimonial_3": "We love ABP. We don't have to write everything from scratch. We start from out-of-the-box features and just focus on what we really need to write. Also, ABP is well-architected and the code is high quality with fewer bugs. If we would have to write everything we needed on our own, we might have to spend years. Once more things we like is that the new version, or issue fixing, or improvement come out very soon every other week. We don't wait too long.",
"Pricing_Page_Testimonial_4": "ABP Commercial is a fantastic product would recommend. Commercial products to market for our customers in a single configurable platform. The jump start that the framework and tooling provide any team is worth every cent. ABP Commercial was the best fit for our needs.",
"AbpBookDownloadArea_ClaimYourEBook": "Claim your <span class='gradient-framework'>Mastering ABP Framework</span> E-Book",
"AddMemberModal_Warning_1": "If the <strong>username</strong> you are trying to add doesn't exist in the system, please ask your team member to register on <a href='{0}/Account/Register'>{0}</a> and share the username of his/her account with you.",
"MyOrganizations_Detail_WelcomeMessage": "Welcome to your organization, {0}",
"MyOrganizations_Detail_OrganizationManagement": "Organization <span class=\"gradient-commercial\">Management</span>",
"OrganizationDisplayName": "Organization Display Name",
"MyOrganizations_Detail_EditDisplayName": "Edit Display Name",
"MyOrganizations_Detail_UpgradeYourLicense": "Upgrade your license",
"MyOrganizations_Detail_LicenseStartAndExpiryDate": "License Start Date - Expiry Date",
"MyOrganizations_Detail_OwnerRightInfo": "You are using {0} of your {1} owners rights.",
"MyOrganizations_Detail_CopyApiKey": "Copy the Key",
"MyOrganizations_Detail_ApiKeyDescription": "The API Key is the token of PRO packages hosted on <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">{1}.</a>",
"MyOrganizations_Detail_YourPrivateNugetSource": "Your private NuGet source is <a href=\"{0}\" arget=\"_blank\" class=\"text-primary\" rel=\"noopener\">{0}</a>",
"MyOrganizations_Detail_PrivateNugetSourceWarning": "This is automatically added as a feed to your NuGet.Config in your ABP solution. Do not share your private key with unauthorized users!",
"MyOrganizations_Detail_DeveloperSeatInfo": "You are using {0} of your {1} developer seats.",
"NeedMoreSeatsForYourTeam": "Need more seats for your team?",
"MyOrganizations_Detail_PricePerYear": "{0} / per year",
"MyOrganizations_Detail_PurchaseDeveloperSeats": "Purchase Developer Seats",
"Invoices": "Invoices",
"RequestInvoice": "Request Invoice",
"OrderNumber": "Order Number",
"Date": "Date",
"Products": "Products",
"TotalPrice": "Total Price",
"ThereIsNoInvoice": "There is no invoice",
"MyOrganizations_Detail_PaymentProviderInfo": "If you have purchased your license through <i>{0}</i> gateway, it sends the PDF invoice to your email address, see <a href=\"{1}\" target=\"_blank\">{0} invoicing.</a>",
"MyOrganizations_Detail_PayUInfo": "If you have purchased through the <i>PayU</i> gateway, click the \"Request Invoice\" button and fill in the billing information.",
"MyOrganizations_Detail_ConclusionInfo": "Your invoice request will be concluded within {0} business days.",
"ExtendYourLicense": "Extend your {0} license",
"Continue": "Continue",
"PurchaseLicense": "Purchase {0} license",
"DownloadInvoiceModal_DownloadInvoice": "Download Invoice",
"DownloadInvoiceModal_SaveInformationOnlyOnce": "You can save your billing information only once.",
"InvoiceModal_EnterCompanyName": "Enter your legal company name...",
"InvoiceModal_EnterCompanyAddress": "Enter your legal company address...",
"InvoiceModal_EnterTaxNumber": "Enter your TAX/VAT number if available...",
"RequestInvoiceModal_EnterNotes": "Enter your extra message about your invoice...",
"PrePayment_PayWithIyzico": "You will pay with Iyzico",
"ContinueToCheckout": "Continue to Checkout",
"PrePayment_IyzicoRedirectionInfo": "You will be redirected to Iyzico Payment Gateway to complete your purchase securely.",
"PrePayment_IyzicoAcceptVisaAndMasterCard": "Iyzico accepts Visa and MasterCard.",
"Purchase": "Purchase",
"AcceptTermsAndConditions": "I have read, understand and accept the <a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">privacy policy</a>, <a href=\"{1}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">terms & conditions</a> and <a href=\"{2}\" target=\"_blank\" class=\"text-primary\"> EULA.</a>",
"AcceptTermsAndConditionsWarningMessage": "Please accept the privacy policy and terms & conditions",
"SelectGatewayToContinue": "Please select a Gateway to continue!",
"GatewaySelection_SelectGateway": "Select a Payment Gateway",
"GatewaySelection_RedirectionMessage": "Next, you will be redirected to the selected payment gateway's website for the transaction.",
"PaymentSucceed_PaymentSuccessMessage": "Payment Successful",
"PaymentSucceed_ThanksForPurchase": "Thank you for your purchase!",
"PaymentSucceed_CreateYourOrganization": "Create your organization",
"PaymentSucceed_AddMeAsDeveloper": "I'm a developer too, add me as a a developer to my organization.",
"PaymentSucceed_CreateOrganization": "Create Organization",
"PaymentSucceed_OrganizationDescription": "An organization consists of developers and owners. The developers are users who write code on ABP project and will benefit from the <a href=\"{0}\" target=\"_blank\">{1}</a> website. The owners are users who allocate developer seats and manage licensing.",
"PaymentSucceed_ViewOrganization": "Click here to view organization",
"Purchase_TotalAnnualPrice": "TOTAL <small class=\"opacity-50\">(annual fee)</small>",
"Purchase_TrainingPrice": "Training Price",
"Purchase_OnboardingTraining": "ABP Onboarding & Web Application Development Live Training",
"TotalDeveloperPrice": "Total Developer Price",
"Purchase_PricePerDeveloper": "<span>{0} {1}</span> per developer",
"Purchase_IncludedDeveloperInfo": "{0} {1} included.",
"Purchase_LicenseExtraDeveloperPurchaseMessage": "The <span class=\"fw-6\">{0} license</span> contains {1} developer(s). You can add additional developers now or later.",
"StartupTemplates_Page_Title": "The Startup Templates",
"StartupTemplates_Page_Description": "ABP Commercial allows you to build solutions with any level of complexity. It provides two main pre-built startup solutions. You can select the one close to your requirements and build your own custom solution on top of it.",
"MicroserviceStartupSolutionForDotnet": "Microservice Startup Solution for .NET",
"MonolithSolutionForDotnet": "Monolith (modular) Solution for .NET",
"TrainingDetailsHeaderInfo_TrainingHour": "{0} hour(s)",
"Trainings_Content": "Content of Training",
"Trial_Page_StartYourFreeTrial": "Start Your <span class=\"gradient-commercial\">Free Trial</span>",
"Contact_Page_Title": "Contact with ABP Development Team",
"Contact_Page_Description": "Contact with ABP Development team, if you need any help or share your thoughts and opinions! ABP Support Team is ready to help.",
"Demo_Page_Title": "Create a Demo",
"Demo_Page_Description": "Create a free demo to see a sample application created using the ABP Commercial startup template. Don't repeat yourself for common application requirements.",
"Discounted_Page_Title": "Discounted pricing",
"Discounted_Page_Description": "Choose the features and functionality your business needs today. Buy an ABP Commercial license and create unlimited projects",
"Faq_Page_Title": "Frequently Asked Questions (FAQ)",
"Faq_Page_Description": "Do you have any questions? Search frequently asked questions or ask us a question using the contact form.",
"Faq_Page_SwiftCode": "SWIFT Code",
"Faq_Page_BankName": "Bank Name",
"Faq_Page_AccountName": "Account Name",
"Faq_Page_AccountNumber": "Account Number",
"Faq_Page_Currency": "Currency",
"Faq_Page_VatNumber": "VAT number",
"Faq_Page_OtherCurrenciesInfo": "For other currencies, see <a href='{0}'>all accounts</a>",
"ModuleDetail_Page_Title": "Module Detail - {0}",
"ProjectCreatedSuccess_Page_Title": "Your project created",
"ProjectCreatedSuccess_Page_Description": "Your ABP project created successfully!",
"Suite_Page_Title": "ABP Suite - Create CRUD Pages",
"Suite_Page_Description": "ABP Commercial provides rapid application development tooling to increase developer productivity. ABP Suite allows you to create CRUD pages easily.",
"Themes_Page_Title": "Modern and Functional UI Themes",
"Themes_Page_Description": "ABP Commercial provides multiple professional, modern UI themes. Create a free demo to have a quick view of what the UI looks like.",
"Tools_Page_Title": "Rapid Application Development Tools",
"Tools_Page_Description": "ABP Commercial provides rapid application development tooling to increase developer productivity. ABP Suite allows you to create CRUD pages easily.",
"DeveloperPrice": "Developer Price",
"AdditionalDeveloperPaymentInfoSection_AdditionalDevelopers": "{0} <small>developers</small>",
"LicenseRemainingDays": "for <span> {0} </span> days",
"ExtendPaymentInfoSection_Description": "By extending/renewing your license, you will continue to get <a href=\"{0}\" target=\"_blank\">premium support</a>. You will also be able to get major updates for modules and themes. You will be able to continue creating new projects. And you will still be able to use <a href=\"{1}\" target=\"_blank\">ABP Suite</a> which speeds up your development.",
"LicenseRenewalPrice": "License renewal price",
"LicensePrice": "License Price",
"TrialLicensePaymentInfoSection_Description": "<strong>Purchase license:</strong> By purchasing a license, you will continue to get <a href=\"{0}\" target=\"_blank\" rel=\"noopener\">premium support</a>. You will also be able to get major updates for modules and themes. You will be able to continue creating new projects. And you will still be able to use <a href=\"{1}\" target=\"_blank\" rel=\"noopener\">ABP Suite</a> which speeds up your development.<br>See the <a href=\"{2}\" target=\"_blank\" rel=\"noopener\">license comparison table</a> to check the differences between the license types.",
"SelectTargetLicense": "Select Target License",
"UpgradePaymentInfoSection_ExtendMyLicenseForOneYear": "Yes, extend my license expiration date for 1 year.",
"UpgradePaymentInfoSection_WantToExtendLicense": "Do you want to extend your license for {0} more year(s)?",
"UpgradePaymentInfoSection_UpgradingWillNotExtendLicense": "Upgrading will not extend your license expiration date!",
"UpgradePaymentInfoSection_LicenseUpgradeDescription": "By upgrading your license, you will promote to a higher license type which will allow you to get additional benefits. See the <a href=\"/Pricing\" target=\"_blank\">license comparison table</a> to check the differences between the license types.",
"Landing_Page_CustomerStories": "Customer Stories",
"Landing_Page_OurGreatCustomers": "Our Great Customers",
"Landing_Page_WebApplicationFramework": "Web Application Framework",
"Landing_Page_WebDevelopmentPlatform": "Web Development Platform",
"Landing_Page_CompleteWebDevelopmentPlatform": "Complete Web Development Platform",
"Landing_Page_TryFreeDemo": "Try Free Demo",
"Landing_Page_StartingPointForWebApplications": "The starting point for ASP.NET Core based web applications! It is based on the ABP Framework for best web development.",
"Landing_Page_AbpProvidesSoftwareInfrastructure": "ABP Framework provides a software infrastructure to develop excellent web applications with best practices.",
"Landing_Page_MicroserviceCompatibleArchitecture": "Microservice Compatible Architecture",
"Landing_Page_PreBuiltApplicationModulesAndThemes": "Pre-Built Application Modules & Themes",
"Landing_Page_MultiTenantArchitecture": "Multi-Tenant Architecture",
"Landing_Page_MultiTenancyDescription": "SaaS applications made easy! Integrated multi-tenancy from database to UI.",
"Landing_Page_DDDIntroduction": "Designed and developed based on DDD patterns and principles. Provides a layered model for your application.",
"Landing_Page_CrossCuttingConcernsInfo": "Complete infrastructure for authorization, validation, exception handling, caching, audit logging, transaction management and more.",
"Landing_Page_PreBuiltApplicationModules": "Pre-Built Application Modules which include most common web application requirements.",
"Landing_Page_ChatModule": "Chat",
"Landing_Page_DocsModule": "Docs",
"Landing_Page_FileManagementModule": "Docs",
"Landing_Page_CustomerStory_1": "ABP Commercial allowed SC Ventures to deliver a bank-grade multi-tenant silo-database SaaS platform in 9 months to support the accounts receivable / accounts payable supply chain financing of significant value invoices from multiple integrated anchors. The modularity of ABP made it possible for the team to deliver in record time, pass all VAPT, and deploy the containerized micro-services stack via full CI/CD and pipelines into production.",
"Landing_Page_CustomerStory_2": "We are seeing the value of using ABP Commercial to reduce the overhead of custom development projects. And the team is able to unify the code pattern in different project streams. We see more potential in the framework for us to build new features faster than before. We trust we will be constantly seeing the value of leveraging ABP Commercial.",
"Landing_Page_CustomerStory_3": "We love ABP. We don't have to write everything from scratch. We start from out-of-the-box features and just focus on what we really need to write. Also, ABP is well-architected and the code is high quality with fewer bugs. If we would have to write everything we needed on our own, we might have to spend years. Once more things we like is that the new version, or issue fixing, or improvement come out very soon\n every other week. We don't wait too long.",
"Landing_Page_CustomerStory_4": "ABP Commercial is a fantastic product would recommend. Commercial products to market for our customers in a single configurable platform. The jump start that the framework and tooling provide any team is worth every cent. ABP Commercial was the best fit for our needs.",
"Landing_Page_AdditionalServices": "Custom or volume license, onboarding, live training & support, custom project development, porting existing projects and more...",
"Landing_Page_IncludedDeveloperLicenses": "Included <strong>{0}</strong> developer licenses",
"Landing_Page_SeeOnDemo": "See on Demo",
"Landing_Page_LeptonThemes": "LeptonThemes",
"Landing_Page_AccountModuleDescription_1": "This module implements the authentication system for an application;",
"Landing_Page_AccountModuleDescription_2": "Provides a <strong>login</strong> page with the username and password",
"Landing_Page_AccountModuleDescription_3": "Provides a <strong>register</strong> page to create a new account.",
"Landing_Page_AccountModuleDescription_4": "Provides a <strong>forgot password</strong> page to send a <strong>password reset</strong> link as an e-mail.",
"Landing_Page_AccountModuleDescription_5": "Provides <strong>email confirmation</strong> functionality with UI.",
"Landing_Page_AccountModuleDescription_6": "Implements <strong>two factor</strong> authentication (SMS and e-mail).",
"Landing_Page_AccountModuleDescription_7": "Implements <strong>user lockout</strong> (locks the account for the set amount of time when a certain number of failed logons occur due to invalid credentials within a certain interval of time).",
"Landing_Page_AccountModuleDescription_8": "Implements <strong>Identity Server</strong> authentication server UI and functionality.",
"Landing_Page_AccountModuleDescription_9": "Allows to <strong>switch between tenants</strong> in a multi-tenant environment.",
"Landing_Page_AccountModuleDescription_10": "Allows to change the <strong>UI language</strong> of the application.",
"Landing_Page_AuditLoggingModuleDescription_1": "This module provides the audit log reporting UI for the auditing infrastructure. Allows to search, filter and show audit log entries and entity change logs.",
"Landing_Page_AuditLoggingModuleDescription_2": "An audit log entry consists of critical data about each client request:",
"Landing_Page_AuditLoggingModuleDescription_3": "URL, Browser, IP address, client name",
"Landing_Page_AuditLoggingModuleDescription_4": "The user",
"Landing_Page_AuditLoggingModuleDescription_5": "HTTP method, HTTP return status code",
"Landing_Page_AuditLoggingModuleDescription_6": "Success/failure, exception details if available",
"Landing_Page_AuditLoggingModuleDescription_7": "Request execution duration",
"Landing_Page_AuditLoggingModuleDescription_8": "The entities have been created, deleted or updated in this request (with changed properties).",
"Landing_Page_BloggingModuleDescription_1": "This module adds a simple blog to your ABP application;",
"Landing_Page_BloggingModuleDescription_2": "Allows to create multiple blogs in a single application.",
"Landing_Page_BloggingModuleDescription_3": "Supports the Markdown format.",
"Landing_Page_BloggingModuleDescription_4": "Allows to write comment for a post.",
"Landing_Page_BloggingModuleDescription_5": "Allows to assign tags to the blog posts.",
"Landing_Page_BloggingModuleDescription_6": "See the <a href=\"blog.abp.io\">blog.abp.io</a> website as a live example of the blogging module.",
"Landing_Page_ChatModuleDescription_1": "This module is used for real-time messaging between users in application.",
"Landing_Page_ChatModuleDescription_2": "Real-time messaging on chat page.",
"Landing_Page_ChatModuleDescription_3": "Search users in application for new conversations.",
"Landing_Page_ChatModuleDescription_4": "Contact list for recent conversations.",
"Landing_Page_ChatModuleDescription_5": "New message notifications when user is looking at another page.",
"Landing_Page_ChatModuleDescription_6": "Total unread message count badge on menu icon.",
"Landing_Page_ChatModuleDescription_7": "Unread message count for each conversation.",
"Landing_Page_ChatModuleDescription_8": "Lazy loaded conversations.",
"Landing_Page_DocsModuleDescription_1": "This module is used to create technical documentation web sites;",
"Landing_Page_DocsModuleDescription_2": "Built-in <strong>GitHub integration</strong>: Directly write and manage documents on GitHub.",
"Landing_Page_DocsModuleDescription_3": "<strong>Versioning</strong> support directly integrated to GitHub releases.",
"Landing_Page_DocsModuleDescription_4": "Supports <strong>multi-language</strong> (with fallback support to the default language).",
"Landing_Page_DocsModuleDescription_5": "Supports the <strong>Markdown</strong> and HTML formats.",
"Landing_Page_DocsModuleDescription_6": "Provides a <strong>navigation</strong> and an <strong>outline</strong> section.",
"Landing_Page_DocsModuleDescription_7": "Allows to host <strong>multiple projects</strong> documentation in a single application.",
"Landing_Page_DocsModuleDescription_8": "Links to the file on GitHub, so anyone can easily contribute by clicking to the <strong>Edit link</strong>.",
"Landing_Page_DocsModuleDescription_9": "In addition to the GitHub source, allows to simply use a folder as the documentation source.",
"Landing_Page_FileManagementModuleDescription_1": "Upload, download and organize files in a hierarchical folder structure.",
"Landing_Page_FileManagementModuleDescription_2": "This module is used to upload, download and organize files in a hierarchical folder structure. It is also compatible to multi-tenancy and you can determine total size limit for your tenants.",
"Landing_Page_FileManagementModuleDescription_3": "This module is based on the <a href=\"https://docs.abp.io/en/abp/latest/Blob-Storing\">BLOB Storing</a> system, so it can use different storage providers to store the file contents.",
"Landing_Page_IdentityModuleDescription_1": "This module implements the User and Role system of an application;",
"Landing_Page_IdentityModuleDescription_2": "Built on the <a href=\"https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity\">Microsoft's ASP.NET Core Identity</a> library.",
"Landing_Page_IdentityModuleDescription_3": "Manage <strong>roles</strong> and <strong>users</strong> in the system. A user is allowed to have <strong>multiple roles</strong>.",
"Landing_Page_IdentityModuleDescription_4": "Set <strong>permissions</strong> in role and user levels.",
"Landing_Page_IdentityModuleDescription_5": "Enable/disable <strong>two factor authentication</strong> and user <strong>lockout</strong> per user.",
"Landing_Page_IdentityModuleDescription_6": "Manage basic <strong>user profile</strong> and <strong>password</strong>.",
"Landing_Page_IdentityModuleDescription_7": "Manage <strong>claim types</strong> in the system, set claims to roles and users.",
"Landing_Page_IdentityModuleDescription_8": "Setting page to manage <strong>password complexity</strong>, user sign-in, account and lockout.",
"Landing_Page_IdentityModuleDescription_9": "Supports <strong>LDAP</strong> authentication.",
"Landing_Page_IdentityModuleDescription_10": "Provides <strong>email &amp; phone number</strong> verification.",
"Landing_Page_IdentityModuleDescription_11": "Supports social login integrations (Twitter, Facebook, GitHub etc...).",
"Landing_Page_IdentityModuleDescription_12": "Manage <strong>organization units</strong> in the system.",
"Landing_Page_PaymentModuleDescription_1": "Provides integration for different payment gateways.",
"Landing_Page_PaymentModuleDescription_2": "This module provides integration for payment gateways, so you can easily get payment from your customers.",
"Landing_Page_PaymentModuleDescription_3": "This module supports the following payment gateways",
"Welcome_Page_UseSameCredentialForCommercialWebsites": "Use the same credentials for both <a href=\"https://commercial.abp.io\" class=\"text-primary\">commercial.abp.io</a> and <a href=\"https://support.abp.io\" class=\"text-primary\">support.abp.io</a>."
}
}

@ -131,7 +131,7 @@
"YourFullName": "你的全名",
"EmailField": "E-mail地址",
"YourEmailAddress": "你的e-mail地址",
"HowMayWeHelpYou": "我们如何帮助你",
"HowMayWeHelpYou": "需要获得购买帮助?(提供中文服务)",
"SendMessage": "发送消息",
"Success": "成功",
"WeWillReplyYou": "你的消息已经发送! 我们会在短时间内给你答复.",

@ -102,6 +102,7 @@
"PostRequestMessageBody": "Here is the list of the requested posts by the Community. Do you want to write a requested post? Please click on the request and join the discussion.",
"Language": "Language",
"CreatePostLanguageInfo": "Language of the post",
"SeeMore": "See More"
"SeeMore": "See More",
"MemberNotPublishedPostYet": "This member hasn't published any posts yet."
}
}

@ -165,6 +165,25 @@
"SortBy": "Sort by",
"NoPublishedEventsYet": "No published events yet.",
"SubscribeYoutubeChannel": "Subscribe to the Youtube Channel",
"Enum:EventType:0": "Talks"
"Enum:EventType:0": "Talks",
"MemberNotPublishedPostYet": "This member hasn't published any posts yet.",
"TimeAgo": "{0} ago",
"Discord_Page_JoinCommunityMessage": "Join ABP Discord Community",
"Discord_Page_Announce": "We are happy to announce ABP Community Discord Server!",
"Discord_Page_Description_1": "ABP Community has been growing since day one. We wanted to take it to the next step by creating an official ABP Discord server so the ABP Community can interact with each other using the wonders of instant messaging.",
"Discord_Page_Description_2": "ABP Community Discord Server is the place where you can showcase your creations using ABP Framework, share the tips that worked for you, catch up with the latest news and announcements about ABP Framework, just chat with community members to exchange ideas, and have fun!",
"Discord_Page_Description_3": "This ABP Community Discord Server is the official one with the ABP Core Team is present on the server to monitor.",
"Discord_Page_JoinToServer": "Join ABP Discord Server",
"Events_Page_MetaTitle": "ABP Community Events",
"Events_Page_MetaDescription": "The live shows, hosted by the ABP Team, are casual sessions full of community content, demos, Q&A, and discussions around what's happening in ABP.",
"Events_Page_Title": "ABP<span class=\"gradient-community\"> Community</span> Talks",
"Events_Page_WritingFromUser": "Read writing from {0} on ABP Community.",
"Post_Create_Page_MetaTitle": "New Post",
"Post_Create_Page_MetaDescription": "Create your post for sharing your experiences about ABP framework and contributing the ABP Community.",
"Post_Create_Page_CreateNewPost": "Create New Post",
"Post_Index_Page_MetaDescription": "ABP Community's purpose is to create a contribution environment for developers who use the ABP framework.",
"Layout_Title": "{0} | ABP Community",
"Layout_MetaDescription": "ABP Community is an environment where people can share posts about ABP framework and follows the projects.",
"Index_Page_CommunityIntroduction": "This is a hub for ABP Framework, .NET and software development. You can read the articles, watch the video tutorials, get informed about ABPs development progress and ABP-related events, help other developers and share your expertise with the ABP community."
}
}

@ -142,6 +142,7 @@
"MinimumSearchContent": "En az 3 karakter girmelisiniz!",
"Volo.AbpIo.Domain:060001": "Kaynak URL (\"{PostUrl}\") Github URL'si değil",
"Volo.AbpIo.Domain:060002": "Makale İçeriği Github(\"{PostUrl}\") kaynağında mevcut değil.",
"Volo.AbpIo.Domain:060003": "Makale içeriği bulunamadı!"
"Volo.AbpIo.Domain:060003": "Makale içeriği bulunamadı!",
"MemberNotPublishedPostYet": "Bu üye henüz bir gönderi yayınlamadı."
}
}

@ -1,5 +1,6 @@
{
"culture": "zh-Hans",
"texts": {
"FAQ": "常问问题"
}
}

@ -1,5 +1,6 @@
{
"culture": "zh-Hant",
"texts": {
"FAQ": "常问问题"
}
}

@ -174,6 +174,7 @@
"CreateProjectWizard": "This wizard creates a new project from the startup template which is properly configured to jump start your project.",
"TieredOption": "Creates a tiered solution where Web and Http API layers are physically separated. If not checked, creates a layered solution which is less complex and suitable for most scenarios.",
"SeparateIdentityServerOption": "Separates the server side into two applications: The first one is for the identity server and the second one is for your server side HTTP API.",
"ProgressiveWebApplicationOption": "Specifies the project as Progressive Web Application",
"UseslatestPreVersion": "Uses the latest pre-release version",
"ReadTheDocumentation": "Read The Documentation",
"Documentation": "Documentation",
@ -296,6 +297,7 @@
"EnterYouEmailToGetNews": "Enter your email to get the latest news about the ABP Framework",
"Tiered": "Tiered",
"SeparateIdentityServer": "Separate Identity Server",
"ProgressiveWebApplication": "Progressive Web Application",
"Preview": "Preview",
"CreateANewSolution": "Create a new solution",
"ABPFrameworkFeatures": "ABP Framework <span class=\"gradient-framework\">Features</span>",
@ -323,6 +325,53 @@
"CreateYourProjectNow": "Create Your Project Now",
"OrderOn": "Order on {0}",
"DownloadFreeDDDBook": "Download Free DDD Book",
"WhatIsABPFramework": "What is the ABP Framework?"
"WhatIsABPFramework": "What is the ABP Framework?",
"TenantDatabase": "Tenant {0} Database",
"SharedDatabase": "Shared Database",
"ConnectionResolver": "Connection Resolver",
"TenantBasedDataFilter": "Tenant Based Data Filter",
"ApplicationCode": "Application Code",
"TenantResolution": "Tenant Resolution",
"TenantUser": "Tenant {0} User",
"CardTitle": "Card title",
"View": "View",
"Model": "Model",
"Email": "Email",
"Password": "Password",
"Address": "Address",
"Gender": "Gender",
"Male": "Male",
"Female": "Female",
"Submit": "Submit",
"Unspecified": "Unspecified",
"StaticFileMiddleware": "Static File Middleware",
"RazorViewEngine": "Razor View Engine",
"PhysicalFiles": "Physical Files (wwwroot)",
"EmbeddedFiles": "Embedded Files (DLL)",
"DynamicFiles": "Dynamic Files (Memory)",
"BuildSolutionsWithAbp": "Build maintainable .NET solutions by following software development best practices using ABP.",
"BuyOnAmazon": "Buy on Amazon",
"BuyOnPackt": "Buy on Packt",
"Discounted": "Discounted",
"MasteringAbpFramework_Book_KeyFeatures": "Key Features",
"MasteringAbpFramework_Book_Key_Features_Description_1": "Build robust, maintainable, modular, and scalable software solutions using ABP Framework.",
"MasteringAbpFramework_Book_Key_Features_Description_2": "Learn how to implement SOLID principles and domain-driven design in your web applications.",
"MasteringAbpFramework_Book_Key_Features_Description_3": "Discover how ABP Framework speeds up your development cycle by automating repetitive tasks.",
"MasteringAbpFramework_Book_Description": "Book Description",
"MasteringAbpFramework_Book_Description_Details_1": "ABP Framework is a complete infrastructure for creating modern web applications by following software \n development best practices and conventions. With ABP's high-level framework and ecosystem, you can \n implement the Don't Repeat Yourself (DRY) principle and focus on your business code.",
"MasteringAbpFramework_Book_Description_Details_2": "Written by the creator of ABP Framework, this book will help you to gain a complete understanding of the\n framework and modern web application development techniques. With step-by-step explanations of essential\n concepts and practical examples, you'll understand the requirements of a modern web solution and how ABP\n Framework makes it enjoyable to develop your own solutions. You'll discover the common requirements of\n enterprise web application development and explore the infrastructure provided by ABP. Throughout the\n book, you'll get to grips with software development best practices for building maintainable and modular\n web solutions.",
"MasteringAbpFramework_Book_Description_Details_3": "By the end of this book, you'll be able to create a complete web solution that is easy to develop,\n maintain, and test.",
"MasteringAbpFramework_Book_WhatYouWillLearn": "What you will learn",
"MasteringAbpFramework_Book_What_You_Will_Learn_1": "Set up the development environment and get started with ABP Framework.",
"MasteringAbpFramework_Book_What_You_Will_Learn_2": "Work with Entity Framework Core and MongoDB to develop your data access layer.",
"MasteringAbpFramework_Book_What_You_Will_Learn_3": "Understand cross-cutting concerns and how ABP automates repetitive tasks.",
"MasteringAbpFramework_Book_What_You_Will_Learn_4": "Get to grips with implementing domain-driven design with ABP Framework.",
"MasteringAbpFramework_Book_What_You_Will_Learn_5": "Build UI pages and components with ASP.NET Core MVC (Razor Pages) and Blazor.",
"MasteringAbpFramework_Book_What_You_Will_Learn_6": "Work with multi-tenancy to create modular web applications.",
"MasteringAbpFramework_Book_What_You_Will_Learn_7": "Understand modularity and create reusable application modules.",
"MasteringAbpFramework_Book_What_You_Will_Learn_8": "Write unit, integration, and UI tests using ABP Framework.",
"MasteringAbpFramework_Book_WhoIsThisBookFor": "Who's this book for",
"MasteringAbpFramework_Book_WhoIsThisBookFor_Description": "This book is for web developers who want to learn software architectures and best practices for building\n maintainable web-based solutions using Microsoft technologies and ABP Framework. Basic knowledge of C#\n and ASP.NET Core is necessary to get started with this book.",
"ComputersAndTechnology": "Computers & Technology"
}
}

@ -174,6 +174,8 @@
"CreateProjectWizard": "Bu sihirbaz, projenize atlamak için uygun şekilde yapılandırılmış başlangıç şablonundan yeni bir proje oluşturur.",
"TieredOption": "Web ve Http API katmanlarının fiziksel olarak ayrıldığı katmanlı bir çözüm oluşturur. İşaretlenmezse, daha az karmaşık olan ve çoğu senaryo için uygun olan katmanlı bir çözüm oluşturur.",
"SeparateIdentityServerOption": "Sunucu tarafını iki uygulamaya ayırır: Birincisi kimlik sunucusu için, ikincisi ise sunucu tarafı HTTP API'niz içindir.",
"ProgressiveWebApplicationOption": "Projeyi Progresif Web Uygulaması olarak ayarlayın.",
"ProgressiveWebApplication": "Progresif Web Uygulaması",
"UseslatestPreVersion": "En son yayın öncesi sürümünü kullanır",
"ReadTheDocumentation": "<span class=\"text-primary\">Belgeleri okuyun</span><span class=\"text-success\"></span>",
"Documentation": "belgeler",

@ -14,6 +14,7 @@ $solutionPaths = @(
"../modules/feature-management",
"../modules/identity",
"../modules/identityserver",
"../modules/openiddict",
"../modules/tenant-management",
"../modules/audit-logging",
"../modules/background-jobs",

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>5.3.0-rc.3</Version>
<Version>6.0.0</Version>
<NoWarn>$(NoWarn);CS1591;CS0436</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io/</PackageProjectUrl>

@ -45,9 +45,9 @@ abp new Acme.BookStore
* `mvc`: ASP.NET Core MVC. Pro tuto šablonu jsou dostupné dodatečné možnosti:
* `--tiered`: Vytvoří stupňovité řešení, kde jsou vrstvy Web a Http API fyzicky odděleny. Pokud není uvedeno, tak vytvoří vrstvené řešení, které je méně složité a vhodné pro většinu scénářů.
* `angular`: Angular. Pro tuto šablonu jsou dostupné dodatečné možnosti:
* `--separate-identity-server`: Oddělí identity server aplikaci od API host aplikace. Pokud není uvedeno, bude na straně serveru jediný koncový bod.
* `--separate-auth-server`: Oddělí identity server aplikaci od API host aplikace. Pokud není uvedeno, bude na straně serveru jediný koncový bod.
* `none`: Bez UI. Pro tuto šablonu jsou dostupné dodatečné možnosti:
* `--separate-identity-server`: Oddělí identity server aplikaci od API host aplikace. Pokud není uvedeno, bude na straně serveru jediný koncový bod.
* `--separate-auth-server`: Oddělí identity server aplikaci od API host aplikace. Pokud není uvedeno, bude na straně serveru jediný koncový bod.
* `--database-provider` nebo `-d`: Určuje poskytovatele databáze. Výchozí poskytovatel je `ef`. Dostupní poskytovatelé:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.

@ -101,7 +101,7 @@ services.AddAbpSwaggerGen(
For non MVC/Tiered applications, we need to configure Swagger with OAUTH to handle authorization.
> ABP Framework uses IdentityServer by default. To get more information about IDS, check this [documentation](../Modules/IdentityServer.md).
> ABP Framework uses OpenIddict by default. To get more information about OpenIddict, check this [documentation](../Modules/OpenIddict.md).

@ -95,29 +95,29 @@ public class MyLogWorker : HangfireBackgroundWorkerBase, IMyLogWorker
## Register BackgroundWorkerManager
After creating a background worker class, you should add it to the `IBackgroundWorkerManager`. The most common place is the `OnApplicationInitialization` method of your module class:
After creating a background worker class, you should add it to the `IBackgroundWorkerManager`. The most common place is the `OnApplicationInitializationAsync` method of your module class:
```` csharp
[DependsOn(typeof(AbpBackgroundWorkersModule))]
public class MyModule : AbpModule
{
public override void OnApplicationInitialization(
public override async Task OnApplicationInitializationAsync(
ApplicationInitializationContext context)
{
context.AddBackgroundWorker<MyLogWorker>();
await context.AddBackgroundWorkerAsync<MyLogWorker>();
//If the interface is defined
//context.AddBackgroundWorker<IMyLogWorker>();
//await context.AddBackgroundWorkerAsync<IMyLogWorker>();
}
}
````
`context.AddBackgroundWorker(...)` is a shortcut extension method for the expression below:
`context.AddBackgroundWorkerAsync(...)` is a shortcut extension method for the expression below:
```` csharp
context.ServiceProvider
.GetRequiredService<IBackgroundWorkerManager>()
.Add(
.AddAsync(
context
.ServiceProvider
.GetRequiredService<MyLogWorker>()
@ -126,4 +126,4 @@ context.ServiceProvider
So, it resolves the given background worker and adds to the `IBackgroundWorkerManager`.
While we generally add workers in OnApplicationInitialization, there are no restrictions on that. You can inject IBackgroundWorkerManager anywhere and add workers at runtime. Background worker manager will stop and release all the registered workers when your application is being shut down.
While we generally add workers in `OnApplicationInitializationAsync`, there are no restrictions on that. You can inject `IBackgroundWorkerManager` anywhere and add workers at runtime. Background worker manager will stop and release all the registered workers when your application is being shut down.

@ -80,16 +80,16 @@ public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase
## Register Background Worker
After creating a background worker class, you should add it to the `IBackgroundWorkerManager`. The most common place is the `OnApplicationInitialization` method of your module class:
After creating a background worker class, you should add it to the `IBackgroundWorkerManager`. The most common place is the `OnApplicationInitializationAsync` method of your module class:
````csharp
[DependsOn(typeof(AbpBackgroundWorkersModule))]
public class MyModule : AbpModule
{
public override Task OnApplicationInitializationAsync(
public override async Task OnApplicationInitializationAsync(
ApplicationInitializationContext context)
{
context.AddBackgroundWorkerAsync<PassiveUserCheckerWorker>();
await context.AddBackgroundWorkerAsync<PassiveUserCheckerWorker>();
}
}
````
@ -108,7 +108,7 @@ await context.ServiceProvider
So, it resolves the given background worker and adds to the `IBackgroundWorkerManager`.
While we generally add workers in `OnApplicationInitialization`, there are no restrictions on that. You can inject `IBackgroundWorkerManager` anywhere and add workers at runtime. Background worker manager will stop and release all the registered workers when your application is being shut down.
While we generally add workers in `OnApplicationInitializationAsync`, there are no restrictions on that. You can inject `IBackgroundWorkerManager` anywhere and add workers at runtime. Background worker manager will stop and release all the registered workers when your application is being shut down.
## Options

@ -15,7 +15,7 @@ The following commands are for creating Angular UI projects:
* **Entity Framework Core**, default app template, **separate Identity Server**, creates the project in a new folder:
```bash
abp new Acme.BookStore -t app -u angular -m none --separate-identity-server --database-provider ef -csf
abp new Acme.BookStore -t app -u angular -m none --separate-auth-server --database-provider ef -csf
```
* **Entity Framework Core**, **custom connection string**, creates the project in a new folder:
@ -33,7 +33,7 @@ The following commands are for creating Angular UI projects:
* **MongoDB**, default app template, no mobile app, **separate Identity Server**, creates the project in a new folder:
```bash
abp new Acme.BookStore -t app -u angular -m none --separate-identity-server --database-provider mongodb -csf
abp new Acme.BookStore -t app -u angular -m none --separate-auth-server --database-provider mongodb -csf
```
## MVC
@ -78,7 +78,7 @@ The following commands are for creating Blazor projects:
* **Entity Framework Core**, **separate Identity Server**, mobile app included:
```bash
abp new Acme.BookStore -u blazor --separate-identity-server
abp new Acme.BookStore -u blazor --separate-auth-server
```
* **MongoDB**, no mobile app, creates the project in a new folder:
@ -116,7 +116,7 @@ In the default app template, there is always a frontend project. In this option
* **Entity Framework Core**, separate Identity Server, creates the project in a new folder:
```bash
abp new Acme.BookStore -u none --separate-identity-server -csf
abp new Acme.BookStore -u none --separate-auth-server -csf
```
* **MongoDB**, no mobile app:

@ -92,15 +92,15 @@ For more samples, go to [ABP CLI Create Solution Samples](CLI-New-Command-Sample
* `mvc`: ASP.NET Core MVC. There are some additional options for this template:
* `--tiered`: Creates a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios.
* `angular`: Angular UI. There are some additional options for this template:
* `--separate-identity-server`: The Identity Server project comes as a separate project and runs at a different endpoint. It separates the Identity Server from the API Host application. If not specified, you will have a single endpoint in the server side.
* `--separate-auth-server`: The Identity Server project comes as a separate project and runs at a different endpoint. It separates the Identity Server from the API Host application. If not specified, you will have a single endpoint in the server side.
* `--pwa`: Specifies the project as Progressive Web Application.
* `blazor`: Blazor UI. There are some additional options for this template:
* `--separate-identity-server`The Identity Server project comes as a separate project and runs at a different endpoint. It separates the Identity Server from the API Host application. If not specified, you will have a single endpoint in the server side.
* `--separate-auth-server`The Identity Server project comes as a separate project and runs at a different endpoint. It separates the Identity Server from the API Host application. If not specified, you will have a single endpoint in the server side.
* `--pwa`: Specifies the project as Progressive Web Application.
* `blazor-server`: Blazor Server UI. There are some additional options for this template:
* `--tiered`: The Identity Server and the API Host project comes as separate projects and run at different endpoints. It has 3 startup projects: *HttpApi.Host*, *IdentityServer* and *Blazor* and and each runs on different endpoints. If not specified, you will have a single endpoint for your web project.
* `--tiered`: The Identity Server and the API Host project comes as separate projects and run at different endpoints. It has 3 startup projects: *HttpApi.Host*, *AuthServer* and *Blazor* and and each runs on different endpoints. If not specified, you will have a single endpoint for your web project.
* `none`: Without UI. No front-end layer will be created. There are some additional options for this template:
* `--separate-identity-server`: The Identity Server project comes as a separate project and runs at a different endpoint. It separates the Identity Server from the API Host application. If not specified, you will have a single endpoint in the server side.
* `--separate-auth-server`: The Identity Server project comes as a separate project and runs at a different endpoint. It separates the Identity Server from the API Host application. If not specified, you will have a single endpoint in the server side.
* `--mobile` or `-m`: Specifies the mobile application framework. If not specified, no mobile application will be created. Available options:
* `react-native`: React Native.
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:

@ -0,0 +1,243 @@
# Consuming HTTP APIs from a .NET Client Using ABP's Client Proxy System
In this article, I will explain how to consume HTTP APIs from a .NET application using ABP's [dynamic](https://docs.abp.io/en/abp/latest/API/Dynamic-CSharp-API-Clients) and [static](https://docs.abp.io/en/abp/latest/API/Static-CSharp-API-Clients) client-side proxy systems. I will start by creating a new project and consume the HTTP APIs from a .NET console application using dynamic client proxies. Then I will switch to static client proxies. Finally, I will glance at the differences and similarities between static and dynamic generic proxies.
Here the main benefits of using the client-side proxy system (either dynamic or static):
* Automatically maps C# method calls to remote server HTTP calls by considering the HTTP method, route, query string parameters, request payload and other details.
* Authenticates the HTTP Client by adding an access token to the HTTP header.
* Serializes to and deserialize from JSON.
* Handles HTTP API versioning.
* Adds correlation id, current tenant id and the current culture to the request.
* Properly handles the error messages sent by the server and throws proper exceptions.
## Create a new ABP application with the ABP CLI
Firstly create a new solution via [ABP CLI](https://docs.abp.io/en/abp/latest/CLI):
```shell
abp new Acme.BookStore
```
> See ABP's [Getting Started document](https://docs.abp.io/en/abp/latest/Getting-Started-Setup-Environment?UI=MVC&DB=EF&Tiered=No) to learn how to create and run your application, if you haven't done it before.
## Create the application service interface
I will start by creating an application service and exposing it as an HTTP API to be consumed by remote clients. First, define an interface for the application service; Create an `IBookAppService` interface in the `Books` folder (namespace) of the `Acme.BookStore.Application.Contracts` project:
````csharp
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Acme.BookStore.Books
{
public interface IBookAppService : IApplicationService
{
Task<PagedResultDto<BookDto>> GetListAsync();
}
}
````
Also add a `BookDto` class inside the same `Books` folder:
```csharp
using System;
using Volo.Abp.Application.Dtos;
namespace Acme.BookStore.Books
{
public class BookDto
{
public string Name { get; set; }
public string AuthorName { get; set; }
public float Price { get; set; }
}
}
```
## Implement the application service
It is time to implement the `IBookAppService` interface. Create a new class named `BookAppService` in the `Books` namespace (folder) of the `Acme.BookStore.Application` project:
```csharp
using Acme.BookStore.Permissions;
using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Acme.BookStore.Books
{
public class BookAppService : ApplicationService, IBookAppService
{
public Task<PagedResultDto<BookDto>> GetListAsync()
{
var bookDtos = new List<BookDto>()
{
new BookDto(){ Name = "Hunger", AuthorName ="Knut Hamsun", Price = 50},
new BookDto(){ Name = "Crime and Punishment", AuthorName ="Dostoevsky", Price = 60},
new BookDto(){ Name = "For Whom the Bell Tolls", AuthorName ="Ernest Hemingway", Price = 70}
};
return Task.FromResult(new PagedResultDto<BookDto>(
bookDtos.Count,
bookDtos
));
}
}
}
```
It simply returns a list of books. You probably want to get the books from a database, but it doesn't matter for this article. If you want it, you can fully implement [this tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF).
## Consume the app service from the console application
The startup solution comes with an example .NET console application (`Acme.BookStore.HttpApi.Client.ConsoleTestApp`) that is fully configured to consume your HTTP APIs remotely. Change `ClientDemoService` as shown in the following `Acme.BookStore.HttpApi.Client.ConsoleTestApp` project (it is under the `test` folder).
```csharp
using Acme.BookStore.Books;
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.DependencyInjection;
namespace Acme.BookStore.HttpApi.Client.ConsoleTestApp;
public class ClientDemoService : ITransientDependency
{
private readonly IBookAppService _bookAppService;
public ClientDemoService(IBookAppService bookAppService )
{
_bookAppService = bookAppService;
}
public async Task RunAsync()
{
var listOfBooks = await _bookAppService.GetListAsync(new PagedAndSortedResultRequestDto());
Console.WriteLine($"Books: {string.Join(", ", listOfBooks.Items.Select(p => p.Name).ToList())}");
}
}
```
We are basically injecting the `IBookAppService` interface to consume the remote service. ABP handles all the details (performing HTTP request, deserializing the resulting JSON object, etc) for us.
You can run the application to see the output:
```
Books: Hunger, Crime and Punishment, For Whom the Bell Tolls
```
## Convert the application to use static client proxies
The [application startup template](https://docs.abp.io/en/abp/latest/Startup-Templates/Application) comes pre-configured for the **dynamic** client proxy generation, in the `HttpApi.Client` project. If you want to switch to the **static** client proxies, you should change `context.Services.AddHttpClientProxies` to `context.Services.AddStaticHttpClientProxies` in the module class of your `HttpApi.Client` project:
```csharp
public class BookStoreHttpApiClientModule : AbpModule
{
public const string RemoteServiceName = "Default";
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Other configurations...
context.Services.AddStaticHttpClientProxies(
typeof(BookStoreApplicationContractsModule).Assembly,
RemoteServiceName
);
}
}
```
The `AddStaticHttpClientProxies` method gets an assembly, finds all service interfaces in the given assembly, and prepares for static client proxy generation.
Now you're ready to generate the client proxy code by running the following command in the root folder of your client project **while your server-side project is running**:
````bash
abp generate-proxy -t csharp -u http://localhost:44397/
````
> The URL (`-u` parameter's value) might be different for your application. It should be the server's root URL.
You should see the generated files under the selected folder:
![files of the static proxy](./static-proxy.png)
Now you can run the console client application again. You should see the same output:
````
Books: Hunger, Crime and Punishment, For Whom the Bell Tolls
````
## Add authorization
The ABP Framework provides an [authorization system](https://docs.abp.io/en/abp/latest/Authorization) based on [ASP.NET Core's authorization infrastructure](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction). We can define permissions and restrict access to some of our application's functionalities, so only the allowed users/clients can use these functionalities. Here, I will define a permission to be able to get the list of books.
### Defining a permission
Under `Acme.BookStore.Application.Contracts` open `BookStorePermissions` and paste the below code:
```csharp
namespace Acme.BookStore.Permissions;
public static class BookStorePermissions
{
public const string GroupName = "BookStore";
public static class Books
{
public const string Default = GroupName + ".Books";
}
}
```
You also need to change `BookStorePermissionDefinitionProvider` under the same folder and project as follows:
```csharp
using Acme.BookStore.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var bookStoreGroup = context.AddGroup(BookStorePermissions.GroupName);
bookStoreGroup.AddPermission(BookStorePermissions.Books.Default);
}
}
```
### Authorizing the application service
We can now add the `[Authorize(BookStorePermissions.Books.Default)]` attribute to the `BookAppService` class:
```csharp
[Authorize(BookStorePermissions.Books.Default)]
public class BookAppService : ApplicationService, IBookAppService
{
...
}
```
If you run the server now, then run the console client application, you will see the following error on the console application:
```
Unhandled exception. Volo.Abp.Http.Client.AbpRemoteCallException: Forbidden at
Volo.Abp.Http.Client.ClientProxying.ClientProxyBase`1
.ThrowExceptionForResponseAsync(HttpResponseMessage response)...
```
To fix the problem, we should grant permission to the admin user. We are granting permission to the admin user because the console application is configured to use the Resource Owner Password Grant Flow. That means the client application is consuming services on behalf of the admin user. You can see the configuration in the `appsettings.json` file of the console application.
### Granting the permission
Once you define the permissions, you can see them on the permission management modal.
Go to the Administration -> Identity -> Roles page, select the Permissions action for the admin role to open the permission management modal:
![persmisson](./permission.png)
Grant the permissions you want and save the modal.
## Dynamic vs static proxies
Static generic proxies provide **better performance** because they don't need to run on runtime, but you should **re-generate** them once you change the API endpoint definition. Dynamic generic proxies don't need to be re-generated because they work on the runtime but they have a slight performance penalty.
## Further Reading
In this tutorial, I explained how you can create an example project and apply a static client proxy instead of a dynamic client proxy. I also summarized the differences between both approaches. If you want to get more information, you can read the following documents:
* [Static C# API Client Proxies](https://docs.abp.io/en/abp/latest/API/Static-CSharp-API-Clients)
* [Dynamic C# API Client Proxies](https://docs.abp.io/en/abp/latest/API/Dynamic-CSharp-API-Clients)
* [Web Application Development Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF)

@ -0,0 +1,54 @@
# How to contribute to abp.io as a frontend developer
## How to setup development environment
### Pre-requirements
- Dotnet core SDK https://dotnet.microsoft.com/en-us/download
- Nodejs LTS https://nodejs.org/en/
- Docker https://docs.docker.com/engine/install
- Angular CLI. https://angular.io/guide/what-is-angular#angular-cli
- Abp CLI https://docs.abp.io/en/abp/latest/cli
- A code editor
Note: This arcticle prepare Windows OS. You may change the path type of your OS. an Example
Windows: `templates\app\aspnet-core\src\MyCompanyName.MyProjectName.DbMigrator\appsettings.json`
Unix: `templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/appsettings.json`
### Sample docker commands
You need to install SQL Server and Redis. You can install these programs without docker, but my example uses docker containers. Your computer should have Docker Engine. Then open the terminal en execute the commands one by one.
For the Sql Server
docker run -v sqlvolume:/var/opt/mssql -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=yourpassword -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-CU3-ubuntu-18.04
For the Redis
docker run -p 6379:6379 -d redis
Then we are ready to download and execute the code.
## Folder Structure
The app has a backend written in .net core (c#) and an angular app. It would help if you ran both of them.
### Running Backend App
The path of the Backend app is “templates\app\aspnet-core.” If you want to work with dockerized SQL Server, you should change connection strings for running with docker. The path of the connection string is
`templates\app\aspnet-core\src\MyCompanyName.MyProjectName.DbMigrator\appsettings.json`.
Before running the backend, you should run the Db migrator project. The DbMigrator created initial tables and values. The path of DbMigrator is `templates\app\aspnet-core\src\MyCompanyName.MyProjectName.DbMigrator`. Open a terminal in the path and execute the command `dotnet run` in terminal
One last step before the running the backend is installing client-side libraries. Go to `templates\app\aspnet-core`. Open a terminal in the path and execute the command `abp install-libs` in terminal
Next step you should go to path of backend host project. The path is `templates\app\aspnet-core\src\MyCompanyName.MyProjectName.HttpApi.HostWithIds`. Open a terminal in the path and execute the command `dotnet run` in terminal
Your backend should be running successfully
### Running Frontend App
There is a demo app. The path of the demo app is `npm\ng-packs\apps\dev-app`. The demo app is connected to the packages with local references. Open the terminal in `npm\ng-packs\apps\dev-app` and execute `yarn` or `npm i` in terminal. After the package installed run `npm start` or `yarn start`.
The repo uses Nx and packages connected with `local references`. The packages path is `”npm\ng-packs\packages`

@ -71,6 +71,10 @@ If you want to make a change on a specific resource file, you can find the file
If you find any bug, please [create an issue on the Github repository](https://github.com/abpframework/abp/issues/new).
## Setup Frontend Development Environment
[How to contribute to abp.io as a frontend developer](How-to-Contribute-abp.io-as-a-frontend-developer.md)
## See Also
* [ABP Community Talks 2022.4: How can you contribute to the open source ABP Framework?](https://www.youtube.com/watch?v=Wz4Z-O-YoPg&list=PLsNclT2aHJcOsPustEkzG6DywiO8eh0lB)
* [ABP Community Talks 2022.4: How can you contribute to the open source ABP Framework?](https://www.youtube.com/watch?v=Wz4Z-O-YoPg&list=PLsNclT2aHJcOsPustEkzG6DywiO8eh0lB)

@ -20,7 +20,7 @@ We will use the ABP CLI to create a new ABP project.
Use the `new` command of the ABP CLI to create a new project:
````shell
abp new Acme.BookStore{{if UI == "NG"}} -u angular{{else if UI == "Blazor"}} -u blazor{{else if UI == "BlazorServer"}} -u blazor-server{{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes"}}{{if UI == "MVC" || UI == "BlazorServer"}} --tiered{{else}} --separate-identity-server{{end}}{{end}}
abp new Acme.BookStore{{if UI == "NG"}} -u angular{{else if UI == "Blazor"}} -u blazor{{else if UI == "BlazorServer"}} -u blazor-server{{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes"}}{{if UI == "MVC" || UI == "BlazorServer"}} --tiered{{else}} --separate-auth-server{{end}}{{end}}
````
*You can use different level of namespaces; e.g. BookStore, Acme.BookStore or Acme.Retail.BookStore.*
@ -33,7 +33,7 @@ abp new Acme.BookStore{{if UI == "NG"}} -u angular{{else if UI == "Blazor"}} -u
{{ else }}
* `--separate-identity-server` argument is used to separate the identity server application from the API host application. If not specified, you will have a single endpoint on the server.
* `--separate-auth-server` argument is used to separate the identity server application from the API host application. If not specified, you will have a single endpoint on the server.
{{ end }}

@ -65,8 +65,8 @@ A React Native application running on an Android emulator or a physical phone **
![React Native tiered project local IP entry](images/rn-tiered-local-ip.png)
* Open the `appsettings.json` in the `.IdentityServer` folder. Replace the `localhost` address on the `SelfUrl` property with your local IP address.
* Open the `launchSettings.json` in the `.IdentityServer/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
* Open the `appsettings.json` in the `.AuthServer` folder. Replace the `localhost` address on the `SelfUrl` property with your local IP address.
* Open the `launchSettings.json` in the `.AuthServer/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
* Open the `appsettings.json` in the `.HttpApi.Host` folder. Replace the `localhost` address on the `Authority` property with your local IP address.
* Open the `launchSettings.json` in the `.HttpApi.Host/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
@ -85,7 +85,7 @@ Run the backend application as described in the [getting started document](Getti
{{ if Tiered == "Yes" }}
> Make sure that `issuer` matches the running address of the `.IdentityServer` project, `apiUrl` matches the running address of the `.HttpApi.Host` or `.Web` project.
> Make sure that `issuer` matches the running address of the `.AuthServer` project, `apiUrl` matches the running address of the `.HttpApi.Host` or `.Web` project.
{{else}}

@ -15,7 +15,7 @@
### Connection String
Check the **connection string** in the `appsettings.json` file under the {{if Tiered == "Yes"}}`.IdentityServer` and `.HttpApi.Host` projects{{else}}{{if UI=="MVC"}}`.Web` project{{else if UI=="BlazorServer"}}`.Blazor` project{{else}}`.HttpApi.Host` project{{end}}{{end}}.
Check the **connection string** in the `appsettings.json` file under the {{if Tiered == "Yes"}}`.AuthServer` and `.HttpApi.Host` projects{{else}}{{if UI=="MVC"}}`.Web` project{{else if UI=="BlazorServer"}}`.Blazor` project{{else}}`.HttpApi.Host` project{{end}}{{end}}.
{{ if DB == "EF" }}
@ -99,7 +99,7 @@ Right click to the `.DbMigrator` project and select **Set as StartUp Project**
> Tiered solutions use **Redis** as the distributed cache. Ensure that it is installed and running in your local computer. If you are using a remote Redis Server, set the configuration in the `appsettings.json` files of the projects below.
1. Ensure that the `.IdentityServer` project is the startup project. Run this application that will open a **login** page in your browser.
1. Ensure that the `.AuthServer` project is the startup project. Run this application that will open a **login** page in your browser.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
@ -137,7 +137,7 @@ Ensure that the {{if UI=="MVC"}}`.Web`{{else}}`.Blazor`{{end}} project is the st
> Tiered solutions use Redis as the distributed cache. Ensure that it is installed and running in your local computer. If you are using a remote Redis Server, set the configuration in the `appsettings.json` files of the projects below.
Ensure that the `.IdentityServer` project is the startup project. Run the application which will open a **login** page in your browser.
Ensure that the `.AuthServer` project is the startup project. Run the application which will open a **login** page in your browser.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.

@ -47,7 +47,7 @@ Application Modules provides pre-built application functionalities;
* [**Account**](Modules/Account.md): Provides UI for the account management and allows user to login/register to the application.
* **[Identity](Modules/Identity.md)**: Manages organization units, roles, users and their permissions, based on the Microsoft Identity library.
* [**IdentityServer**](Modules/IdentityServer.md): Integrates to IdentityServer4.
* [**OpenIddict**](Modules/OpenIddict.md): Integrates to OpenIddict.
* [**Tenant Management**](Modules/Tenant-Management.md): Manages tenants for a [multi-tenant](Multi-Tenancy.md) (SaaS) application.
See the [Application Modules](Modules/Index.md) document for all pre-built modules.

@ -2,7 +2,7 @@
Account module implements the basic authentication features like **login**, **register**, **forgot password** and **account management**.
This module is based on [Microsoft's Identity library](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity) and the [Identity Module](Identity.md). It has [IdentityServer](https://github.com/IdentityServer) integration (based on the [IdentityServer Module](IdentityServer.md)) to provide **single sign-on**, access control and other advanced authentication features.
This module is based on [Microsoft's Identity library](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity) and the [Identity Module](Identity.md). It has [IdentityServer](https://github.com/IdentityServer) integration (based on the [IdentityServer Module](IdentityServer.md)) and [OpenIddict](https://github.com/openiddict) integration (based on the [OpenIddict Module](OpenIddict.md)) to provide **single sign-on**, access control and other advanced authentication features.
## How to Install
@ -42,6 +42,10 @@ Social/external login buttons becomes visible if you setup it. See the *Social/E
![account-module-manage-account](../images/account-module-manage-account.png)
## OpenIddict Integration
[Volo.Abp.Account.Web.OpenIddict](https://www.nuget.org/packages/Volo.Abp.Account.Web.OpenIddict) package provides integration for the [OpenIddict](https://github.com/openiddict). This package comes as installed with the [application startup template](../Startup-Templates/Application.md). See the [OpenIddict Module](OpenIddict.md) documentation.
## IdentityServer Integration
[Volo.Abp.Account.Web.IdentityServer](https://www.nuget.org/packages/Volo.Abp.Account.Web.IdentityServer) package provides integration for the [IdentityServer](https://github.com/IdentityServer). This package comes as installed with the [application startup template](../Startup-Templates/Application.md). See the [IdentityServer Module](IdentityServer.md) documentation.

@ -19,6 +19,7 @@ There are some **free and open source** application modules developed and mainta
* [**Feature Management**](Feature-Management.md): Used to persist and manage the [features](../Features.md).
* **[Identity](Identity.md)**: Manages organization units, roles, users and their permissions, based on the Microsoft Identity library.
* [**IdentityServer**](IdentityServer.md): Integrates to IdentityServer4.
* [**OpenIddict**](OpenIddict.md): Integrates to OpenIddict.
* [**Permission Management**](Permission-Management.md): Used to persist permissions.
* **[Setting Management](Setting-Management.md)**: Used to persist and manage the [settings](../Settings.md).
* [**Tenant Management**](Tenant-Management.md): Manages tenants for a [multi-tenant](../Multi-Tenancy.md) application.

@ -0,0 +1,215 @@
## ABP OpenIddict Modules
## How to Install
TODO:
## User Interface
This module implements the domain logic and database integrations, but not provides any UI. Management UI is useful if you need to add applications and scopes on the fly. In this case, you may build the management UI yourself or consider to purchase the [ABP Commercial](https://commercial.abp.io/) which provides the management UI for this module.
## Relations to Other Modules
This module is based on the [Identity Module](Identity.md) and have an [integration package](https://www.nuget.org/packages/Volo.Abp.Account.Web.OpenIddict) with the [Account Module](Account.md).
## The module
### Demo projects
In the module's `app` directory there are six projects(including `angular`)
* `OpenIddict.Demo.Server`: An abp application with integrated modules (has two `clients` and a `scope`).
* `OpenIddict.Demo.API`: ASP NET Core API application using JwtBearer authentication
* `OpenIddict.Demo.Client.Mvc`: ASP NET Core MVC application using `OpenIdConnect` for authentication
* `OpenIddict.Demo.Client.Console`: Use `IdentityModel` to test OpenIddict's various endpoints, and call the api of `OpenIddict.Demo.API`
* `OpenIddict.Demo.Client.BlazorWASM:` ASP NET Core Blazor application using `OidcAuthentication` for authentication
* `angular`: An angular application that integrates the abp ng modules and uses oauth for authentication
#### How to run?
Confirm the connection string of `appsettings.json` in the `OpenIddict.Demo.Server` project. Running the project will automatically create the database and initialize the data.
After running the `OpenIddict.Demo.API` project, then you can run the rest of the projects to test.
### Domain module
There are four main entities included in this module.
* OpenIddictApplication: **Represents applications(client)**
* OpenIddictScope: **Represents scopes**
* OpenIddictAuthorization: **Represents authorizations, Track of logical chains of tokens and user consent..**
* OpenIddictToken: **Represents various tokens.**
Domain also implements four store interfaces in OpenIddict, OpenIddict uses store to manage entities, corresponding to the above four entities, Custom entity repository is used in the store.
```cs
//Manager
OpenIddictApplicationManager
OpenIddictScopeManager
OpenIddictAuthorizationManager
OpenIddictTokenManager
//Store
IOpenIddictApplicationStore
IOpenIddictScopeStore
IOpenIddictAuthorizationStore
IOpenIddictTokenStore
//Repository
IOpenIddictApplicationRepository
IOpenIddictScopeRepository
IOpenIddictAuthorizationRepository
IOpenIddictTokenRepository
```
We enabled most of OpenIddict's features in the `AddOpenIddict` method, You can change OpenIddict's related builder options via `PreConfigure`.
```cs
PreConfigure<OpenIddictBuilder>(builder =>
{
//builder
});
PreConfigure<OpenIddictCoreBuilder>(builder =>
{
//builder
});
PreConfigure<OpenIddictServerBuilder>(builder =>
{
//builder
});
```
#### AbpOpenIddictAspNetCoreOptions
`UpdateAbpClaimTypes(default: true)`: Updates AbpClaimTypes to be compatible with identity server claims.
`AddDevelopmentEncryptionAndSigningCertificate(default: true)`: Registers (and generates if necessary) a user-specific development encryption/development signing certificate.
You can also change this options via `PreConfigure`.
#### Automatically removing orphaned tokens/authorizations
There is a background task in the `Domain` module (`enabled by default`) that automatically removes orphaned tokens/authorizations, you can configure `TokenCleanupOptions` to manage it.
### ASP NET Core module
This module integrates ASP NET Core, with built-in MVC controllers for four protocols. It uses OpenIddict's [Pass-through mode](https://documentation.openiddict.com/guides/index.html#pass-through-mode).
```cs
AuthorizeController -> connect/authorize
TokenController -> connect/token
LogoutController -> connect/logout
UserInfoController -> connect/userinfo
```
> We will implement the related functions of **device flow** in the PRO module..
#### How to control claims in access_token and id_token
You can use the [Claims Principal Factory](https://docs.abp.io/en/abp/latest/Authorization#claims-principal-factory) to add/remove claims to the `ClaimsPrincipal`.
The `AbpDefaultOpenIddictClaimDestinationsProvider` service will add `Name`, `Email` and `Role` types of Claims to `access_token` and `id_token`, other claims are only added to `access_token` by default, and remove the `SecurityStampClaimType` secret claim of `Identity`.
You can create a service that inherits from `IAbpOpenIddictClaimDestinationsProvider` and add it to DI to fully control the destinations of claims
```cs
public class MyClaimDestinationsProvider : IAbpOpenIddictClaimDestinationsProvider, ITransientDependency
{
public virtual Task SetDestinationsAsync(AbpOpenIddictClaimDestinationsProviderContext context)
{
// ...
return Task.CompletedTask;
}
}
Configure<AbpOpenIddictClaimDestinationsOptions>(options =>
{
options.ClaimDestinationsProvider.Add<MyClaimDestinationsProvider>();
});
```
For detailed information, please refer to: [OpenIddict claim destinations](https://documentation.openiddict.com/configuration/claim-destinations.html)
### EF Core module
Implements the above four repository interfaces.
### MongoDB module
Implements the above four repository interfaces.
## OpenIddict
### Documentation
For more details about OpenIddict, please refer to its official documentation and Github.
https://documentation.openiddict.com
https://github.com/openiddict/openiddict-core#resources
### Disable AccessToken Encryption
ABP disables the `access token encryption` by default for compatibility, you can manually enable it if needed.
```cs
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<OpenIddictServerBuilder>(builder =>
{
builder.Configure(options => options.DisableAccessTokenEncryption = false);
});
}
```
https://documentation.openiddict.com/configuration/token-formats.html#disabling-jwt-access-token-encryption
### PKCE
https://documentation.openiddict.com/configuration/proof-key-for-code-exchange.html
### Request/Response process
I will briefly introduce the principle of OpenIddict so that everyone can quickly understand it.
The `OpenIddict.Server.AspNetCore` adds an authentication scheme(`Name: OpenIddict.Server.AspNetCore, handler: OpenIddictServerAspNetCoreHandler`) and implements the `IAuthenticationRequestHandler` interface.
It will be executed first in `AuthenticationMiddleware` and can short-circuit the current request. Otherwise, `DefaultAuthenticateScheme` will be called and continue to execute the pipeline.
`OpenIddictServerAspNetCoreHandler` will call various built-in handlers(Handling requests and responses), And the handler will process according to the context or skip logic that has nothing to do with it.
Example a token request:
```
POST /connect/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=password&
client_id=AbpApp&
client_secret=1q2w3e*&
username=admin&
password=1q2w3E*&
scope=AbpAPI offline_access
```
This request will be processed by various handlers. They will confirm the endpoint type of the request, check `http/https`, verify that the request parameters (`client. scope etc`) are valid and exist in the database, etc. Various protocol checks. And build a `OpenIddictRequest` object, If there are any errors, the response content may be set and directly short-circuit the current request.
If everything is ok, the request will go to our processing controller(eg `TokenController`), we can get an `OpenIddictRequest` from the http request at this time. The rest of our work will be based on this object.
We may check the `username` and `password` in the request. If it is correct we create a `ClaimsPrincipal` object and return a `SignInResult`, which uses the `OpenIddict.Validation.AspNetCore` authentication scheme name, will calls `OpenIddictServerAspNetCoreHandler` for processing.
`OpenIddictServerAspNetCoreHandler` do some checks to generate json and replace the http response content.
The `ForbidResult` `ChallengeResult` are all the above types of processing.
If you need to customize OpenIddict, you need to replace/delete/add new handlers and make it execute in the correct order.
Please refer to:
https://documentation.openiddict.com/guides/index.html#events-model
## Sponsor
Please consider sponsoring this project: https://github.com/sponsors/kevinchalet

@ -178,10 +178,8 @@ namespace MyMvcUIPlugIn
//Add plugin assembly
mvcBuilder.PartManager.ApplicationParts.Add(new AssemblyPart(typeof(MyMvcUIPlugInModule).Assembly));
//Add views assembly
var viewDllPath = Path.Combine(Path.GetDirectoryName(typeof(MyMvcUIPlugInModule).Assembly.Location), "MyMvcUIPlugIn.Views.dll");
var viewAssembly = new CompiledRazorAssemblyPart(Assembly.LoadFrom(viewDllPath));
mvcBuilder.PartManager.ApplicationParts.Add(viewAssembly);
//Add CompiledRazorAssemblyPart if the PlugIn module contains razor views.
mvcBuilder.PartManager.ApplicationParts.Add(new CompiledRazorAssemblyPart(typeof(MyMvcUIPlugInModule).Assembly));
});
}
}
@ -189,8 +187,7 @@ namespace MyMvcUIPlugIn
````
* Depending on the `AbpAspNetCoreMvcUiThemeSharedModule` since we added the related NuGet package.
* Adding the plug-in's assembly to the `PartManager` of ASP.NET Core MVC. This is required by ASP.NET Core. Otherwise, your controllers inside the plug-in doesn't work.
* Adding the plug-in's views assembly to the `PartManager` of ASP.NET Core MVC. This is required by ASP.NET Core. Otherwise, your views inside the plug-in doesn't work.
* Adding the plug-in's assembly as `AssemblyPart` and `CompiledRazorAssemblyPart` to the `PartManager` of ASP.NET Core MVC. This is required by ASP.NET Core. Otherwise, your controllers or views inside the plug-in doesn't work.
You can now add a razor page, like `MyPlugInPage.cshtml` inside the `Pages` folder:
@ -205,7 +202,7 @@ Now, you can build the plug-in project. It will produce the following output:
![simple-razor-plug-in-dll-file](images/simple-razor-plug-in-dll-file.png)
Copy the `MyMvcUIPlugIn.dll` and `MyMvcUIPlugIn.Views.dll` into the plug-in folder (`D:\Temp\MyPlugIns` for this example).
Copy the `MyMvcUIPlugIn.dll` into the plug-in folder (`D:\Temp\MyPlugIns` for this example).
If you have configured the main application like described above (see Basic Usage section), you should be able to visit the `/MyPlugInPage` URL when your application:

@ -164,6 +164,10 @@ If your entity is a soft-delete entity, you can use the `HardDeleteAsync` method
> See the [Data Filtering](Data-Filtering.md) documentation for more about soft-delete.
### Ensure Entities Exists
The `EnsureExistsAsync` extension method accepts entity id or entities query expression to ensure entities exist, otherwise, it will throw `EntityNotFoundException`.
## Other Generic Repository Types
Standard `IRepository<TEntity, TKey>` interface exposes the standard `IQueryable<TEntity>` and you can freely query using the standard LINQ methods. This is fine for most of the applications. However, some ORM providers or database systems may not support standard `IQueryable` interface. If you want to use such providers, you can't rely on the `IQueryable`.

@ -226,15 +226,15 @@ The solution structure is shown below:
![bookstore-visual-studio-solution-v3](../images/bookstore-visual-studio-solution-tiered.png)
As different from the default structure, two new projects come into play: `.IdentityServer` & `.HttpApi.Host`.
As different from the default structure, two new projects come into play: `.AuthServer` & `.HttpApi.Host`.
#### .IdentityServer Project
#### .AuthServer Project
This project is used as an authentication server for other projects. `.Web` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the IdentityServer. Then uses the access token to call the HTTP API server. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
This project is used as an authentication server for other projects. `.Web` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the AuthServer. Then uses the access token to call the HTTP API server. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
![tiered-solution-applications](../images/tiered-solution-applications.png)
ABP uses the open source [IdentityServer4](https://identityserver.io/) framework for the authentication between applications. See [IdentityServer4 documentation](http://docs.identityserver.io) for details about the IdentityServer4 and OpenID Connect protocol.
ABP uses the open source [OpenIddcit](https://github.com/openiddict/openiddict-core) framework for the authentication between applications. See [OpenIddcit documentation](https://documentation.openiddict.com/) for details about the OpenIddict and OpenID Connect protocol.
It has its own `appsettings.json` that contains database connection and other configurations.
@ -256,7 +256,7 @@ This project contains an `appsettings.json` file, but this time it does not have
You should run the application with the given order:
* First, run the `.IdentityServer` since other applications depend on it.
* First, run the `.AuthServer` since other applications depend on it.
* Then run the `.HttpApi.Host` since it is used by the `.Web` application.
* Finally, you can run the `.Web` project and login to the application (using `admin` as the username and `1q2w3E*` as the password).

@ -139,7 +139,7 @@ Set `host/YourProjectName.Web.Unified` as the startup project, run `Update-Datab
In this scenario, there are three applications;
* `.IdentityServer` application is an authentication server used by other applications. It has its own `appsettings.json` that contains database connection and other configurations.
* `.AuthServer` application is an authentication server used by other applications. It has its own `appsettings.json` that contains database connection and other configurations.
* `.HttpApi.Host` hosts the HTTP API of the module. It has its own `appsettings.json` that contains database connections and other configurations.
* `.Web.Host` host the UI of the module. This project contains an `appsettings.json` file, but it does not have a connection string because it never connects to the database. Instead, it mainly contains endpoint of the remote API server and the authentication server.
@ -147,7 +147,7 @@ The diagram below shows the relation of the applications:
![tiered-solution-applications](../images/tiered-solution-applications.png)
`.Web.Host` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the `.IdentityServer`. Then uses the access token to call the `.HttpApi.Host`. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
`.Web.Host` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the `.AuthServer`. Then uses the access token to call the `.HttpApi.Host`. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
##### Pre-requirements
@ -157,7 +157,7 @@ The diagram below shows the relation of the applications:
You should run the application with the given order:
- First, run the `.IdentityServer` since other applications depends on it.
- First, run the `.AuthServer` since other applications depends on it.
- Then run the `.HttpApi.Host` since it is used by the `.Web.Host` application.
- Finally, you can run the `.Web.Host` project and login to the application using `admin` as the username and `1q2w3E*` as the password.
@ -195,7 +195,7 @@ The module you will develop depends on two of these ABP packages: _@abp/ng.core_
Once all dependencies are installed, follow the steps below to serve your development app:
1. Make sure `.IdentityServer` and `*.HttpApi.Host` projects are up and running.
1. Make sure `.AuthServer` and `*.HttpApi.Host` projects are up and running.
2. Open your terminal at the root folder, i.e. `angular`.
3. Run `yarn start` or `npm start`.

@ -79,4 +79,4 @@ To change the logos and brand color of `LeptonX`, simply add the following CSS t
### Server Side
In order to migrate to LeptonX on your server side projects (Host and/or IdentityServer projects), please follow the [Server Side Migration](mvc.md) document.
In order to migrate to LeptonX on your server side projects (Host and/or AuthServer projects), please follow the [Server Side Migration](mvc.md) document.

@ -16,7 +16,7 @@ LeptonX Lite has implementation for the ABP Framework Blazor WebAssembly & Blazo
## Installation
{{if UI == "Blazor"}}
- Complete the [MVC Razor Pages Installation](mvc.md#installation) for the **HttpApi.Host** application first. _If the solution is tiered/micro-service, complete the MVC steps for all MVC applications such as **HttpApi.Host** and if identity server is separated, install to the **IdentityServer**_.
- Complete the [MVC Razor Pages Installation](mvc.md#installation) for the **HttpApi.Host** application first. _If the solution is tiered/micro-service, complete the MVC steps for all MVC applications such as **HttpApi.Host** and if identity server is separated, install to the **OpenIddict**_.
- Add **Volo.Abp.AspNetCore.Components.WebAssembly.LeptonXLiteTheme** package to your **Blazor WebAssembly** application with the following command:
@ -52,7 +52,7 @@ builder.RootComponents.Add<App>("#ApplicationContainer");
{{if UI == "BlazorServer"}}
- Complete the [MVC Razor Pages Installation](mvc.md#installation) first. _If the solution is tiered/micro-service, complete the MVC steps for all MVC applications such as **HttpApi.Host** and **IdentityServer**_.
- Complete the [MVC Razor Pages Installation](mvc.md#installation) first. _If the solution is tiered/micro-service, complete the MVC steps for all MVC applications such as **HttpApi.Host** and **AuthServer**_.
- Add **Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme** package to your **Blazor server** application with the following command:

@ -110,7 +110,7 @@ export const environment = {
// other options removed for sake of brevity
oAuthConfig: {
issuer: 'https://localhost:44305', // IdentityServer url
issuer: 'https://localhost:44305', // AuthServer url
clientId: 'MyProjectName_App',
dummyClientSecret: '1q2w3e*',
scope: 'offline_access MyProjectName',

@ -0,0 +1,47 @@
# Angular: Global Features API
The `ConfigStateService.getGlobalFeatures` API allows you to get the enabled features of the [Global Features](../../Global-Features.md) on the client side.
> This document only explains the JavaScript API. See the [Global Features](../../Global-Features.md) document to understand the ABP Global Features system.
## Usage
````js
import { ConfigStateService } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core';
@Component({
/* class metadata here */
})
class DemoComponent implements OnInit {
constructor(private config: ConfigStateService) {}
ngOnInit(): void {
// Gets all enabled global features.
const getGlobalFeatures = this.config.getGlobalFeatures();
//Example result is: `{ enabledFeatures: [ 'Shopping.Payment', 'Ecommerce.Subscription' ] }`
// or
this.config.getGlobalFeatures$().subscribe(getGlobalFeatures => {
// use getGlobalFeatures here
})
// Check the global feature is enabled
this.config.getGlobalFeatureIsEnabled('Ecommerce.Subscription')
//Example result is `true`
this.config.getGlobalFeatureIsEnabled('My.Subscription')
//Example result is `false`
// or
this.config.getGlobalFeatureIsEnabled$('Ecommerce.Subscription').subscribe((isEnabled:boolean) => {
// use isEnabled here
})
}
}

@ -0,0 +1,44 @@
# Loading Directive
You may want to block a part of the UI and show a spinner for a while; the `LoadingDirective` directive makes this for you. `LoadingDirective` has been exposed by the `@abp/ng.theme.shared` package.
## Getting Started
In order to use the `LoadingDirective` in an HTML template, the **`ThemeSharedModule`** should be imported into your module like this:
```js
// ...
import { ThemeSharedModule } from '@abp/ng.theme.shared';
@NgModule({
//...
imports: [..., ThemeSharedModule],
})
export class MyFeatureModule {}
```
## Usage
The `LoadingDirective` is easy to use. The directive's selector is **`abpLoading`**. By adding the `abpLoading` attribute to an HTML element, you can activate the `LoadingDirectiveective` for the HTML element when the value is true.
See an example usage:
```html
<div [abpLoading]="true">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Laboriosam commodi quae aspernatur,
corporis velit et suscipit id consequuntur amet minima expedita cum reiciendis dolorum
cupiditate? Voluptas eaque voluptatum odio deleniti quo vel illum nemo accusamus nulla ratione
impedit dolorum expedita necessitatibus fugiat ullam beatae, optio eum cupiditate ducimus
architecto.
</div>
```
The `abpLoading` attribute has been added to the `<div>` element that contains very a long text inside to activate the `LoadingDirective`.
See the result:
![Loading directive result](./images/abp-loading.png)

@ -119,7 +119,7 @@ After the configuration above, if your app runs on the `mytenant1.mydomain.com`,
After this replacement, the app will use the following URLs:
- `https://mytenant1.ids.mydomain.com` as IdentityServer URL.
- `https://mytenant1.ids.mydomain.com` as AuthServer URL.
- `https://mytenant1.api.mydomain.com` as default URL.
- `https://mytenant1.identity.mydomain.com` as `AbpIdentity` remote endpoint URL.

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

@ -16,8 +16,7 @@ The Blazor Server application UI is actually a hybrid application that is combin
{{if UI == "Blazor"}}
* When the Blazor application needs to authenticate, it is redirected to the server side.
* Users can enter username & password to login if they already have an account. If not, they can use the register form to create a new user. They can also use forgot password and other features.,
* Users can enter username & password to login if they already have an account. If not, they can use the register form to create a new user. They can also use forgot password and other features. The server side uses OpenIddict to handle the authentication.
* Finally, they are redirected back to the Blazor application to complete the login process.
This is a typical and recommended approach to implement authentication in Single-Page Applications. The client side configuration is done in the startup template, so you can change it.

@ -13,6 +13,37 @@ Title is used to render page title in the PageHeader.
}
```
## MenuItemName
Indicates current selected menu item name. Menu item name should match a unique menu item name defined using the [Navigation / Menu system](../Blazor/Navigation-Menu.md). In this case, it is expected from the theme to make the menu item "active" in the main menu.
```csharp
@inject PageLayout PageLayout
@code {
protected override async Task OnInitializedAsync()
{
PageLayout.MenuItemName = "MyProjectName.Products";
}
}
```
Menu item name can be set on runtime too.
```html
@inject PageLayout PageLayout
<Button Clicked="SetCategoriesMenuAsSelected">Change Menu</Button>
@code{
protected void SetCategoriesMenuAsSelected()
{
PageLayout.MenuItemName = "MyProjectName.Categories";
}
}
```
> Be aware, The [Basic Theme](../Blazor/Basic-Theme.md) currently doesn't support the selected menu item since it is not applicable to the top menu.
## BreadCrumbs
BreadCrumbItems are used to render breadcrumbs in the PageHeader.
```csharp

@ -989,6 +989,10 @@
"text": "Features",
"path": "UI/Angular/Features.md"
},
{
"text": "Global Features",
"path": "UI/Angular/GlobalFeatures.md"
},
{
"text": "Permission Management",
"path": "UI/Angular/Permission-Management.md"
@ -1042,6 +1046,10 @@
"text": "Confirmation Popup",
"path": "UI/Angular/Confirmation-Service.md"
},
{
"text": "Loading Directive",
"path": "UI/Angular/Loading-Directive.md"
},
{
"text": "Toast Overlay",
"path": "UI/Angular/Toaster-Service.md"
@ -1303,6 +1311,10 @@
{
"text": "IdentityServer",
"path": "Modules/IdentityServer.md"
},
{
"text": "OpenIddict",
"path": "Modules/OpenIddict.md"
},
{
"text": "Permission Management",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 160 KiB

@ -45,7 +45,7 @@ abp new Acme.BookStore
* `mvc`: ASP.NET Core MVC. Existem algumas opções adicionais para este modelo:
* `--tiered`: Cria uma solução em camadas em que as camadas da Web e da API HTTP são fisicamente separadas. Se não especificado, ele cria uma solução em camadas que é menos complexa e adequada para a maioria dos cenários.
* `angular`: Angular. Existem algumas opções adicionais para este modelo:
* `--separate-identity-server`: Separa o aplicativo do servidor de identidade do aplicativo host da API. Se não especificado, você terá um único ponto de extremidade no lado do servidor.
* `--separate-auth-server`: Separa o aplicativo do servidor de identidade do aplicativo host da API. Se não especificado, você terá um único ponto de extremidade no lado do servidor.
* `--database-provider` Ou `-d`: especifica o provedor de banco de dados. O provedor padrão é `ef`. Fornecedores disponíveis:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.

@ -87,7 +87,7 @@ public class CreateBookDto
}
```
有关DTO更的教程,请参见[数据传输对象文档](Entities.md)
有关DTO更的教程,请参见[数据传输对象文档](Entities.md)
### BookAppService(实现)
@ -330,7 +330,7 @@ public class BookAppService :
}
````
`CrudAppService`实现了`ICrudAppService`接口中声明的所有方法. 然后,你可以添加自己的自定义方法或覆盖和自定义实现.
`CrudAppService`实现了`ICrudAppService`接口中声明的所有方法. 然后,你可以添加自己的自定义方法或重写和自定义实现.
> `CrudAppService` 有不同数量泛型参数的版本,你可以选择适合的使用.
@ -380,4 +380,4 @@ public class DistrictKey
### 生命周期
应用服务的生命周期是[transient](Dependency-Injection)的,它们会自动注册到依赖注入系统.
应用服务的生命周期是[transient](Dependency-Injection)的,它们会自动注册到依赖注入系统.

@ -96,29 +96,29 @@ public class MyLogWorker : HangfireBackgroundWorkerBase, IMyLogWorker
## 注册到后台工作者管理器
创建一个后台工作者后, 你应该添加到 `IBackgroundWorkerManager`, 最常用的地方是在你模块类的 `OnApplicationInitialization` 方法中:
创建一个后台工作者后, 你应该添加到 `IBackgroundWorkerManager`, 最常用的地方是在你模块类的 `OnApplicationInitializationAsync` 方法中:
```` csharp
[DependsOn(typeof(AbpBackgroundWorkersModule))]
public class MyModule : AbpModule
{
public override void OnApplicationInitialization(
public override async Task OnApplicationInitializationAsync(
ApplicationInitializationContext context)
{
context.AddBackgroundWorker<MyLogWorker>();
await context.AddBackgroundWorkerAsync<MyLogWorker>();
//如果定义了接口
//context.AddBackgroundWorker<IMyLogWorker>();
//await context.AddBackgroundWorkerAsync<IMyLogWorker>();
}
}
````
`context.AddBackgroundWorker(...)` 是一个是以下代码快捷的扩展方法:
`context.AddBackgroundWorkerAsync(...)` 是一个是以下代码快捷的扩展方法:
```` csharp
context.ServiceProvider
.GetRequiredService<IBackgroundWorkerManager>()
.Add(
.AddAsync(
context
.ServiceProvider
.GetRequiredService<MyLogWorker>()
@ -127,4 +127,4 @@ context.ServiceProvider
它解析给定的后台工作者并添加到 `IBackgroundWorkerManager`.
虽然我们通常在 `OnApplicationInitialization` 中添加后台工作者, 但对此没有限制. 你可以在任何地方注入 `IBackgroundWorkerManager` 并在运行时添加后台工作者.
虽然我们通常在 `OnApplicationInitializationAsync` 中添加后台工作者, 但对此没有限制. 你可以在任何地方注入 `IBackgroundWorkerManager` 并在运行时添加后台工作者.

@ -78,26 +78,26 @@ public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase
## 注册后台工作者
创建一个后台工作者后,你应该将其添加到 `IBackgroundWorkerManager`. 最常见的地方是模块类的 `OnApplicationInitialization` 方法:
创建一个后台工作者后,你应该将其添加到 `IBackgroundWorkerManager`. 最常见的地方是模块类的 `OnApplicationInitializationAsync` 方法:
````csharp
[DependsOn(typeof(AbpBackgroundWorkersModule))]
public class MyModule : AbpModule
{
public override void OnApplicationInitialization(
public override async Task OnApplicationInitialization(
ApplicationInitializationContext context)
{
context.AddBackgroundWorker<PassiveUserCheckerWorker>();
await context.AddBackgroundWorkerAsync<PassiveUserCheckerWorker>();
}
}
````
`context.AddBackgroundWorker(...)` 是以下代码的简化扩展方法:
`context.AddBackgroundWorkerAsync(...)` 是以下代码的简化扩展方法:
````csharp
context.ServiceProvider
.GetRequiredService<IBackgroundWorkerManager>()
.Add(
.AddAsync(
context
.ServiceProvider
.GetRequiredService<PassiveUserCheckerWorker>()
@ -106,7 +106,7 @@ context.ServiceProvider
所以,它解析了给定的后台工作者并添加到 `IBackgroundWorkerManager`.
如果我们通常在 `OnApplicationInitialization` 添加工作者,但并不是强制的. 你可以在应用程序的任何地方注入 `IBackgroundWorkerManager` 并在运行时添加工作者. 在你的应用程序关闭时Background worker manager会释放所有已注册的后台工作者.
如果我们通常在 `OnApplicationInitializationAsync` 添加工作者,但并不是强制的. 你可以在应用程序的任何地方注入 `IBackgroundWorkerManager` 并在运行时添加工作者. 在你的应用程序关闭时Background worker manager会释放所有已注册的后台工作者.
## Options

@ -0,0 +1,248 @@
# ABP CLI - 新解决方案命令示例
`abp new`命令基于abp模板创建abp解决方案或其他组件. [ABP CLI](CLI.md)有一些参数可以用于创建新的ABP解决方案. 在本文档中, 我们将向你展示一些创建新的解决方案的命令示例. 所有的项目名称都是`Acme.BookStore`. 目前, 唯一可用的移动端项目是`React Native`移动端应用程序. 可用的数据库提供程序有`Entity Framework Core`和`MongoDB`. 所有命令都以`abp new`开头.
## Angular
以下命令用于创建Angular UI项目:
* 在新文件夹中创建项目, **Entity Framework Core**, 非移动端应用程序:
````bash
abp new Acme.BookStore -u angular --mobile none --database-provider ef -csf
````
* 在新文件夹中创建项目, **Entity Framework Core**, 默认应用程序模板, **拆分Identity Server**:
```bash
abp new Acme.BookStore -t app -u angular -m none --separate-auth-server --database-provider ef -csf
```
* 在新文件夹中创建项目, **Entity Framework Core**, **自定义连接字符串**:
```bash
abp new Acme.BookStore -u angular -csf --connection-string Server=localhost;Database=MyDatabase;Trusted_Connection=True
```
* 在`C:\MyProjects\Acme.BookStore`中创建解决方案, **MongoDB**, 默认应用程序模板, 包含移动端项目:
```bash
abp new Acme.BookStore -u angular --database-provider mongodb --output-folder C:\MyProjects\Acme.BookStore
```
* 在新文件夹中创建项目, **MongoDB**, 默认应用程序模板, 不创建移动端应用程序, **拆分Identity Server**:
```bash
abp new Acme.BookStore -t app -u angular -m none --separate-auth-server --database-provider mongodb -csf
```
## MVC
以下命令用于创建MVC UI项目:
* 在新文件夹中创建项目, **Entity Framework Core**, 不创建移动端应用程序:
```bash
abp new Acme.BookStore -t app -u mvc --mobile none --database-provider ef -csf
```
* 在新文件夹中创建项目, **Entity Framework Core**, **分层结构** (*Web和HTTP API层是分开的*), 不创建移动端应用程序:
```bash
abp new Acme.BookStore -u mvc --mobile none --tiered --database-provider ef -csf
```
* 在新文件夹中创建项目, **MongoDB**, 不创建移动端应用程序:
```bash
abp new Acme.BookStore -t app -u mvc --mobile none --database-provider mongodb -csf
```
* 在新文件夹中创建项目, **MongoDB**, **分层结构**:
```bash
abp new Acme.BookStore -u mvc --tiered --database-provider mongodb -csf
```
## Blazor
以下命令用于创建Blazor项目:
* **Entity Framework Core**, 不创建移动端应用程序:
```bash
abp new Acme.BookStore -t app -u blazor --mobile none
```
* **Entity Framework Core**, **拆分Identity Server**, 包含移动端应用程序:
```bash
abp new Acme.BookStore -u blazor --separate-auth-server
```
* 在新文件夹中创建项目, **MongoDB**, 不创建移动端应用程序:
```bash
abp new Acme.BookStore -u blazor --database-provider mongodb --mobile none -csf
```
## Blazor Server
以下命令用于创建Blazor项目:
* **Entity Framework Core**, 不创建移动端应用程序:
```bash
abp new Acme.BookStore -t app -u blazor-server --mobile none
```
* **Entity Framework Core**, **拆分Identity Server**, **拆分API Host**, 包含移动端应用程序:
```bash
abp new Acme.BookStore -u blazor-server --tiered
```
* 在新文件夹中创建项目, **MongoDB**, 不创建移动端应用程序:
```bash
abp new Acme.BookStore -u blazor --database-provider mongodb --mobile none -csf
```
## 无UI
在默认应用程序模板中, 始终有一个前端项目. 在这个选项中没有前端项目. 它有一个`HttpApi.Host`项目为你的HTTP WebAPI提供服务. 这个选项适合在你想创建一个WebAPI服务时使用.
* 在新文件夹中创建项目, **Entity Framework Core**, 拆分Identity Server:
```bash
abp new Acme.BookStore -u none --separate-auth-server -csf
```
* **MongoDB**, 不创建移动端应用程序:
```bash
abp new Acme.BookStore -u none --mobile none --database-provider mongodb
```
## 控制台应用程序
这是一个基于.NET控制台应用程序的模板, 集成了ABP模块架构. 要创建控制台应用程序, 请使用以下命令:
* 项目由以下文件组成: `Acme.BookStore.csproj`, `appsettings.json`, `BookStoreHostedService.cs`, `BookStoreModule.cs`, `HelloWorldService.cs``Program.cs`.
```bash
abp new Acme.BookStore -t console -csf
```
## 模块
模块是主项目使用的可重用子应用程序. 如果你正在构建微服务解决方案, 使用ABP模块是最佳方案. 由于模块不是最终的应用程序, 每个模块都有前端UI项目和数据库提供程序. 模块模板带有MVC UI, 可以在没有最终解决方案的情况下进行开发. 但是, 如果要在最终解决方案下开发模块, 可以添加`--no-ui`参数来去除MVC UI项目.
* 包含前端: `MVC`, `Angular`, `Blazor`. 包含数据库提供程序: `Entity Framework Core`, `MongoDB`. 包含MVC启动项目.
```bash
abp new Acme.IssueManagement -t module
```
* 与上面相同, 但不包括MVC启动项目.
```bash
abp new Acme.IssueManagement -t module --no-ui
```
* 创建模块并将其添加到解决方案中
```bash
abp new Acme.IssueManagement -t module --add-to-solution-file
```
## 从特定版本创建解决方案
创建解决方案时, 它总是使用最新版本创建. 要从旧版本创建项目, 可以使用`--version`参数.
* 使用v3.3.0版本创建解决方案, 包含Angular UI和Entity Framework Core.
```bash
abp new Acme.BookStore -t app -u angular -m none --database-provider ef -csf --version 3.3.0
```
要获取ABP版本列表, 请查看以下链接: https://www.nuget.org/packages/Volo.Abp.Core/
## 从自定义模板创建
ABP CLI使用默认的[应用程序模板](https://github.com/abpframework/abp/tree/dev/templates/app)创建项目. 如果要从自定义模板创建新的解决方案, 可以使用参数`--template-source`.
* 在`c:\MyProjects\templates\app`目录中使用模板, MVC UI, Entity Framework Core, 不创建移动端应用程序.
```bash
abp new Acme.BookStore -t app -u mvc --mobile none --database-provider ef --template-source "c:\MyProjects\templates\app"
```
* 除了此命令从URL `https://myabp.com/app-template.zip` 检索模板之外, 与上一个命令相同.
```bash
abp new Acme.BookStore -t app -u mvc --mobile none --database-provider ef --template-source https://myabp.com/app-template.zip
```
## 创建预览版本
ABP CLI始终使用最新版本. 要从预览(RC)版本创建解决方案, 请添加`--preview`参数.
* 在新文件夹中创建项目, Blazor UI, Entity Framework Core, 不创建移动端应用程序, **使用最新版本**:
```bash
abp new Acme.BookStore -t app -u blazor --mobile none -csf --preview
```
## 选择数据库管理系统
默认的数据库管理系统是 `Entity Framework Core` / ` SQL Server`. 你可以通过使用`--database-management-system`参数选择DBMS. [可用的值](https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/DatabaseManagementSystem.cs) 包括 `SqlServer`, `MySQL`, `SQLite`, `Oracle`, `Oracle-Devart`, `PostgreSQL`. 默认值是 `SqlServer`.
* 在新文件夹中创建项目, Angular UI, **PostgreSQL** 数据库:
```bash
abp new Acme.BookStore -u angular --database-management-system PostgreSQL -csf
```
## 使用静态HTTP端口
ABP CLI始终为项目分配随机端口. 如果需要保留默认端口并且创建解决方案始终使用相同的HTTP端口, 请添加参数`--no-random-port`.
* 在新文件夹中创建项目, MVC UI, Entity Framework Core, **静态端口**:
```bash
abp new Acme.BookStore --no-random-port -csf
```
## 引用本地ABP框架
在ABP解决方案中, 默认情况下从NuGet引用ABP库. 有时, 你需要在本地将ABP库引用到你的解决方案中. 这利于调试框架本身. 本地ABP框架的根目录必须有`Volo.Abp.sln`文件. 你可以将以下目录的内容复制到你的文件系统中
* MVC UI, Entity Framework Core, **引用本地的ABP库**:
本地路径必须是ABP存储库的根目录.
如果`C:\source\abp\framework\Volo.Abp.sln`是你的框架解决方案的路径, 那么你必须设置`--abp-path`参数值为`C:\source\abp`.
```bash
abp new Acme.BookStore --local-framework-ref --abp-path C:\source\abp
```
**输出**:
如下所示, 引用本地ABP框架库项目.
```xml
<ItemGroup>
<ProjectReference Include="C:\source\abp\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="C:\source\abp\framework\src\Volo.Abp.AspNetCore.Serilog\Volo.Abp.AspNetCore.Serilog.csproj" />
<ProjectReference Include="C:\source\abp\framework\src\Volo.Abp.AspNetCore.Authentication.JwtBearer\Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj" />
<ProjectReference Include="..\Acme.BookStore.Application\Acme.BookStore.Application.csproj" />
<ProjectReference Include="..\Acme.BookStore.HttpApi\Acme.BookStore.HttpApi.csproj" />
<ProjectReference Include="..\Acme.BookStore.EntityFrameworkCore\Acme.BookStore.EntityFrameworkCore.csproj" />
</ItemGroup>
```
## 另请参阅
* [ABP CLI文档](CLI.md)

@ -79,6 +79,8 @@ abp new Acme.BookStore
* Acme.BookStore是解决方案的名称.
* 常见的命名方式类似于 *YourCompany.YourProject*. 不过你可以使用自己喜欢的方式,如 *YourProject* (单级命名空间) 或 *YourCompany.YourProduct.YourModule* (三级命名空间).
参阅[ABP CLI 创建新解决方案示例](CLI-New-Command-Samples.md)查看更多示例.
#### Options
* `--template` 或者 `-t`: 指定模板. 默认的模板是 `app`,会生成web项目.可用的模板有:
@ -87,11 +89,11 @@ abp new Acme.BookStore
* `mvc`: ASP.NET Core MVC.此模板的其他选项:
* `--tiered`: 创建分层解决方案,Web和Http Api层在物理上是分开的.如果未指定会创建一个分层的解决方案,此解决方案没有那么复杂,适合大多数场景.
* `angular`: Angular. 这个模板还有一些额外的选项:
* `--separate-identity-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点.
* `--separate-auth-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点.
* `blazor`: Blazor. 这个模板还有一些额外的选项:
* `--separate-identity-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点.
* `--separate-auth-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点.
* `none`: 无UI. 这个模板还有一些额外的选项:
* `--separate-identity-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点.
* `--separate-auth-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点.
* `--mobile` 或者 `-m`: 指定移动应用程序框架. 如果未指定,则不会创建任何移动应用程序,其他选项:
* `none`: 不包含移动应用程序.
* `react-native`: React Native.

@ -32,11 +32,11 @@
领域驱动设计(DDD)是一种将实现与**持续进化**的模型连接在一起来满足**复杂**需求的软件开发方法.
DDD适用于**复杂领域**或**较大规模**的系统,而不是简单的CRUD程序.它着重**核心领域逻辑**,而不是基础架构.这样有助于构建一个**灵活**,模块化,**可维护**的代码库.
DDD适用于**复杂领域**或**较大规模**的系统,而不是简单的CRUD程序.它着重**核心领域逻辑**,而不是基础架构.这样有助于构建一个**灵活**,模块化,**可维护**的代码库.
### OOP & SOLID
实现DDD高度依赖面对象编程思想(OOP)和[SOLID](https://zh.wikipedia.org/wiki/SOLID_(%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%AE%BE%E8%AE%A1))原则.事实上,DDD已经**实现**并**延伸**了这些原则,因此,**深入了解**OOP和SOLID对实施DDD十分有利.
实现DDD高度依赖面对象编程思想(OOP)和[SOLID](https://zh.wikipedia.org/wiki/SOLID_(%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%AE%BE%E8%AE%A1))原则.事实上,DDD已经**实现**并**延伸**了这些原则,因此,**深入了解**OOP和SOLID对实施DDD十分有利.
### DDD分层与整洁架构
@ -154,7 +154,7 @@ ABP的启动解决方案中包含两个用于集成Entity Framework Core的项
* `Application` 依赖`Application.Contracts`项目,因为此项目需要实现应用服务的接口及接口使用的DTO.另外也依赖`Domain`项目,因为应用服务的实现必须依赖领域层中的对象.
* `EntityFrameworkCore` 依赖`Domain`项目,因为此项目需要将领域对象(实体或值对象)映射到数据库的表,另外还需要实现`Domain`项目中的仓储接口.
* `HttpApi` 依赖`Application.Contracts`项目,因为Controllers需要注入应用服务.
* `HttpApi.Client` 依赖`Application.Contracts`项目,因为此项目需要使用应用服务.
* `HttpApi.Client` 依赖`Application.Contracts`项目,因为此项目需要使用应用服务.
* `Web` 依赖`HttpApi`项目,因为此项目对外提供HTTP APIs.另外Pages或Components 需要使用应用服务,所以还间接依赖了`Application.Contracts`项目
#### 虚线依赖
@ -198,7 +198,7 @@ ABP的启动解决方案中包含两个用于集成Entity Framework Core的项
##### 关于数据库独立原则的讨论
**原因1**会非常影响你**领域对象的建模**(特别是实体间的关系)及**应用程序的代码**.假如,开始选择了关系型数据库,并使用了[Entity Framework Core](Entity-Framework-Core.md),后面尝试切换到[MongoDB](MongoDB.md),那么 **EF Core 中一些非常用的特性**你就不能使用了,例如:
**原因1**会非常影响你**领域对象的建模**(特别是实体间的关系)及**应用程序的代码**.假如,开始选择了关系型数据库,并使用了[Entity Framework Core](Entity-Framework-Core.md),后面尝试切换到[MongoDB](MongoDB.md),那么 **EF Core 中一些非常用的特性**你就不能使用了,例如:
* 无法使用[变更追踪](https://docs.microsoft.com/zh-cn/ef/core/querying/tracking) ,因为*MongoDB provider*没有提供此功能,因此,你始终需要显式的更新已变更的实体.
* 无法在不同的聚合间使用[导航属性](https://docs.microsoft.com/zh-cn/ef/core/modeling/relationships),因为文档型数据库是不支持的.有关更多信息,请参见"规则:聚合间仅通过Id关联".
@ -209,7 +209,7 @@ ABP的启动解决方案中包含两个用于集成Entity Framework Core的项
#### 展现层技术无关原则
展现层技术(UI框架)现代应用程序中最多变的部分之一.**领域层和应用层**应该对展现层所采用的技术或框架**一无所知**.使用ABP启动模板就非常容易实现此原则.
展现层技术(UI框架)现代应用程序中最多变的部分之一.**领域层和应用层**应该对展现层所采用的技术或框架**一无所知**.使用ABP启动模板就非常容易实现此原则.
在某些情况下,你可能需要在应用层和展现层中写重复的逻辑,例如,参数验证和授权检查.展现层检查出于**用户体验**,应用层或领域层检查出于**数据安全性**和**数据完整性**.
@ -259,7 +259,7 @@ DDD忽略**领域对象的数据展示**,这并不意味着它们并不重要,
这样做的原因是我们需要执行业务规则来保证数据的一致性和完整性.假如有一个业务规则:"用户不能对已锁定的问题进行评论".那如何在不查询数据库的情况下,获取问题是否已被锁定?所以,只有关联的对象都被加载了的时候,我们才可以执行业务规则.
另外,使用**MongoDB**的开发人员就认为此原则很好理解.在MongoDB中,聚合对象(包含子集合)会被保存到一个`collection`中.因而,无需任何其它配置,就可以实现查询一个聚合,同时所有子对象.
另外,使用**MongoDB**的开发人员就认为此原则很好理解.在MongoDB中,聚合对象(包含子集合)会被保存到一个`collection`中.因而,无需任何其它配置,就可以实现查询一个聚合,同时包含所有子对象.
ABP框架有助于你实现这一原则
@ -293,7 +293,7 @@ public class IssueAppService : ApplicationService, IIssueAppService
最后,我们使用`_issueRepository.UpdateAsync`方法,将对象保存到数据库中.
> EF Core 具有**变更追踪**的功能,因此,不需要调用`_issueRepository.UpdateAsync`方法.ABP的工作单元会方法结束时,自动执行`DbContext.SaveChanges()`的.如果使用MongoDB则需要显式手动调用.
> EF Core 具有**变更追踪**的功能,因此,不需要调用`_issueRepository.UpdateAsync`方法.ABP的工作单元会方法结束时,自动执行`DbContext.SaveChanges()`的.如果使用MongoDB则需要显式手动调用.
>
> 因此,当需要额外编写仓储层的实现,应该在实体变化时始终调用 `UpdateAsync` 方法.
@ -375,7 +375,7 @@ MongoDB中不适合使用导航属性或集合的,原因是:当前源聚合对
并不是所有的子集合的主键都是联合主键,有些情况下,可以使用单独的`Id`作为主键.
> 联合主键实际上关系型数据库中的概念,因为子集合对象有与之对应的数据库表,而表也要有主键.但是在非关系型数据库中,无需为子集合实体定义主键,因为它们本身就已属于一个聚合根.
> 联合主键实际上关系型数据库中的概念,因为子集合对象有与之对应的数据库表,而表也要有主键.但是在非关系型数据库中,无需为子集合实体定义主键,因为它们本身就已属于一个聚合根.
##### 聚合根 / 实体的构造函数
@ -1017,7 +1017,7 @@ public class IssueAppService : ApplicationService, IIssueAppService
### 领域服务
领域服务主要来实现本领域的逻辑:
领域服务主要来实现本领域的逻辑:
* 依赖**服务和仓储**.
* 需要使用多个聚合.
@ -1276,7 +1276,7 @@ public class UserChangePasswordDto
虽然编写了更多的代码,但是这样可维护性更高.
**例外情况:**该规则有一些例外的情况,例如,你想开发两个方法,它们共用相同的输入DTO(通过继承或重用),有一个报表页面有多个过滤条件,多个应用服务使用相同的输入参数返回不同的结果(如,大屏展示数据,Excel报表,csv报表).这种情况下,你是需要修改一个参数,多个应用服务都应该一起被修改.
**例外情况:** 该规则有一些例外的情况,例如,你想开发两个方法,它们共用相同的输入DTO(通过继承或重用),有一个报表页面有多个过滤条件,多个应用服务使用相同的输入参数返回不同的结果(如,大屏展示数据,Excel报表,csv报表).这种情况下,你是需要修改一个参数,多个应用服务都应该一起被修改.
##### 输入DTO中验证逻辑
@ -1635,7 +1635,7 @@ public class IssueCreationDto
因为,应用服务可能在保存`Issue`对象之前,需要对其它对象进行修改.如果领域服务执行了保存,那么*保存*操作就是重复的.
* 会触发两次数据库交互,这会导致性能损失.
* 会触发两次数据库交互,这会导致性能损失.
* 需要额外添加显式的事务来包含这两个操作,才能保证数据一致性.
* 如果因为业务规则取消了实体的创建,则应该在数据库事务中回滚事务,取消所有操作.
@ -1966,7 +1966,7 @@ public class IssueAppService
* 如果没有**任何业务逻辑**,只有简单的**CRUD**操作,**请勿**创建领域服务.
* **切勿**将**DTO**传递给领域服务,或从领域服务返回**DTO**.
可以在应用服务中直接注入仓储,实现查询,创建,更新及删除操作.除非在这些操作过程中需要执行某些业务逻辑,在这种情况下,请创建领域服务.
可以在应用服务中直接注入仓储,实现查询,创建,更新及删除操作.除非在这些操作过程中需要执行某些领域逻辑,在这种情况下,请创建领域服务.
> 不要创建"将来可能需要"这种CRUD领域服务方法([YAGNI](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it)),在需要时重构它并重构现有代码. 由于应用层优雅地抽象了领域层,因此重构过程不会影响UI层和其他客户端.

@ -116,7 +116,6 @@ namespace MyProject.Issues
## 应用程序服务与领域服务
虽然应用服务和领域服务都实现了业务规则,但存在根本的逻辑和形式差异;
虽然 [应用服务](Application-Services.md) 和领域服务都实现了业务规则,但存在根本的逻辑和形式差异:
* 应用程序服务实现应用程序的 **用例** (典型 Web 应用程序中的用户交互), 而领域服务实现 **核心的、用例独立的领域逻辑**.

@ -28,7 +28,7 @@ public class Book : Entity<Guid>
* 创建一个构造函数,获取ID作为参数传递给基类.
* 如果没有为GUID Id赋值,**ABP框架会在保存时设置它**,但是在将实体保存到数据库之前最好在实体上有一个有效的Id.
* 如果使用带参数的构造函数创建实体,那么还要创建一个 `private``protected` 构造函数. 当数据库提供程序从数据库读取你的实体时(反序列化时)将使用它.
* 不要使用 `Guid.NewGuid()` 来设置Id! 在创建实体的代码中**使用[`IGuidGenerator`服务](Guid-Generation.md)**传递Id参数. `IGuidGenerator`经过优化可以产生连续的GUID.这对于关系数据库中的聚集索引非常重要.
* 不要使用 `Guid.NewGuid()` 来设置Id! 在创建实体的代码中**使用[`IGuidGenerator`服务](Guid-Generation.md)** 传递Id参数. `IGuidGenerator`经过优化可以产生连续的GUID.这对于关系数据库中的聚集索引非常重要.
示例实体:
@ -70,7 +70,7 @@ public class BookAppService : ApplicationService, IBookAppService
}
````
* `BookAppService` 注入图书实体的默认[仓](Repositories.md),使用`InsertAsync`方法插入 `Book` 到数据库中.
* `BookAppService` 注入图书实体的默认[仓](Repositories.md),使用`InsertAsync`方法插入 `Book` 到数据库中.
* `GuidGenerator`类型是 `IGuidGenerator`,它是在`ApplicationService`基类中定义的属性. ABP将这样常用属性预注入,所以不需要手动[注入](Dependency-Injection.md).
* 如果你想遵循DDD最佳实践,请参阅下面的*聚合示例*部分.
@ -373,7 +373,7 @@ public static class IdentityUserExtensions
* 对于 [Entity Framework Core](Entity-Framework-Core.md),这是两种类型的配置;
* 默认它以 `JSON` 字符串形式存储在 `ExtraProperties` 字段中. 序列化到 `JSON` 和反序列化到 `JSON` 由ABP使用EF Core的[值转换](https://docs.microsoft.com/zh-cn/ef/core/modeling/value-conversions)系统自动完成.
* 如果需要,你可以使用 `ObjectExtensionManager` 为所需的额外属性定义一个单独的数据库字段. 那些使用 `ObjectExtensionManager` 配置的属性继续使用单个 `JSON` 字段. 当你使用预构建的[应用模块](Modules/Index.md)并且想要[扩展模块的实体](Customizing-Application-Modules-Extending-Entities.md). 参阅[EF Core迁移文档](Entity-Framework-Core.md)了解如何使用 `ObjectExtensionManager`.
* 如果需要,你可以使用 `ObjectExtensionManager` 为所需的额外属性定义一个单独的数据库字段. 使用 `ObjectExtensionManager` 配置的属性继续使用单个 `JSON` 字段. 当你使用预构建的[应用模块](Modules/Index.md)并且想要[扩展模块的实体](Customizing-Application-Modules-Extending-Entities.md). 参阅[EF Core迁移文档](Entity-Framework-Core.md)了解如何使用 `ObjectExtensionManager`.
* 对于 [MongoDB](MongoDB.md), 它以 **常规字段** 存储, 因为 MongoDB 天生支持这种 [额外](https://mongodb.github.io/mongo-csharp-driver/1.11/serialization/#supporting-extra-elements) 系统.
### 讨论额外的属性

@ -20,7 +20,7 @@
使用 ABP CLI 的 `new` 命令创建一个新项目:
````shell
abp new Acme.BookStore{{if UI == "NG"}} -u angular{{else if UI == "Blazor"}} -u blazor{{else if UI == "BlazorServer"}} -u blazor-server{{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes"}}{{if UI == "MVC" || UI == "BlazorServer"}} --tiered{{else}} --separate-identity-server{{end}}{{end}}
abp new Acme.BookStore{{if UI == "NG"}} -u angular{{else if UI == "Blazor"}} -u blazor{{else if UI == "BlazorServer"}} -u blazor-server{{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes"}}{{if UI == "MVC" || UI == "BlazorServer"}} --tiered{{else}} --separate-auth-server{{end}}{{end}}
````
*你可以使用不同级别的命名空间, 例如: BookStore、Acme.BookStore或 Acme.Retail.BookStore.*
@ -33,7 +33,7 @@ abp new Acme.BookStore{{if UI == "NG"}} -u angular{{else if UI == "Blazor"}} -u
{{ else }}
* `--separate-identity-server` 参数用于将Identity Server应用程序与API主机应用程序分隔开. 如果未指定, 则服务器上将只有一个端点.
* `--separate-auth-server` 参数用于将Identity Server应用程序与API主机应用程序分隔开. 如果未指定, 则服务器上将只有一个端点.
{{ end }}

@ -18,8 +18,8 @@ ABP平台提供了[React Native](https://reactnative.dev/)模板用于开发移
![React Native tiered project local IP entry](images/rn-tiered-local-ip.png)
* 打开 `.IdentityServer` 文件夹下的 `appsettings.json` 文件. 将 `SelfUrl` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.IdentityServer/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.AuthServer` 文件夹下的 `appsettings.json` 文件. 将 `SelfUrl` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.AuthServer/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.HttpApi.Host` 文件夹下的 `appsettings.json` 文件. 将 `Authority` 属性的 `localhost` 替换为你本地的IP地址.
* 打开 `.HttpApi.Host/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址.
@ -41,7 +41,7 @@ yarn
{{ if Tiered == "Yes" }}
> 确保 `issuer` 与正在运行的 `.IdentityServer` 项目匹配, `apiUrl` 与正在运行的 `.HttpApi.Host` 项目匹配.
> 确保 `issuer` 与正在运行的 `.AuthServer` 项目匹配, `apiUrl` 与正在运行的 `.HttpApi.Host` 项目匹配.
{{else}}

@ -15,7 +15,7 @@
### 连接字符串
检查在 {{if Tiered == "Yes"}}`.IdentityServer` 和`.HttpApi.Host` 项目{{else}}{{if UI=="MVC"}}`.Web` 项目{{else if UI=="BlazorServer"}}`.Blazor` 项目{{else}}`.HttpApi.Host` 项目{{end}}{{end}} 中 `appsettings.json` 文件里的**连接字符串**.
检查在 {{if Tiered == "Yes"}}`.AuthServer` 和`.HttpApi.Host` 项目{{else}}{{if UI=="MVC"}}`.Web` 项目{{else if UI=="BlazorServer"}}`.Blazor` 项目{{else}}`.HttpApi.Host` 项目{{end}}{{end}} 中 `appsettings.json` 文件里的**连接字符串**.
{{ if DB == "EF" }}
@ -97,7 +97,7 @@ dotnet run
> 分层解决方案使用 **Redis** 作为分布式缓存. 确保它已安装并在本地计算机上运行. 如果你使用的是远程 Redis 服务器, 请修改项目的 ` appsettings.json` 文件中的配置.
1. 确保 `.IdentityServer` 项目是启动项目. 运行此应用程序, 它将在浏览器中打开 **登录** 页面.
1. 确保 `.AuthServer` 项目是启动项目. 运行此应用程序, 它将在浏览器中打开 **登录** 页面.
> 在 Visual Studio 中使用 Ctrl+F5(而不是F5) 在不进行调试的情况下运行应用程序. 如果你没有调试目的, 这会更快.
@ -135,7 +135,7 @@ dotnet run
> 分层解决方案使用 Redis 作为分布式缓存. 确保它已安装并在本地计算机上运行. 如果你使用的是远程 Redis 服务器, 请修改项目的 `appsettings.json` 文件中的配置.
确保 `.IdentityServer` 项目是启动项目. 运行此应用程序, 它将在浏览器中打开 **登录** 页面.
确保 `.AuthServer` 项目是启动项目. 运行此应用程序, 它将在浏览器中打开 **登录** 页面.
> 在 Visual Studio 中使用 Ctrl+F5(而不是F5) 在不进行调试的情况下运行应用程序. 如果你没有调试目的, 这会更快.

@ -12,7 +12,7 @@
#### 添加NuGet包
添加[Microsoft.AspNetCore.Authentication.Facebook]包到你的项目. 基于你的架构,可能是 `.Web`,`.IdentityServer`(对于分层启动)或 `.Host` 项目.
添加[Microsoft.AspNetCore.Authentication.Facebook]包到你的项目. 基于你的架构,可能是 `.Web`,`.AuthServer`(对于分层启动)或 `.Host` 项目.
#### 配置提供程序

@ -18,6 +18,7 @@ ABP是一个 **模块化的应用程序框架** 由十多个 **NuGet & NPM packa
* [**Docs**](Docs.md): 用于创建技术文档页面. ABP的[文档](https://abp.io/documents/) 就使用了此模块.
* [**Feature Management**](Feature-Management.md): 用于保存和管理功能.
* [**Identity**](Identity.md): 基于Microsoft Identity管理组织单元,角色,用户和他们的权限.
* [**OpenIddict**](OpenIddict.md): 集成了OpenIddict.
* [**Identity Server**](IdentityServer.md): 集成了IdentityServer4.
* [**Permission Management**](Permission-Management.md): 用于保存权限.
* [**Setting Management**](Setting-Management.md): 用于保存设置.

@ -239,15 +239,15 @@ ABP有[动态 C# API 客户端](../API/Dynamic-CSharp-API-Clients.md)功能,所
![bookstore-visual-studio-solution-tiered](../images/bookstore-visual-studio-solution-tiered.png)
与默认结构不同,我们得到了两个新项目: `.IdentityServer` 和 `.HttpApi.Host`.
与默认结构不同,我们得到了两个新项目: `.AuthServer` 和 `.HttpApi.Host`.
#### .IdentityServer 项目
#### .AuthServer 项目
用于其他项目的身份验证服务器. `.Web`项目使用OpenId Connect身份验证从IdentityServer获取当前用户的身份和访问令牌. 然后使用访问令牌调用HTTP API服务器. HTTP API服务器使用bearer token从访问令牌获取声明授权当前用户.
用于其他项目的身份验证服务器. `.Web`项目使用OpenId AuthServer. 然后使用访问令牌调用HTTP API服务器. HTTP API服务器使用bearer token从访问令牌获取声明授权当前用户.
![tiered-solution-applications](../images/tiered-solution-applications.png)
ABP使用开源的[IdentityServer4](https://identityserver.io/)框架做应用程序间的身份验证. 有关IdentityServer4和OpenId Connect协议的详细信息请参阅[IdentityServer4文档](http://docs.identityserver.io).
ABP使用开源的[IdentityServer4](https://identityserver.io/) 和 [OpenIddict](https://github.com/openiddict/) 框架做应用程序间的身份验证. 有关IdentityServer4,OpenIddict和OpenId Connect协议的详细信息请参阅[IdentityServer4文档](http://docs.identityserver.io) [OpenIddict文档](https://documentation.openiddict.com/).
它有自己的`appsettings.json`文件(数据库连接字符串等其他配置).
@ -269,7 +269,7 @@ ABP使用开源的[IdentityServer4](https://identityserver.io/)框架做应用
你应该按照以下顺序运行应用:
* 首先运行`.IdentityServer`,因为其他应用程序依赖它做身份验证.
* 首先运行`.AuthServer`,因为其他应用程序依赖它做身份验证.
* 然后运行`.HttpApi.Host`,因为`.Web`应用程序需要访问HTTI API.
* 最后运行`.Web`并登录到应用程序(用户名: `admin` 密码: `1q2w3E*`).

@ -139,7 +139,7 @@ abp new Acme.IssueManagement -t module --no-ui
在这个场景中,有三个应用程序;
* `.IdentityServer` 应用程序是其他应用程序使用的身份验证服务器,它有自己的 `appsettings.json` 包含数据库连接字符串和其他配置.
* `.AuthServer` 应用程序是其他应用程序使用的身份验证服务器,它有自己的 `appsettings.json` 包含数据库连接字符串和其他配置.
* `.HttpApi.Host` 托管模块的HTTP API. 它有自己的 `appsettings.json` 包含数据库连接字符串和其他配置.
* `.Web.Host` 托管模块的UI. 它包含 `appsettings.json` 文件, 但是其中没有数据库连接字符串, 因为它不需要连接到数据库, 它主要调用远程API服务器和身份认证服务器.
@ -147,7 +147,7 @@ abp new Acme.IssueManagement -t module --no-ui
![tiered-solution-applications](../images/tiered-solution-applications.png)
`.Web.Host` 项目使用OpenId Connect身份认证从`.IdentityServer`获取当前用户的身份和访问令牌. 然后使用访问令牌调用 `.HttpApi.Host`. HTTP API 服务器使用bearer token验证访问令牌获取当前用户声明并授权用户.
`.Web.Host` 项目使用OpenId Connect身份认证从`.AuthServer`获取当前用户的身份和访问令牌. 然后使用访问令牌调用 `.HttpApi.Host`. HTTP API 服务器使用bearer token验证访问令牌获取当前用户声明并授权用户.
##### 前置条件
@ -157,6 +157,6 @@ abp new Acme.IssueManagement -t module --no-ui
你需要按照以下顺序运行应用程序:
- 首先, 运行 `.IdentityServer`,因为其他应用程序依赖它做身份认证.
- 首先, 运行 `.AuthServer`,因为其他应用程序依赖它做身份认证.
- 然后运行 `.HttpApi.Host`,因为`.Web.Host`应用程序使用API接口.
- 最后运行 `.Web.Host` 使用用户名: `admin` 密码: `1q2w3E*` 登录到应用程序.

@ -0,0 +1,570 @@
# Razor 集成
Razor模板是标准的C#类, 所以你可以使用任何C#的功能, 例如`依赖注入`, 使用`LINQ`, 自定义方法甚至使用`仓储`
## 安装
建议使用[ABP CLI](CLI.md)安装此包.
### 使用ABP CLI
在项目文件夹(.csproj 文件)中打开命令行窗口并输入以下命令:
````bash
abp add-package Volo.Abp.TextTemplating.Razor
````
### 手动安装
如果你想要手动安装:
1. 添加 [Volo.Abp.TextTemplating.Razor](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Razor) NuGet 包到你的项目:
````
Install-Package Volo.Abp.TextTemplating.Razor
````
2.添加 `AbpTextTemplatingRazorModule` 到你的模块的依赖列表:
````csharp
[DependsOn(
//...other dependencies
typeof(AbpTextTemplatingRazorModule) //Add the new module dependency
)]
public class YourModule : AbpModule
{
}
````
## 添加 MetadataReference到CSharpCompilerOptions
你需要将添加`MetadataReference`模板中使用的类型添加到 `CSharpCompilerOptions``References`.
```csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpRazorTemplateCSharpCompilerOptions>(options =>
{
options.References.Add(MetadataReference.CreateFromFile(typeof(YourModule).Assembly.Location));
});
}
```
## 添加MetadataReference到模板
你可以添加一些`MetadataReference`到模板
```csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
services.Configure<AbpCompiledViewProviderOptions>(options =>
{
//Hello is template name.
options.TemplateReferences.Add("Hello", new List<Assembly>()
{
Assembly.Load("Microsoft.Extensions.Logging.Abstractions"),
Assembly.Load("Microsoft.Extensions.Logging")
}
.Select(x => MetadataReference.CreateFromFile(x.Location))
.ToList());
});
}
```
## 定义模板
在渲染模板之前,需要定义它. 创建一个继承自 `TemplateDefinitionProvider` 的类:
````csharp
public class DemoTemplateDefinitionProvider : TemplateDefinitionProvider
{
public override void Define(ITemplateDefinitionContext context)
{
context.Add(
new TemplateDefinition("Hello") //template name: "Hello"
.WithRazorEngine()
.WithVirtualFilePath(
"/Demos/Hello/Hello.cshtml", //template content path
isInlineLocalized: true
)
);
}
}
````
* `context` 对象用于添加新模板或获取依赖模块定义的模板. 使用 `context.Add(...)` 定义新模板.
* `TemplateDefinition` 是代表模板的类,每个模板必须有唯一的名称(在渲染模板时使用).
* `/Demos/Hello/Hello.cshtml` 是模板文件的路径.
* `isInlineLocalized` 声明针对所有语言使用一个模板(`true` 还是针对每种语言使用不同的模板(`false`). 更多内容参阅下面的本地化部分.
* `WithRenderEngine` 方法为模板设置渲染引擎.
### 模板基类
每个 `cshtml` 模板页面都需要继承`RazorTemplatePageBase` 或 `RazorTemplatePageBase<Model>`. 基类提供了一些使用实用的属性可以在模板中使用. 例如: `Localizer`, `ServiceProvider`.
### 模板内容
`WithVirtualFilePath` 表示我们使用[虚拟文件系统](Virtual-File-System.md)存储模板内容. 在项目内创建一个 `Hello.cshtml` 文件,并在属性窗口中将其标记为"**嵌入式资源**":
![hello-template-razor](images/hello-template-razor.png)
示例 `Hello.cshtml` 内容如下所示:
```csharp
namespace HelloModelNamespace
{
public class HelloModel
{
public string Name { get; set; }
}
}
```
[虚拟文件系统](Virtual-File-System.md) 需要在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法添加你的文件:
````csharp
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<TextTemplateDemoModule>("TextTemplateDemo");
});
````
* `TextTemplateDemoModule`是模块类.
* `TextTemplateDemo` 是你的项目的根命名空间.
## 渲染模板
`ITemplateRenderer` 服务用于渲染模板内容.
### 示例: 渲染一个简单的模板
````csharp
public class HelloDemo : ITransientDependency
{
private readonly ITemplateRenderer _templateRenderer;
public HelloDemo(ITemplateRenderer templateRenderer)
{
_templateRenderer = templateRenderer;
}
public async Task RunAsync()
{
var result = await _templateRenderer.RenderAsync(
"Hello", //the template name
new HelloModel
{
Name = "John"
}
);
Console.WriteLine(result);
}
}
````
* `HelloDemo` 是一个简单的类,在构造函数注入了 `ITemplateRenderer` 并在 `RunAsync` 方法中使用它.
* `RenderAsync` 有两个基本参数:
* `templateName`: 要渲染的模板名称 (本示例中是 `Hello`).
* `model`: 在模板内部用做 `model` 的对象 (本示例中是 `HelloModel` 对象).
示例会返回以下结果:
````csharp
Hello John :)
````
## 本地化
可以基于当前文化对模板内容进行本地化. 以下部分描述了两种类型的本地化选项.
### 内联本地化
内联本地化使用[本地化系统](Localization.md)本地化模板内的文本.
#### 示例: 重置密码链接
假设你需要向用户发送电子邮件重置密码. 模板内容:
```csharp
namespace ResetMyPasswordModelNamespace
{
public class ResetMyPasswordModel
{
public string Link { get; set; }
public string Name { get; set; }
}
}
```
```csharp
@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase<ResetMyPasswordModelNamespace.ResetMyPasswordModel>
<a title="@Localizer["ResetMyPasswordTitle"]" href="@Model.Link">@Localizer["ResetMyPassword", Model.Name]</a>
```
`Localizer` 函数用于根据当前用户的文化来定位给定的Key,你需要在本地化文件中定义 `ResetMyPassword` 键:
````json
"ResetMyPasswordTitle": "Reset my password",
"ResetMyPassword": "Hi {0}, Click here to reset your password"
````
你还需要在模板定义提供程序类中声明要与此模板一起使用的本地化资源:
````csharp
context.Add(
new TemplateDefinition(
"PasswordReset", //Template name
typeof(DemoResource) //LOCALIZATION RESOURCE
)
.WithRazorEngine()
.WithVirtualFilePath(
"/Demos/PasswordReset/PasswordReset.cshtml", //template content path
isInlineLocalized: true
)
);
````
当你这样渲染模板时:
````csharp
var result = await _templateRenderer.RenderAsync(
"PasswordReset", //the template name
new PasswordResetModel
{
Name = "john",
Link = "https://abp.io/example-link?userId=123&token=ABC"
}
);
````
你可以看到以下本地化结果:
````csharp
<a title="Reset my password" href="https://abp.io/example-link?userId=123&token=ABC">Hi john, Click here to reset your password</a>
````
> 如果你为应用程序定义了 [默认本地化资源](Localization.md), 则无需声明模板定义的资源类型.
### 多个内容本地化
你可能希望为每种语言创建不同的模板文件,而不是使用本地化系统本地化单个模板. 如果模板对于特定的文化(而不是简单的文本本地化)应该是完全不同的,则可能需要使用它.
#### 示例: 欢迎电子邮件模板
假设你要发送电子邮件欢迎用户,但要定义基于用户的文化完全不同的模板.
首先创建一个文件夹,将模板放在里面,像 `en.cshtml`, `tr.cshtml` 每一个你支持的文化:
![multiple-file-template-razor](images/multiple-file-template-razor.png)
然后在模板定义提供程序类中添加模板定义:
````csharp
context.Add(
new TemplateDefinition(
name: "WelcomeEmail",
defaultCultureName: "en"
)
.WithRazorEngine()
.WithVirtualFilePath(
"/Demos/WelcomeEmail/Templates", //template content folder
isInlineLocalized: false
)
);
````
* 设置 **默认文化名称**, 当没有所需的文化模板,回退到缺省文化.
* 指定 **模板文件夹** 而不是单个模板文件.
* 设置 `isInlineLocalized``false`.
就这些,你可以渲染当前文化的模板:
````csharp
var result = await _templateRenderer.RenderAsync("WelcomeEmail");
````
> 为了简单我们跳过了模型,但是你可以使用前面所述的模型.
### 指定文化
`ITemplateRenderer` 服务如果没有指定则使用当前文化 (`CultureInfo.CurrentUICulture`). 如果你需要你可以使用 `cultureName` 参数指定文化.
````csharp
var result = await _templateRenderer.RenderAsync(
"WelcomeEmail",
cultureName: "en"
);
````
## 布局模板
布局模板用于在其他模板之间创建共享布局. 它类似于ASP.NET Core MVC / Razor Pages中的布局系统.
### 示例: 邮件HTML布局模板
例如,你想为所有电子邮件模板创建一个布局.
首先像之前一样创建一个模板文件:
```csharp
@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
@Body
</body>
</html>
```
* 布局模板必须具有 **Body** 部分作为渲染的子内容的占位符.
在模板定义提供程序中注册模板:
````csharp
context.Add(
new TemplateDefinition(
"EmailLayout",
isLayout: true //SET isLayout!
)
.WithRazorEngine()
.WithVirtualFilePath(
"/Demos/EmailLayout/EmailLayout.cshtml",
isInlineLocalized: true
)
);
````
现在你可以将此模板用作任何其他模板的布局:
````csharp
context.Add(
new TemplateDefinition(
name: "WelcomeEmail",
defaultCultureName: "en",
layout: "EmailLayout" //Set the LAYOUT
)
.WithRazorEngine()
.WithVirtualFilePath(
"/Demos/WelcomeEmail/Templates",
isInlineLocalized: false
)
);
````
## 全局上下文
ABP传递 `model`,可用于访问模板内的模型. 如果需要,可以传递更多的全局变量.
示例模板内容:
````csharp
@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase
A global object value: @GlobalContext["myGlobalObject"]
````
模板假定它渲染上下文中的 `myGlobalObject` 对象. 你可以如下所示提供它:
````csharp
var result = await _templateRenderer.RenderAsync(
"GlobalContextUsage",
globalContext: new Dictionary<string, object>
{
{"myGlobalObject", "TEST VALUE"}
}
);
````
渲染的结果将是:
````
A global object value: TEST VALUE
````
## 替换存在的模板
通过替换应用程序中使用的模块定义的模板. 这样你可以根据自己的需求自定义模板,而无需更改模块代码.
### 选项-1: 使用虚拟文件系统
[虚拟文件系统](Virtual-File-System.md)允许你通过将相同文件放入项目中的相同路径来覆盖任何文件.
#### 示例: 替换标准电子邮件布局模板
ABP框架提供了一个[邮件发送系统](Emailing.md), 它在内部使用文本模板来渲染邮件内容. 它在 `/Volo/Abp/Emailing/Templates/Layout.cshtml` 路径定义了一个标准邮件布局模板. 模板的唯一名称是 `Abp.StandardEmailTemplates.Layout` 并且这个字符中`Volo.Abp.Emailing.Templates.StandardEmailTemplates`静态类上定义为常量.
执行以下步骤将替换模板替换成你自定义的;
**1)** 在你的项目中相同的路径添加一个新文件 (`/Volo/Abp/Emailing/Templates/Layout.cshtml`):
![replace-email-layout-razor](images/replace-email-layout-razor.png)
**2)** 准备你的邮件布局模板:
````html
@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
<h1>This my header</h1>
@Body
<footer>
This is my footer...
</footer>
</body>
</html>
````
此示例只是向模板添加页眉和页脚并呈现它们之间的内容(请参阅上面的布局模板部分).
**3)** 在`.csproj`文件配置嵌入式资源e
* 添加 [Microsoft.Extensions.FileProviders.Embedded](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Embedded) NuGet 包到你的项目.
* 在 `.csproj` 中添加 `<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>``<PropertyGroup>...</PropertyGroup>` 部分.
* 添加以下代码到你的 `.csproj` 文件:
````xml
<ItemGroup>
<None Remove="Volo\Abp\Emailing\Templates\*.cshtml" />
<EmbeddedResource Include="Volo\Abp\Emailing\Templates\*.cshtml" />
</ItemGroup>
````
这将模板文件做为"嵌入式资源".
**4)** 配置虚拟文件系统
在[模块](Module-Development-Basics.md) `ConfigureServices` 方法配置 `AbpVirtualFileSystemOptions` 将嵌入的文件添加到虚拟文件系统中:
```csharp
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<BookStoreDomainModule>();
});
```
`BookStoreDomainModule` 应该是你的模块名称
> 确保你的模块(支持或间接)[依赖](Module-Development-Basics.md) `AbpEmailingModule`. 因为VFS可以基于依赖顺序覆盖文件.
现在渲染邮件布局模板时会使用你的模板.
### 选项-2: 使用模板定义提供者
你可以创建一个模板定义提供者类来获取邮件布局模板来更改它的虚拟文件路径.
**示例: 使用 `/MyTemplates/EmailLayout.cshtml` 文件而不是标准模板**
```csharp
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing.Templates;
using Volo.Abp.TextTemplating;
namespace MyProject
{
public class MyTemplateDefinitionProvider
: TemplateDefinitionProvider, ITransientDependency
{
public override void Define(ITemplateDefinitionContext context)
{
var emailLayoutTemplate = context.GetOrNull(StandardEmailTemplates.Layout);
emailLayoutTemplate
.WithVirtualFilePath(
"/MyTemplates/EmailLayout.cshtml",
isInlineLocalized: true
);
}
}
}
```
你依然应该添加 `/MyTemplates/EmailLayout.cshtml` 到虚拟文件系统. 这种方法允许你在任何文件夹中找到模板,而不是在依赖模块定义的文件夹中.
除了模板内容之外你还可以操作模板定义属性, 例如`DisplayName`, `Layout`或`LocalizationSource`.
## 高级功能
本节介绍文本模板系统的一些内部知识和高级用法.
### 模板内容Provider
`TemplateRenderer` 用于渲染模板,这是大多数情况下所需的模板. 但是你可以使用 `ITemplateContentProvider` 获取原始(未渲染的)模板内容.
> `ITemplateRenderer` 内部使用 `ITemplateContentProvider` 获取原始模板内容.
示例:
````csharp
public class TemplateContentDemo : ITransientDependency
{
private readonly ITemplateContentProvider _templateContentProvider;
public TemplateContentDemo(ITemplateContentProvider templateContentProvider)
{
_templateContentProvider = templateContentProvider;
}
public async Task RunAsync()
{
var result = await _templateContentProvider
.GetContentOrNullAsync("Hello");
Console.WriteLine(result);
}
}
````
结果是原始模板内容:
````
@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase<HelloModelNamespace.HelloModel>
Hello @Model.Name
````
* `GetContentOrNullAsync` 如果没有为请求的模板定义任何内容,则返回 `null`.
* 它可以获取 `cultureName` 参数,如果模板针对不同的文化具有不同的文件,则可以使用该参数(请参见上面的"多内容本地化"部分).
### 模板内容贡献者
`ITemplateContentProvider` 服务使用 `ITemplateContentContributor` 实现来查找模板内容. 有一个预实现的内容贡献者 `VirtualFileTemplateContentContributor`,它从上面描述的虚拟文件系统中获取模板内容.
你可以实现 `ITemplateContentContributor` 从另一个源读取原始模板内容.
示例:
````csharp
public class MyTemplateContentProvider
: ITemplateContentContributor, ITransientDependency
{
public async Task<string> GetOrNullAsync(TemplateContentContributorContext context)
{
var templateName = context.TemplateDefinition.Name;
//TODO: Try to find content from another source
return null;
}
}
````
如果源无法找到内容, 则返回 `null`, `ITemplateContentProvider` 将回退到下一个贡献者.
### Template Definition Manager
`ITemplateDefinitionManager` 服务可用于获取模板定义(由模板定义提供程序创建).
## 另请参阅
* 本文开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo).
* [本地化系统](Localization.md).
* [虚拟文件系统](Virtual-File-System.md).

@ -0,0 +1,527 @@
# Scriban 集成
## 安装
建议使用[ABP CLI](CLI.md)安装此包.
### 使用ABP CLI
在项目文件夹(.csproj 文件)中打开命令行窗口并输入以下命令:
````bash
abp add-package Volo.Abp.TextTemplating.Scriban
````
### 手动安装
如果你想要手动安装:
1. 添加 [Volo.Abp.TextTemplating.Scriban](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Scriban) NuGet 包到你的项目:
````
Install-Package Volo.Abp.TextTemplating.Scriban
````
2.添加 `AbpTextTemplatingScribanModule` 到你的模块的依赖列表:
````csharp
[DependsOn(
//...other dependencies
typeof(AbpTextTemplatingScribanModule) //Add the new module dependency
)]
public class YourModule : AbpModule
{
}
````
## 定义模板
在渲染模板之前,需要定义它. 创建一个继承自 `TemplateDefinitionProvider` 的类:
````csharp
public class DemoTemplateDefinitionProvider : TemplateDefinitionProvider
{
public override void Define(ITemplateDefinitionContext context)
{
context.Add(
new TemplateDefinition("Hello") //template name: "Hello"
.WithVirtualFilePath(
"/Demos/Hello/Hello.tpl", //template content path
isInlineLocalized: true
)
.WithScribanEngine()
);
}
}
````
* `context` 对象用于添加新模板或获取依赖模块定义的模板. 使用 `context.Add(...)` 定义新模板.
* `TemplateDefinition` 是代表模板的类,每个模板必须有唯一的名称(在渲染模板时使用).
* `/Demos/Hello/Hello.tpl` 是模板文件的路径.
* `isInlineLocalized` 声明针对所有语言使用一个模板(`true` 还是针对每种语言使用不同的模板(`false`). 更多内容参阅下面的本地化部分.
* `WithScribanEngine` 方法为模板设置渲染引擎.
### 模板内容
`WithVirtualFilePath` 表示我们使用[虚拟文件系统](Virtual-File-System.md)存储模板内容. 在项目内创建一个 `Hello.tpl` 文件,并在属性窗口中将其标记为"**嵌入式资源**":
![hello-template](images/hello-template.png)
示例 `Hello.tpl` 内容如下所示:
````
Hello {%{{{model.name}}}%} :)
````
[虚拟文件系统](Virtual-File-System.md) 需要在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法添加你的文件:
````csharp
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<TextTemplateDemoModule>("TextTemplateDemo");
});
````
* `TextTemplateDemoModule`是模块类.
* `TextTemplateDemo` 是你的项目的根命名空间.
## 渲染模板
`ITemplateRenderer` 服务用于渲染模板内容.
### 示例: 渲染一个简单的模板
````csharp
public class HelloDemo : ITransientDependency
{
private readonly ITemplateRenderer _templateRenderer;
public HelloDemo(ITemplateRenderer templateRenderer)
{
_templateRenderer = templateRenderer;
}
public async Task RunAsync()
{
var result = await _templateRenderer.RenderAsync(
"Hello", //the template name
new HelloModel
{
Name = "John"
}
);
Console.WriteLine(result);
}
}
````
* `HelloDemo` 是一个简单的类,在构造函数注入了 `ITemplateRenderer` 并在 `RunAsync` 方法中使用它.
* `RenderAsync` 有两个基本参数:
* `templateName`: 要渲染的模板名称 (本示例中是 `Hello`).
* `model`: 在模板内部用做 `model` 的对象 (本示例中是 `HelloModel` 对象).
示例会返回以下结果:
````csharp
Hello John :)
````
### 匿名模型
虽然建议为模板创建模型类,但在简单情况下使用匿名对象也是可行的:
````csharp
var result = await _templateRenderer.RenderAsync(
"Hello",
new
{
Name = "John"
}
);
````
示例中我们并没有创建模型类,但是创建了一个匿名对象模型.
### PascalCase 与 snake_case
PascalCase 属性名(如 `UserName`) 在模板中使用蛇形命名(如 `user_name`).
## 本地化
可以基于当前文化对模板内容进行本地化. 以下部分描述了两种类型的本地化选项.
### 内联本地化
内联本地化使用[本地化系统](Localization.md)本地化模板内的文本.
#### 示例: 重置密码链接
假设你需要向用户发送电子邮件重置密码. 模板内容:
````
<a title="{%{{{L "ResetMyPasswordTitle"}}}%}" href="{%{{{model.link}}}%}">{%{{{L "ResetMyPassword" model.name}}}%}</a>
````
`L` 函数用于根据当前用户的文化来定位给定的Key,你需要在本地化文件中定义 `ResetMyPassword` 键:
````json
"ResetMyPasswordTitle": "Reset my password",
"ResetMyPassword": "Hi {0}, Click here to reset your password"
````
你还需要在模板定义提供程序类中声明要与此模板一起使用的本地化资源:
````csharp
context.Add(
new TemplateDefinition(
"PasswordReset", //Template name
typeof(DemoResource) //LOCALIZATION RESOURCE
)
.WithScribanEngine()
.WithVirtualFilePath(
"/Demos/PasswordReset/PasswordReset.tpl", //template content path
isInlineLocalized: true
)
);
````
当你这样渲染模板时:
````csharp
var result = await _templateRenderer.RenderAsync(
"PasswordReset", //the template name
new PasswordResetModel
{
Name = "john",
Link = "https://abp.io/example-link?userId=123&token=ABC"
}
);
````
你可以看到以下本地化结果:
````csharp
<a title="Reset my password" href="https://abp.io/example-link?userId=123&token=ABC">Hi john, Click here to reset your password</a>
````
> 如果你为应用程序定义了 [默认本地化资源](Localization.md), 则无需声明模板定义的资源类型.
### 多个内容本地化
你可能希望为每种语言创建不同的模板文件,而不是使用本地化系统本地化单个模板. 如果模板对于特定的文化(而不是简单的文本本地化)应该是完全不同的,则可能需要使用它.
#### 示例: 欢迎电子邮件模板
假设你要发送电子邮件欢迎用户,但要定义基于用户的文化完全不同的模板.
首先创建一个文件夹,将模板放在里面,像 `en.tpl`, `tr.tpl` 每一个你支持的文化:
![multiple-file-template](images/multiple-file-template.png)
然后在模板定义提供程序类中添加模板定义:
````csharp
context.Add(
new TemplateDefinition(
name: "WelcomeEmail",
defaultCultureName: "en"
)
.WithScribanEngine()
.WithVirtualFilePath(
"/Demos/WelcomeEmail/Templates", //template content folder
isInlineLocalized: false
)
);
````
* 设置 **默认文化名称**, 当没有所需的文化模板,回退到缺省文化.
* 指定 **模板文件夹** 而不是单个模板文件.
* 设置 `isInlineLocalized``false`.
就这些,你可以渲染当前文化的模板:
````csharp
var result = await _templateRenderer.RenderAsync("WelcomeEmail");
````
> 为了简单我们跳过了模型,但是你可以使用前面所述的模型.
### 指定文化
`ITemplateRenderer` 服务如果没有指定则使用当前文化 (`CultureInfo.CurrentUICulture`). 如果你需要你可以使用 `cultureName` 参数指定文化.
````csharp
var result = await _templateRenderer.RenderAsync(
"WelcomeEmail",
cultureName: "en"
);
````
## 布局模板
布局模板用于在其他模板之间创建共享布局. 它类似于ASP.NET Core MVC / Razor Pages中的布局系统.
### 示例: 邮件HTML布局模板
例如,你想为所有电子邮件模板创建一个布局.
首先像之前一样创建一个模板文件:
````xml
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
{%{{{content}}}%}
</body>
</html>
````
* 布局模板必须具有 **{%{{{content}}}%}** 部分作为渲染的子内容的占位符.
在模板定义提供程序中注册模板:
````csharp
context.Add(
new TemplateDefinition(
"EmailLayout",
isLayout: true //SET isLayout!
)
.WithScribanEngine()
.WithVirtualFilePath(
"/Demos/EmailLayout/EmailLayout.tpl",
isInlineLocalized: true
)
);
````
现在你可以将此模板用作任何其他模板的布局:
````csharp
context.Add(
new TemplateDefinition(
name: "WelcomeEmail",
defaultCultureName: "en",
layout: "EmailLayout" //Set the LAYOUT
)
.WithScribanEngine()
.WithVirtualFilePath(
"/Demos/WelcomeEmail/Templates",
isInlineLocalized: false
)
);
````
## 全局上下文
ABP传递 `model`,可用于访问模板内的模型. 如果需要,可以传递更多的全局变量.
示例模板内容:
````
A global object value: {%{{{myGlobalObject}}}%}
````
模板假定它渲染上下文中的 `myGlobalObject` 对象. 你可以如下所示提供它:
````csharp
var result = await _templateRenderer.RenderAsync(
"GlobalContextUsage",
globalContext: new Dictionary<string, object>
{
{"myGlobalObject", "TEST VALUE"}
}
);
````
渲染的结果将是:
````
A global object value: TEST VALUE
````
## 替换存在的模板
通过替换应用程序中使用的模块定义的模板. 这样你可以根据自己的需求自定义模板,而无需更改模块代码.
### 选项-1: 使用虚拟文件系统
[虚拟文件系统](Virtual-File-System.md)允许你通过将相同文件放入项目中的相同路径来覆盖任何文件.
#### 示例: 替换标准电子邮件布局模板
ABP框架提供了一个[邮件发送系统](Emailing.md), 它在内部使用文本模板来渲染邮件内容. 它在 `/Volo/Abp/Emailing/Templates/Layout.tp` 路径定义了一个标准邮件布局模板. 模板的唯一名称是 `Abp.StandardEmailTemplates.Layout` 并且这个字符中`Volo.Abp.Emailing.Templates.StandardEmailTemplates`静态类上定义为常量.
执行以下步骤将替换模板替换成你自定义的;
**1)** 在你的项目中相同的路径添加一个新文件 (`/Volo/Abp/Emailing/Templates/Layout.tpl`):
![replace-email-layout](images/replace-email-layout.png)
**2)** 准备你的邮件布局模板:
````html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
<h1>This my header</h1>
{%{{{content}}}%}
<footer>
This is my footer...
</footer>
</body>
</html>
````
此示例只是向模板添加页眉和页脚并呈现它们之间的内容(请参阅上面的布局模板部分).
**3)** 在`.csproj`文件配置嵌入式资源e
* 添加 [Microsoft.Extensions.FileProviders.Embedded](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Embedded) NuGet 包到你的项目.
* 在 `.csproj` 中添加 `<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>``<PropertyGroup>...</PropertyGroup>` 部分.
* 添加以下代码到你的 `.csproj` 文件:
````xml
<ItemGroup>
<None Remove="Volo\Abp\Emailing\Templates\*.tpl" />
<EmbeddedResource Include="Volo\Abp\Emailing\Templates\*.tpl" />
</ItemGroup>
````
这将模板文件做为"嵌入式资源".
**4)** 配置虚拟文件系统
在[模块](Module-Development-Basics.md) `ConfigureServices` 方法配置 `AbpVirtualFileSystemOptions` 将嵌入的文件添加到虚拟文件系统中:
```csharp
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<BookStoreDomainModule>();
});
```
`BookStoreDomainModule` 应该是你的模块名称
> 确保你的模块(支持或间接)[依赖](Module-Development-Basics.md) `AbpEmailingModule`. 因为VFS可以基于依赖顺序覆盖文件.
现在渲染邮件布局模板时会使用你的模板.
### 选项-2: 使用模板定义提供者
你可以创建一个模板定义提供者类来获取邮件布局模板来更改它的虚拟文件路径.
**示例: 使用 `/MyTemplates/EmailLayout.tpl` 文件而不是标准模板**
```csharp
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing.Templates;
using Volo.Abp.TextTemplating;
namespace MyProject
{
public class MyTemplateDefinitionProvider
: TemplateDefinitionProvider, ITransientDependency
{
public override void Define(ITemplateDefinitionContext context)
{
var emailLayoutTemplate = context.GetOrNull(StandardEmailTemplates.Layout);
emailLayoutTemplate
.WithVirtualFilePath(
"/MyTemplates/EmailLayout.tpl",
isInlineLocalized: true
);
}
}
}
```
你依然应该添加 `/MyTemplates/EmailLayout.tpl` 到虚拟文件系统. 这种方法允许你在任何文件夹中找到模板,而不是在依赖模块定义的文件夹中.
除了模板内容之外你还可以操作模板定义属性, 例如`DisplayName`, `Layout`或`LocalizationSource`.
## 高级功能
本节介绍文本模板系统的一些内部知识和高级用法.
### 模板内容Provider
`TemplateRenderer` 用于渲染模板,这是大多数情况下所需的模板. 但是你可以使用 `ITemplateContentProvider` 获取原始(未渲染的)模板内容.
> `ITemplateRenderer` 内部使用 `ITemplateContentProvider` 获取原始模板内容.
示例:
````csharp
public class TemplateContentDemo : ITransientDependency
{
private readonly ITemplateContentProvider _templateContentProvider;
public TemplateContentDemo(ITemplateContentProvider templateContentProvider)
{
_templateContentProvider = templateContentProvider;
}
public async Task RunAsync()
{
var result = await _templateContentProvider
.GetContentOrNullAsync("Hello");
Console.WriteLine(result);
}
}
````
结果是原始模板内容:
````
Hello {%{{{model.name}}}%} :)
````
* `GetContentOrNullAsync` 如果没有为请求的模板定义任何内容,则返回 `null`.
* 它可以获取 `cultureName` 参数,如果模板针对不同的文化具有不同的文件,则可以使用该参数(请参见上面的"多内容本地化"部分).
### 模板内容贡献者
`ITemplateContentProvider` 服务使用 `ITemplateContentContributor` 实现来查找模板内容. 有一个预实现的内容贡献者 `VirtualFileTemplateContentContributor`,它从上面描述的虚拟文件系统中获取模板内容.
你可以实现 `ITemplateContentContributor` 从另一个源读取原始模板内容.
示例:
````csharp
public class MyTemplateContentProvider
: ITemplateContentContributor, ITransientDependency
{
public async Task<string> GetOrNullAsync(TemplateContentContributorContext context)
{
var templateName = context.TemplateDefinition.Name;
//TODO: Try to find content from another source
return null;
}
}
````
如果源无法找到内容, 则返回 `null`, `ITemplateContentProvider` 将回退到下一个贡献者.
### Template Definition Manager
`ITemplateDefinitionManager` 服务可用于获取模板定义(由模板定义提供程序创建).
## 另请参阅
* 本文开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo).
* [本地化系统](Localization.md).
* [虚拟文件系统](Virtual-File-System.md).

@ -8,450 +8,30 @@ Template + Model =renderer=> Rendered Content
它非常类似于 ASP.NET Core Razor View (或 Page):
*RAZOR VIEW (or PAGE) + MODEL ==render==> HTML CONTENT*
*RAZOR VIEW ( PAGE) + MODEL ==render==> HTML CONTENT*
你可以将渲染的输出用于任何目的,例如发送电子邮件或准备一些报告.
### 示例
模板渲染引擎非常强大:
Here, a simple template:
````
Hello {%{{{model.name}}}%} :)
````
你可以定义一个含有 `Name` 属性的类来渲染这个模板:
````csharp
public class HelloModel
{
public string Name { get; set; }
}
````
如果你使用 `Name``John``HelloModel` 渲染模板,输出为:
````
Hello John :)
````
模板渲染引擎非常强大;
* 它基于 [Scriban](https://github.com/lunet-io/scriban) 库, 所以它支持 **条件逻辑**, **循环** 等.
* 模板内容 **可以本地化**.
* 你可以定义 **布局模板** 在渲染其他模板中用做布局.
* 它支持**条件逻辑**, **循环**等等.
* 模板内容**可以本地化**.
* 你可以为其他渲染模板定义**布局模板**。
* 对于高级场景,你可以传递任何对象到模板上下文.
### 源码
这里是本文开发和引用的[示例应用程序源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo).
## 安装
推荐使用 [ABP CLI](CLI.md) 安装包.
### 使用 ABP CLI
在项目目录(.csproj file)打开命令行窗口运行以下命令:
````bash
abp add-package Volo.Abp.TextTemplating
````
### 手动安装
如果你想要手动安装;
1. 添加 [Volo.Abp.TextTemplating](https://www.nuget.org/packages/Volo.Abp.TextTemplating) NuGet包到你的项目:
````
Install-Package Volo.Abp.TextTemplating
````
2. 添加 `AbpTextTemplatingModule` 到你的模块依赖列表:
````csharp
[DependsOn(
//...other dependencies
typeof(AbpTextTemplatingModule) //Add the new module dependency
)]
public class YourModule : AbpModule
{
}
````
## 定义模板
在渲染模板之前,需要定义它. 创建一个继承自 `TemplateDefinitionProvider` 的类:
````csharp
public class DemoTemplateDefinitionProvider : TemplateDefinitionProvider
{
public override void Define(ITemplateDefinitionContext context)
{
context.Add(
new TemplateDefinition("Hello") //template name: "Hello"
.WithVirtualFilePath(
"/Demos/Hello/Hello.tpl", //template content path
isInlineLocalized: true
)
);
}
}
````
* `context` 对象用于添加新模板或获取依赖模块定义的模板. 使用 `context.Add(...)` 定义新模板.
* `TemplateDefinition` 是代表模板的类,每个模板必须有唯一的名称(在渲染模板时使用).
* `/Demos/Hello/Hello.tpl` 是模板文件的路径.
* `isInlineLocalized` 声明针对所有语言使用一个模板(`true` 还是针对每种语言使用不同的模板(`false`). 更多内容参阅下面的本地化部分.
### 模板内容
`WithVirtualFilePath` 表示我们使用[虚拟文件系统](Virtual-File-System.md)存储模板内容. 在项目内创建一个 `Hello.tpl` 文件,并在属性窗口中将其标记为"**嵌入式资源**":
![hello-template](images/hello-template.png)
示例 `Hello.tpl` 内容如下所示:
````
Hello {%{{{model.name}}}%} :)
````
[虚拟文件系统](Virtual-File-System.md) 需要在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法添加你的文件:
````csharp
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<TextTemplateDemoModule>("TextTemplateDemo");
});
````
* `TextTemplateDemoModule`是模块类.
* `TextTemplateDemo` 是你的项目的根命名空间.
## 渲染模板
`ITemplateRenderer` 服务用于渲染模板内容.
### 示例: 渲染一个简单的模板
````csharp
public class HelloDemo : ITransientDependency
{
private readonly ITemplateRenderer _templateRenderer;
public HelloDemo(ITemplateRenderer templateRenderer)
{
_templateRenderer = templateRenderer;
}
public async Task RunAsync()
{
var result = await _templateRenderer.RenderAsync(
"Hello", //the template name
new HelloModel
{
Name = "John"
}
);
Console.WriteLine(result);
}
}
````
* `HelloDemo` 是一个简单的类,在构造函数注入了 `ITemplateRenderer` 并在 `RunAsync` 方法中使用它.
* `RenderAsync` 有两个基本参数:
* `templateName`: 要渲染的模板名称 (本示例中是 `Hello`).
* `model`: 在模板内部用做 `model` 的对象 (本示例中是 `HelloModel` 对象).
示例会返回以下结果:
````csharp
Hello John :)
````
### 匿名模型
虽然建议为模板创建模型类,但在简单情况下使用匿名对象也是可行的:
````csharp
var result = await _templateRenderer.RenderAsync(
"Hello",
new
{
Name = "John"
}
);
````
示例中我们并没有创建模型类,但是创建了一个匿名对象模型.
### PascalCase 与 snake_case
PascalCase 属性名(如 `UserName`) 在模板中使用蛇形命名(如 `user_name`).
## 本地化
可以基于当前文化对模板内容进行本地化. 以下部分描述了两种类型的本地化选项.
### 内联本地化
内联本地化使用[本地化系统](Localization.md)本地化模板内的文本.
#### 示例: 重置密码链接
假设你需要向用户发送电子邮件重置密码. 模板内容:
````
<a title="{%{{{L "ResetMyPasswordTitle"}}}%}" href="{%{{{model.link}}}%}">{%{{{L "ResetMyPassword" model.name}}}%}</a>
````
`L` 函数用于根据当前用户的文化来定位给定的Key,你需要在本地化文件中定义 `ResetMyPassword` 键:
````json
"ResetMyPasswordTitle": "Reset my password",
"ResetMyPassword": "Hi {0}, Click here to reset your password"
````
你还需要在模板定义提供程序类中声明要与此模板一起使用的本地化资源:
````csharp
context.Add(
new TemplateDefinition(
"PasswordReset", //Template name
typeof(DemoResource) //LOCALIZATION RESOURCE
).WithVirtualFilePath(
"/Demos/PasswordReset/PasswordReset.tpl", //template content path
isInlineLocalized: true
)
);
````
当你这样渲染模板时:
````csharp
var result = await _templateRenderer.RenderAsync(
"PasswordReset", //the template name
new PasswordResetModel
{
Name = "john",
Link = "https://abp.io/example-link?userId=123&token=ABC"
}
);
````
你可以看到以下本地化结果:
````csharp
<a title="Reset my password" href="https://abp.io/example-link?userId=123&token=ABC">Hi john, Click here to reset your password</a>
````
> 如果你为应用程序定义了 [默认本地化资源](Localization.md), 则无需声明模板定义的资源类型.
### 多个内容本地化
你可能希望为每种语言创建不同的模板文件,而不是使用本地化系统本地化单个模板. 如果模板对于特定的文化(而不是简单的文本本地化)应该是完全不同的,则可能需要使用它.
#### 示例: 欢迎电子邮件模板
假设你要发送电子邮件欢迎用户,但要定义基于用户的文化完全不同的模板.
首先创建一个文件夹,将模板放在里面,像 `en.tpl`, `tr.tpl` 每一个你支持的文化:
![multiple-file-template](images/multiple-file-template.png)
然后在模板定义提供程序类中添加模板定义:
````csharp
context.Add(
new TemplateDefinition(
name: "WelcomeEmail",
defaultCultureName: "en"
)
.WithVirtualFilePath(
"/Demos/WelcomeEmail/Templates", //template content folder
isInlineLocalized: false
)
);
````
* 设置 **默认文化名称**, 当没有所需的文化模板,回退到缺省文化.
* 指定 **模板文件夹** 而不是单个模板文件.
* 设置 `isInlineLocalized``false`.
就这些,你可以渲染当前文化的模板:
````csharp
var result = await _templateRenderer.RenderAsync("WelcomeEmail");
````
> 为了简单我们跳过了模型,但是你可以使用前面所述的模型.
### 指定文化
`ITemplateRenderer` 服务如果没有指定则使用当前文化 (`CultureInfo.CurrentUICulture`). 如果你需要你可以使用 `cultureName` 参数指定文化.
````csharp
var result = await _templateRenderer.RenderAsync(
"WelcomeEmail",
cultureName: "en"
);
````
## 布局模板
布局模板用于在其他模板之间创建共享布局. 它类似于ASP.NET Core MVC / Razor Pages中的布局系统.
### 示例: 邮件HTML布局模板
例如,你想为所有电子邮件模板创建一个布局.
首先像之前一样创建一个模板文件:
````xml
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
{%{{{content}}}%}
</body>
</html>
````
* 布局模板必须具有 **{%{{{content}}}%}** 部分作为渲染的子内容的占位符.
在模板定义提供程序中注册模板:
````csharp
context.Add(
new TemplateDefinition(
"EmailLayout",
isLayout: true //SET isLayout!
).WithVirtualFilePath(
"/Demos/EmailLayout/EmailLayout.tpl",
isInlineLocalized: true
)
);
````
现在你可以将此模板用作任何其他模板的布局:
````csharp
context.Add(
new TemplateDefinition(
name: "WelcomeEmail",
defaultCultureName: "en",
layout: "EmailLayout" //Set the LAYOUT
).WithVirtualFilePath(
"/Demos/WelcomeEmail/Templates",
isInlineLocalized: false
)
);
````
## 全局上下文
ABP传递 `model`,可用于访问模板内的模型. 如果需要,可以传递更多的全局变量.
示例模板内容:
````
A global object value: {%{{{myGlobalObject}}}%}
````
模板假定它渲染上下文中的 `myGlobalObject` 对象. 你可以如下所示提供它:
````csharp
var result = await _templateRenderer.RenderAsync(
"GlobalContextUsage",
globalContext: new Dictionary<string, object>
{
{"myGlobalObject", "TEST VALUE"}
}
);
````
渲染的结果将是:
````
A global object value: TEST VALUE
````
## 高级功能
本节介绍文本模板系统的一些内部知识和高级用法.
### 模板内容Provider
`TemplateRenderer` 用于渲染模板,这是大多数情况下所需的模板. 但是你可以使用 `ITemplateContentProvider` 获取原始(未渲染的)模板内容.
> `ITemplateRenderer` 内部使用 `ITemplateContentProvider` 获取原始模板内容.
示例:
````csharp
public class TemplateContentDemo : ITransientDependency
{
private readonly ITemplateContentProvider _templateContentProvider;
public TemplateContentDemo(ITemplateContentProvider templateContentProvider)
{
_templateContentProvider = templateContentProvider;
}
public async Task RunAsync()
{
var result = await _templateContentProvider
.GetContentOrNullAsync("Hello");
Console.WriteLine(result);
}
}
````
结果是原始模板内容:
````
Hello {%{{{model.name}}}%} :)
````
* `GetContentOrNullAsync` 如果没有为请求的模板定义任何内容,则返回 `null`.
* 它可以获取 `cultureName` 参数,如果模板针对不同的文化具有不同的文件,则可以使用该参数(请参见上面的"多内容本地化"部分).
### 模板内容贡献者
`ITemplateContentProvider` 服务使用 `ITemplateContentContributor` 实现来查找模板内容. 有一个预实现的内容贡献者 `VirtualFileTemplateContentContributor`,它从上面描述的虚拟文件系统中获取模板内容.
你可以实现 `ITemplateContentContributor` 从另一个源读取原始模板内容.
示例:
````csharp
public class MyTemplateContentProvider
: ITemplateContentContributor, ITransientDependency
{
public async Task<string> GetOrNullAsync(TemplateContentContributorContext context)
{
var templateName = context.TemplateDefinition.Name;
//TODO: Try to find content from another source
return null;
}
}
ABP框架提供了两个模板引擎:
````
* **[Razor](Text-Templating-Razor.md)**
* **[Scriban](Text-Templating-Scriban.md)**
如果源无法找到内容, 则返回 `null`, `ITemplateContentProvider` 将回退到下一个贡献者.
你可以在同一个应用应用程序中使用不同的模板引擎, 或者创建一个新的自定义模板引擎.
### Template Definition Manager
## 源码
`ITemplateDefinitionManager` 服务可用于获取模板定义(由模板定义提供程序创建).
查看开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo).
## 另请参阅
* 本文开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo).
* [本地化系统](Localization.md).
* [虚拟文件系统](Virtual-File-System.md).
* [虚拟文件系统](Virtual-File-System.md).

@ -48,7 +48,7 @@ namespace MyCompany.MyProject
</abp-style-bundle>
````
`abp-script-bundle`定义了一个带有**唯一名称**的样式包:`MyGlobalBundle`. 使用方法很容易理解. 让我们看看它是如何*工作的*:
`abp-style-bundle`定义了一个带有**唯一名称**的样式包:`MyGlobalBundle`. 使用方法很容易理解. 让我们看看它是如何*工作的*:
* 当首次请求时,ABP从提供的文件中 **(延迟)lazy** 创建. 后续将从 **缓存** 中返回内容. 这意味着如果你有条件地将文件添加到包中,它只执行一次, 并且条件的任何更改都不会影响下一个请求的包.
* 在`development`环境中ABP会将包文件**单独**添加到页面中, 其他环境(`staging`,`production`...)会自动捆绑和压缩.

@ -409,14 +409,16 @@
"path": "Entities.md"
},
{
"text": "值对象"
"text": "值对象",
"path": "Value-Objects.md"
},
{
"text": "仓储",
"path": "Repositories.md"
},
{
"text": "领域服务"
"text": "领域服务",
"path": "Domain-Services.md"
},
{
"text": "规约",
@ -808,7 +810,13 @@
"items": [
{
"text": "CLI",
"path": "CLI.md"
"path": "CLI.md",
"items": [
{
"text": "新命令示例",
"path": "CLI-New-Command-Samples.md"
}
]
},
{
"text": "API文档",

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save