diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 9fca40ba4f..fe03c27997 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: true +blank_issues_enabled: false contact_links: - name: Issue with ABP Commercial url: https://abp.io/support/questions diff --git a/.github/workflows/angular.yml b/.github/workflows/angular.yml index ee66fbcf2c..cd48c0448a 100644 --- a/.github/workflows/angular.yml +++ b/.github/workflows/angular.yml @@ -27,12 +27,12 @@ jobs: with: fetch-depth: 0 - - uses: actions/cache@v2 + - uses: actions/cache@v4 with: path: 'npm/ng-packs/node_modules' key: ${{ runner.os }}-${{ hashFiles('npm/ng-packs/yarn.lock') }} - - uses: actions/cache@v2 + - uses: actions/cache@v4 with: path: 'templates/app/angular/node_modules' key: ${{ runner.os }}-${{ hashFiles('templates/app/angular/yarn.lock') }} diff --git a/.github/workflows/auto-pr.yml b/.github/workflows/auto-pr.yml index cba64abd5d..434e65e031 100644 --- a/.github/workflows/auto-pr.yml +++ b/.github/workflows/auto-pr.yml @@ -1,13 +1,13 @@ -name: Merge branch dev with rel-9.1 +name: Merge branch dev with rel-9.2 on: push: branches: - - rel-9.1 + - rel-9.2 permissions: contents: read jobs: - merge-dev-with-rel-9-1: + merge-dev-with-rel-9-2: permissions: contents: write # for peter-evans/create-pull-request to create branch pull-requests: write # for peter-evans/create-pull-request to create a PR @@ -18,14 +18,14 @@ jobs: ref: dev - name: Reset promotion branch run: | - git fetch origin rel-9.1:rel-9.1 - git reset --hard rel-9.1 + git fetch origin rel-9.2:rel-9.2 + git reset --hard rel-9.2 - name: Create Pull Request uses: peter-evans/create-pull-request@v3 with: - branch: auto-merge/rel-9-1/${{github.run_number}} - title: Merge branch dev with rel-9.1 - body: This PR generated automatically to merge dev with rel-9.1. Please review the changed files before merging to prevent any errors that may occur. + branch: auto-merge/rel-9-2/${{github.run_number}} + title: Merge branch dev with rel-9.2 + body: This PR generated automatically to merge dev with rel-9.2. Please review the changed files before merging to prevent any errors that may occur. reviewers: maliming draft: true token: ${{ github.token }} @@ -34,5 +34,5 @@ jobs: GH_TOKEN: ${{ secrets.BOT_SECRET }} run: | gh pr ready - gh pr review auto-merge/rel-9-1/${{github.run_number}} --approve - gh pr merge auto-merge/rel-9-1/${{github.run_number}} --merge --auto --delete-branch + gh pr review auto-merge/rel-9-2/${{github.run_number}} --approve + gh pr merge auto-merge/rel-9-2/${{github.run_number}} --merge --auto --delete-branch diff --git a/Directory.Packages.props b/Directory.Packages.props index d922a48e93..09c9a476fd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,189 +3,183 @@ true - + - - + + - + - - + + - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + - + - + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - + + + - - - - - - - - + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - + - - - + + + - - - - + + + + - + - - + - - - - - - + + + + + + - - - - + + + + - - - - + - \ No newline at end of file + diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index 1ea52ff5f5..d056e70b09 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -690,6 +690,33 @@ "Permission:Campaigns": "Campaigns", "Permission:Licenses": "License Settings", "BlockUserPolicy": "User Block Policies", - "Permission:BlockUserPolicy": "User Block Policy" + "Permission:BlockUserPolicy": "User Block Policy", + "ManageImages" : "Manage Image", + "ModuleMainImage": "Module Main Image", + "ModuleImageInfo1": "Accepted file types : JPEG, JPG, PNG, SVG ", + "ModuleImageInfo2": "Max file size : 1 MB ", + "ModuleImageInfo3": "Image Proportion : 1:1 ", + "ModuleImageInfo4": " Download a sample cover image ", + "AreYouSureToDeleteImage": "Are you sure you want to delete this image?", + "CompactPercentage": "Compact Percentage", + "CompactPercentageDescription": "Percentage of cache entries to remove during compaction (1-100)", + "Settings": "Settings", + "MaxSizeOfInMemoryCache": "Max Size Of In Memory Cache", + "MaxSizeOfInMemoryCacheInfo": "Gets or sets the maximum size of the memory cache.", + "SizeOfTriggerAutoCompact": "Size Of Trigger Auto Compact", + "SizeOfTriggerAutoCompactInfo": "Gets or sets the size threshold that triggers auto-compaction. When the cache size exceeds this value, auto-compaction will be performed", + "AutoCompactPercentage": "Auto Compact Percentage", + "AutoCompactPercentageInfo": "Gets or sets the percentage of items to remove when auto-compaction is triggered (1-100)", + "ClearCaches": "This clears all NuGet content cache", + "CacheLatestVersions": "Cache Latest Versions", + "VersionCount": "Version Count", + "VersionCountInfo": "Gets or sets the number of versions to cache", + "IncludeLeptonX": "Include LeptonX", + "IncludeLeptonXInfo": "Gets or sets whether to include LeptonX in the cache", + "IncludePrerelease": "Include Prerelease", + "IncludePrereleaseInfo": "Gets or sets whether to include prerelease versions in the cache", + "CacheVersionCount": "Cache Version Count", + "CacheVersionCountInfo": "Gets or sets the number of versions to cache", + "CacheLatestVersionsInfo": "This caches the latest versions of the NuGet packages" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json index 78231bb1af..f1dcba47a8 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json @@ -264,6 +264,7 @@ "EditProfile": "Edit Profile", "ConfirmEmailForPost": "To be able to post, you need to confirm your email. Go to account.abp.io/Account/Manage and verify your email in the Personal Info tab.", "DailyPostCreateLimitation": "You have reached the daily post creation limit. You can create a new post in {0}.", - "YourAccountDisabled": "Your user account is disabled!" + "YourAccountDisabled": "Your user account is disabled!", + "PostCreationFailed": "An error occurred while creating the post. Please try again later." } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json index bed79bc302..91f0fd81a3 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json @@ -672,20 +672,20 @@ "ExternalPostUrl": "External Post Url", "ThankYouForContribution": "Thank you for contributing to the ABP Community. We accept articles and video tutorials on ABP Framework, .NET, ASP.NET Core and general software development topics.", "GithubPost": "GitHub Post", - "GithubPostSubmitStepOne": "1. Write a post on any public GitHub repository with the Markdown format. example", - "GithubPostSubmitStepTwo": "2. Submit your post URL using the form.", - "GithubPostSubmitStepThree": "3. Your post will be rendered in this website.", + "GithubPostSubmitStepOne": "1. Write a post on any public GitHub repository with the Markdown format. example", + "GithubPostSubmitStepTwo": "2. Submit your post URL using the form.", + "GithubPostSubmitStepThree": "3. Your post will be rendered in this website.", "YoutubeVideo": "Youtube Video", - "YoutubeVideoSubmitStepOne": "1. Publish your video on YouTube.", - "YoutubeVideoSubmitStepTwo": "2. Submit the video URL using the form.", - "YoutubeVideoSubmitStepThree": "3. Visitors will be able to watch your video content directly on this website.", + "YoutubeVideoSubmitStepOne": "1. Publish your video on YouTube.", + "YoutubeVideoSubmitStepTwo": "2. Submit the video URL using the form.", + "YoutubeVideoSubmitStepThree": "3. Visitors will be able to watch your video content directly on this website.", "ExternalContent": "External Content", - "ExternalContentSubmitStepOne": "1. Create a content on any public platform (Medium, your own blog or anywhere you like).", - "ExternalContentSubmitStepTwo": "2. Submit your content URL using the form.", - "ExternalContentSubmitStepThree": "3. Visitors are redirected to the content on the original website.", + "ExternalContentSubmitStepOne": "1. Create a content on any public platform (Medium, your own blog or anywhere you like).", + "ExternalContentSubmitStepTwo": "2. Submit your content URL using the form.", + "ExternalContentSubmitStepThree": "3. Visitors are redirected to the content on the original website.", "ChooseYourContentType": "Please choose the way you want to add your content.", - "PostContentViaGithub": "I want to add my post with GitHub in accordance with the markdown rules.", - "PostContentViaYoutube": "I want to share my videos available on Youtube here.", + "PostContentViaGithub": "I want to add my post with GitHub in accordance with the markdown rules.", + "PostContentViaYoutube": "I want to share my videos available on Youtube here.", "PostContentViaExternalSource": "I want to add the content I published on another platform here.", "GitHubUserNameValidationMessage": "Your Github username can not include whitespace, please make sure your Github username is correct.", "PersonalSiteUrlValidationMessage": "Your personal site URL can not include whitespace, please make sure your personal site URL is correct.", @@ -1886,6 +1886,9 @@ "FaqIyzicoPaymentIssuesExplanation5": "Credit/Debit Cards: Visa, MasterCard, American Express, Discover, Diners Club, JCB", "FaqIyzicoPaymentIssuesExplanation6": "Digital Wallets: PayPal, AliPay, WebMoney", "FaqIyzicoPaymentIssuesExplanation7": "Alternatively, you can send the license amount directly via bank wire transfer. For our bank account details, please visit: Bank Account Information (use USD currency).", - "FaqIyzicoPaymentIssuesExplanation8": "ABP website doesn't save or process your credit card. We use payment gateways for this and the entire transaction is handled by payment gateways. We have no authority to interfere with the payment process or fix the payment steps. If you have further questions or need additional support, feel free to contact us at abp.io/contact." + "FaqIyzicoPaymentIssuesExplanation8": "ABP website doesn't save or process your credit card. We use payment gateways for this and the entire transaction is handled by payment gateways. We have no authority to interfere with the payment process or fix the payment steps. If you have further questions or need additional support, feel free to contact us at abp.io/contact.", + "BiographyContainsUrlValidationMessage": "Biography cannot contain URL.", + "CreatePostSEOTitleInfo": "SEO URL is a clean, readable, keyword-rich URL that helps both users and search engines understand what this post is about. Keep it short with 60 characters. SEO titles over 60 characters will be truncated. Use hyphens (-) to separate words (not underscores). Include target keywords near the start. Lowercase only. No stop words unless needed (e.g: \"and\", \"or\", \"the\").", + "SEOTitle": "SEO URL" } } diff --git a/common.props b/common.props index b1355eb36a..54e6251ac6 100644 --- a/common.props +++ b/common.props @@ -1,8 +1,8 @@ latest - 9.2.0-preview - 4.2.0-preview + 9.3.0-preview + 4.3.0-preview $(NoWarn);CS1591;CS0436 https://abp.io/assets/abp_nupkg.png https://abp.io/ diff --git a/docs/en/Blog-Posts/2022-03-08 v5_2_Preview/POST.md b/docs/en/Blog-Posts/2022-03-08 v5_2_Preview/POST.md index b78e3235a2..b0b44b9e45 100644 --- a/docs/en/Blog-Posts/2022-03-08 v5_2_Preview/POST.md +++ b/docs/en/Blog-Posts/2022-03-08 v5_2_Preview/POST.md @@ -187,7 +187,7 @@ We organized the 3rd live [ABP Community Talks](https://community.abp.io/events) * [Enis Necipoğlu](https://twitter.com/EnisNecipoglu) has also created [an article](https://community.abp.io/posts/using-autofilterer-with-abp-framework-uuqv81jm) to demonstrate how to use his own open source [AutoFilterer](https://github.com/enisn/AutoFilterer) library with the ABP Framework. * [Jonathan Potts](https://github.com/jonathanpotts) has created his first ABP Community article that shows how to use Bootswatch themes with the ABP Framework. [See it here](https://community.abp.io/posts/customizing-the-abp-basic-theme-with-bootswatch-4luoqzr0). -Thanks to all the contributors. It is appreciated if you want to submit your post and share your knowledge with the ABP community: https://community.abp.io/posts/submit +Thanks to all the contributors. It is appreciated if you want to submit your post and share your knowledge with the ABP community: https://community.abp.io/posts/create ## Conclusion diff --git a/docs/en/Blog-Posts/2024-02-16 v8_1_Preview/POST.md b/docs/en/Blog-Posts/2024-02-16 v8_1_Preview/POST.md index 24f55474ad..70cb53c75e 100644 --- a/docs/en/Blog-Posts/2024-02-16 v8_1_Preview/POST.md +++ b/docs/en/Blog-Posts/2024-02-16 v8_1_Preview/POST.md @@ -230,7 +230,7 @@ There are exciting articles contributed by the ABP community as always. I will h * [A Best Practice for Designing Interfaces in .NET C#](https://community.abp.io/posts/a-best-practice-for-designing-interfaces-in-.net-c-9xqc4h8d) * [Invariance, Covariance, and Contravariance in .NET C#](https://community.abp.io/posts/invariance-covariance-and-contravariance-in-.net-c-9blmuhme) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/create) to the ABP Community. ## Conclusion diff --git a/docs/en/Blog-Posts/2024-05-21 v8_2_Release/POST.md b/docs/en/Blog-Posts/2024-05-21 v8_2_Release/POST.md index 23260950d4..aadaae1c6f 100644 --- a/docs/en/Blog-Posts/2024-05-21 v8_2_Release/POST.md +++ b/docs/en/Blog-Posts/2024-05-21 v8_2_Release/POST.md @@ -260,7 +260,7 @@ There are exciting articles contributed by the ABP community as always. I will h * [Using FluentValidation with ABP Framework](https://community.abp.io/posts/using-fluentvalidation-with-abp-framework-2cxuwl70) by [Enes Döner](https://community.abp.io/members/Enes) * [Using Blob Storage with ABP](https://community.abp.io/posts/using-blob-storage-with-abp-framework-jygtmhn4) by [Emre Kendirli](https://community.abp.io/members/emrekenderli) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/create) to the ABP Community. ## Conclusion diff --git a/docs/en/Blog-Posts/2024-07-31 v8_3_Preview/post.md b/docs/en/Blog-Posts/2024-07-31 v8_3_Preview/post.md index 2d8a767568..fc3590b0d5 100644 --- a/docs/en/Blog-Posts/2024-07-31 v8_3_Preview/post.md +++ b/docs/en/Blog-Posts/2024-07-31 v8_3_Preview/post.md @@ -168,7 +168,7 @@ There are exciting articles contributed by the ABP community as always. I will h * [How to use .NET Aspire with ABP framework](https://abp.io/community/articles/how-to-use-.net-aspire-with-abp-framework-h29km4kk) by [Berkan Şaşmaz](https://twitter.com/berkansasmazz) * [Exciting New Feature in ABP.IO CMS Kit: Marked Item System](https://abp.io/community/articles/exciting-new-feature-in-abp.io-cms-kit-marked-item-system.-2hvpq0me) by [Suhaib Mousa](https://abp.io/community/members/suhaibmousa032@gmail.com) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/create) to the ABP Community. ## Conclusion diff --git a/docs/en/Blog-Posts/2024-09-06 v8_3_Release_Stable/POST.md b/docs/en/Blog-Posts/2024-09-06 v8_3_Release_Stable/POST.md index e69fbb86ea..3ac9de075a 100644 --- a/docs/en/Blog-Posts/2024-09-06 v8_3_Release_Stable/POST.md +++ b/docs/en/Blog-Posts/2024-09-06 v8_3_Release_Stable/POST.md @@ -70,7 +70,7 @@ As always, exciting articles have been contributed by the ABP community. I will * [Introducing the Google Cloud Storage BLOB Provider](https://abp.io/community/articles/introducing-the-google-cloud-storage-blob-provider-yrt6azc0) by [Engincan Veske](https://twitter.com/EngincanVeske) * [Switching Between Organization Units](https://abp.io/community/articles/switching-between-organization-units-i5tokpzt) by [Liming Ma](https://github.com/maliming) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/create) to the ABP Community. ## About the Next Version diff --git a/docs/en/Blog-Posts/2024-10-23 v9_0_Preview/POST.md b/docs/en/Blog-Posts/2024-10-23 v9_0_Preview/POST.md index f06648e586..db52eed7d5 100644 --- a/docs/en/Blog-Posts/2024-10-23 v9_0_Preview/POST.md +++ b/docs/en/Blog-Posts/2024-10-23 v9_0_Preview/POST.md @@ -214,7 +214,7 @@ There are exciting articles contributed by the ABP community as always. I will h * [ABP-Powered Web App with Inertia.js, React, and Vite](https://abp.io/community/articles/abppowered-web-app-with-inertia.js-react-and-vite-j7cccvad) by [Anto Subash](https://antosubash.com/) * [Multi-Tenancy Support in Angular Apps with ABP.IO](https://abp.io/community/articles/multitenancy-support-in-angular-apps-with-abp.io-lw9l36c5) by [HeadChannel Team](https://headchannel.co.uk/) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/create) to the ABP Community. ## Conclusion diff --git a/docs/en/Blog-Posts/2024-11-19 v9_0_Release_Stable/post.md b/docs/en/Blog-Posts/2024-11-19 v9_0_Release_Stable/post.md index 26850bc57f..9c96351d96 100644 --- a/docs/en/Blog-Posts/2024-11-19 v9_0_Release_Stable/post.md +++ b/docs/en/Blog-Posts/2024-11-19 v9_0_Release_Stable/post.md @@ -78,7 +78,7 @@ In addition to [the articles to highlight .NET 9.0 features written by our team] * [How to create your Own AI Bot on WhatsApp Using an ABP.io Template](https://abp.io/community/articles/how-to-create-your-own-ai-bot-on-whatsapp-using-the-abp-framework-c6jgvt9c) by [Michael Kokula](https://abp.io/community/members/Michal_Kokula) * [ABP Now Supports .NET 9](https://abp.io/community/articles/abp-now-supports-.net-9-zpkznc4f) by [Alper Ebiçoğlu](https://x.com/alperebicoglu) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP related (text or video) content](https://abp.io/community/posts/create) to the ABP Community. ### ABP Community Talks 2024.7: What’s New with .NET 9 & ABP 9? diff --git a/docs/en/Blog-Posts/2025-01-21 v9_1_Preview/POST.md b/docs/en/Blog-Posts/2025-01-21 v9_1_Preview/POST.md index bb8bfdf4c7..bbcc69c727 100644 --- a/docs/en/Blog-Posts/2025-01-21 v9_1_Preview/POST.md +++ b/docs/en/Blog-Posts/2025-01-21 v9_1_Preview/POST.md @@ -138,7 +138,7 @@ There are exciting articles contributed by the ABP community as always. I will h * [The new Unit Test structure in ABP application](https://abp.io/community/articles/the-new-unit-test-structure-in-abp-application-4vvvp2oy) by [Liming Ma](https://github.com/maliming) * [How to Use OpenAI API with ABP Framework](https://abp.io/community/articles/how-to-use-openai-api-with-abp-framework-rsfvihla) by [Berkan Şaşmaz](https://github.com/berkansasmaz) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/create) to the ABP Community. ## Conclusion diff --git a/docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/POST.md b/docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/POST.md index 96096807f1..2e6764e9b7 100644 --- a/docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/POST.md +++ b/docs/en/Blog-Posts/2025-03-07 v9_1_Release_Stable/POST.md @@ -67,7 +67,7 @@ As always, exciting articles have been contributed by the ABP community. I will * [Containerization: Blazor WASM + JWT Web API => Docker](https://abp.io/community/articles/containerization-blazor-wasm-jwt-web-api-docker-i3eirlsf) by [Bart Van Hoey](https://abp.io/community/members/bartvanhoey) * [Configuring Post-Logout Redirect URI in ABP Based Blazor Applications with OpenIddict](https://abp.io/community/articles/configuring-postlogout-redirect-uri-in-abp-based-blazor-applications-with-openiddict-1t84suxg) by [Engincan Veske](https://github.com/EngincanV) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP related (text or video) content](https://abp.io/community/posts/create) to the ABP Community. ### ABP Community Talks 2025.2: Real World Problems and Solutions with AI diff --git a/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/POST.md b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/POST.md new file mode 100644 index 0000000000..e59e75f44a --- /dev/null +++ b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/POST.md @@ -0,0 +1,195 @@ +# ABP Platform 9.2 RC Has Been Released + +We are happy to release [ABP](https://abp.io) version **9.2 RC** (Release Candidate). This blog post introduces the new features and important changes in this new version. + +Try this version and provide feedback for a more stable version of ABP v9.2! Thanks to you in advance. + +## Get Started with the 9.2 RC + +You can check the [Get Started page](https://abp.io/get-started) to see how to get started with ABP. You can either download [ABP Studio](https://abp.io/get-started#abp-studio-tab) (**recommended**, if you prefer a user-friendly GUI application - desktop application) or use the [ABP CLI](https://abp.io/docs/latest/cli). + +By default, ABP Studio uses stable versions to create solutions. Therefore, if you want to create a solution with a preview version, first you need to create a solution and then switch your solution to the preview version from the ABP Studio UI: + +![studio-switch-to-preview.png](studio-switch-to-preview.png) + +## Migration Guide + +There are a few breaking changes in this version that may affect your application. Please read the migration guide carefully, if you are upgrading from v9.x or earlier: [ABP Version 9.2 Migration Guide](https://abp.io/docs/9.2/release-info/migration-guides/abp-9-2) + +## What's New with ABP v9.2? + +In this section, I will introduce some major features released in this version. +Here is a brief list of titles explained in the next sections: + +* Added `ApplicationName` Property to Isolate Background Jobs & Background Workers +* Docs Module: Added "Alternative Words" to Filter Items +* Introducing the Bunny BLOB Storage Provider +* Upgraded `MongoDB.Driver` to v3.1.0 +* Using Timezone Settings to Display Datetime +* Identity Pro Module: Require Email Verification to Register +* Switching users during OAuth login + +### Added ApplicationName Property to Isolate Background Jobs & Background Workers + +ABP's [Background Jobs Module](https://abp.io/docs/latest/modules/background-jobs) has been enhanced with a new `ApplicationName` property that helps isolate jobs and workers across multiple applications sharing the same database. + +Previously, when different applications used the BackgroundJobs module and shared a database, an application might encounter jobs that didn't belong to it. This would lead to failed processing attempts and marking jobs as `IsAbandoned = true` with a "Undefined background job for the job name" error, preventing these jobs from ever being executed. + +With the new `ApplicationName` property, applications now properly filter jobs at the repository level, ensuring each application only processes job types it recognizes. This prevents the incorrect abandonment of jobs and ensures consistent behavior in multi-application scenarios. + +You can set `ApplicationName` of `AbpBackgroundJobWorkerOptions` to your application name to isolate jobs and workers across multiple applications sharing the same database: + +```csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure(options => + { + options.ApplicationName = context.Services.GetApplicationName()!; + }); +} +``` + +> For more information, please refer to the [Background Jobs Module](https://abp.io/docs/latest/modules/background-jobs) documentation and the [PR](https://github.com/abpframework/abp/pull/22169) that added this feature. + +### Docs Module: Added "Alternative Words" to Filter Items + +[ABP's Docs Module](https://abp.io/docs/9.2/modules/docs) now supports "alternative words" to enhance the search functionality when filtering documentation items. This feature addresses a common user experience issue where users might search using terminology different from what appears in the documentation. + +For example, when a user searches for "Error" in the documentation, they may actually be looking for content related to "Exception Handling." With this new feature, documentation items can now be configured with alternative keywords that are considered during filtering. + +The implementation allows defining optional "keywords" for items in the navigation tree. For example: + +```json +{ + "text": "Exception Handling", + "path": "framework/fundamentals/exception-handling.md", + "keywords": ["Error", "Another Value"] +} +``` + +When users search or filter content, the system now considers both the original text and these alternative keywords, improving discoverability of relevant documentation sections. This enhancement makes the documentation more accessible and user-friendly, especially for newcomers who might not be familiar with the exact terminology used in the ABP documentation. + +### Introducing the Bunny BLOB Storage Provider + +ABP v9.2 RC introduces a new BLOB storage provider for [Bunny Storage](https://bunny.net/storage/), a global edge storage solution. This addition expands ABP's BLOB Storage options beyond the existing providers like Azure, AWS, and others. + +The [Bunny BLOB Storage Provider](https://abp.io/docs/9.2/framework/infrastructure/blob-storing/bunny) allows ABP applications to seamlessly integrate with Bunny's CDN-backed storage service, which offers high-performance content delivery through its global network. + +To use this new provider, you'll need to: + +* Run `abp add-package Volo.Abp.BlobStoring.Bunny` command. +* And then configure the provider in your module's `ConfigureServices` method: + +```csharp +Configure(options => +{ + options.Containers.ConfigureDefault(container => + { + container.UseBunny(bunny => + { + bunny.StorageZoneName = "your-storage-zone"; + bunny.ApiKey = "your-api-key"; + bunny.Region = "your-region"; // de, ny, la, sg, or sy + }); + }); +}); +``` + +This integration provides ABP applications with an efficient and globally distributed storage solution, particularly beneficial for applications requiring fast content delivery across different geographical regions. To use this new provider and make the related configurations, you can refer to the [Bunny Storage Provider](https://abp.io/docs/9.2/framework/infrastructure/blob-storing/bunny) documentation always. + +> This new BLOB Storage provider is contributed by [@suhaib-mousa](https://github.com/suhaib-mousa). Thanks to him for his contribution! +> We are always happy to see the community contributing to the ABP Framework and encouraging them to contribute more. + +### Upgraded `MongoDB.Driver` to `v3.1.0` + +ABP v9.2 RC includes an upgrade to `MongoDB.Driver` version `3.1.0`. This significant version bump from previous releases brings several improvements and new features that benefit ABP applications using MongoDB as their database. + +The upgrade provides: + +* Async/Await Support: Write non-blocking, asynchronous code easily. +* Fluent API: Build queries and updates intuitively with Builders. +* LINQ Support: Use LINQ for querying MongoDB collections. +* and more ... + +> For more information, please refer to the [MongoDB.Driver release notes](https://github.com/mongodb/mongo-csharp-driver/releases/tag/v3.1.0). + +We have prepared a [migration guide](https://abp.io/docs/9.2/release-info/migration-guides/MongoDB-Driver-2-to-3) for this upgrade. Please refer to it to learn more about the changes and how to migrate your application. + +### Using Timezone Settings to Display Datetime + +A significant enhancement in ABP v9.2 is the ability to use timezone settings to display `DateTime` values according to the user's or application's configured timezone. Introduced in `v9.2.0-rc.2`, this feature addresses the common challenge of ensuring users see accurate time information, regardless of their geographical location. + +Previously, `DateTime` values were often shown in the server's timezone or in UTC, which could cause confusion for users in different timezones. With this new feature, ABP applications can now respect the configured timezone and automatically convert and display datetime values accordingly. + +**Before setting the timezone:** + +Consider a scenario where you have a list of books, and the `CreationTime` property is displayed without any timezone consideration. It may appear in the server's default timezone: + +![before.png](before.png) +*(Screenshot of a Books page showing the CreationTime in a default timezone.)* + +**Setting the timezone:** + +To set the timezone, start by configuring the `AbpClockOptions` in your module's `ConfigureServices` method: + +```csharp +Configure(options => +{ + options.Kind = DateTimeKind.Utc; +}); +``` + +> By setting the `Kind` property of `AbpClockOptions` to `DateTimeKind.Utc`, ABP will normalize all datetime values. Times stored in the database and returned to the frontend will be in UTC. Additionally, the `SupportsMultipleTimezone` property of the `IClock` service will be **true**, and you’ll be able to configure the timezone from the UI under the _Settings_ page. + +After setting the `Kind` property, you can run your application and configure the timezone from the UI under the _Settings_ page. For example, set the timezone to "Asia/Tokyo (+09:00)": + +![configure-timezone.png](configure-timezone.png) +*(Screenshot showing the configuration setting for Abp.Timing.TimeZone, set to "Asia/Tokyo (+09:00)".)* + +> ABP provides the `Abp.Timing.TimeZone` setting, which allows you to configure the desired timezone at the application, tenant, or user level and this setting can be configured in the UI under the _Settings_ page, inside of the _Time Zone_ tab. + +**After setting the timezone:** + +Once the timezone is configured, ABP automatically handles the conversion and display of `DateTime` values. The `CreationTime` on the _Books_ page will now be shown in the **"Asia/Tokyo"** timezone. + +![after.png](after.png) +*(Screenshot of the _Books_ page after setting the timezone, showing the `CreationTime` adjusted to the "Asia/Tokyo" timezone.)* + +This feature utilizes the `IClock` service, which provides methods like `ConvertToUserTime` and `ConvertToUtc` to facilitate timezone conversions. By configuring the `Abp.Timing.TimeZone` setting, developers can ensure a consistent and user-friendly experience across applications with a global user base. +> For a more detailed guide on implementing and using this feature, refer to the article: [Developing a Multi-Timezone Application Using the ABP Framework](https://abp.io/community/articles/developing-a-multitimezone-application-using-the-abp-framework-zk7fnrdq). It offers step-by-step instructions and examples for handling multi-timezone scenarios in ABP applications. + +### Identity Pro Module: Require Email Verification to Register + +[ABP Identity Pro module](https://abp.io/docs/9.2/modules/identity-pro) has been enhanced with a new feature that allows administrators to require email verification during the registration process. This security improvement ensures that users must verify their email addresses before their registration is considered complete. Enabling this feature is especially important for applications that want to prevent spam registrations. + +Administrators can enable or disable this feature through the **Identity management -> Identity Verification (tab)** settings page (by checking the `Enforce email verification to register` checkbox): + +![require-email-verification.png](./require-email-verification-for-register.png) + +### Switching users during OAuth login + +If you have an OAuth/Auth Server application using the [ABP Account Pro module](https://abp.io/docs/9.2/modules/account-pro) , you can pass the `prompt=select_account` parameter to force the user to select an account. + +![select-account.png](./select-account.png) + +For more information, please refer to the [Switching users during OAuth login](https://abp.io/docs/9.2/modules/account-pro#switching-users-during-oauth-login) documentation. + +### New ABP Community Articles + +There are exciting articles contributed by the ABP community as always. I will highlight some of them here: + +* [Implementing CQRS with MediatR in ABP](https://abp.io/community/articles/implementing-cqrs-with-mediatr-in-abp-xiqz2iio) by [Engincan Veske](https://github.com/EngincanV) +* [Using Vue Components in a Razor Pages ABP Application](https://abp.io/community/articles/using-vue-components-in-a-razor-pages-abp-application-z3jr07tv) by [Enis Necipoglu](https://github.com/enisn) +* [Using ABP's AWS Blob Storing Provider with DigitalOcean Spaces](https://abp.io/community/articles/using-abps-aws-blob-storing-provider-with-digitalocean-spaces-7hlyb25g) by [Suhaib Mousa](https://abp.io/community/members/suhaib-mousa) +* [Video Post: Using Vue Components in a Razor Pages ABP Application](https://abp.io/community/articles/using-vue-components-in-a-razor-pages-abp-application-z3jr07tv) by [Enis Necipoglu](https://github.com/enisn) +* [Understanding the Embedded Files in ABP Framework](https://abp.io/community/articles/understanding-the-embedded-files-in-abp-framework-nsrp8aa9) by [Liming Ma](https://github.com/maliming) +* [How to Change the CurrentUser in ABP?](https://abp.io/community/articles/how-to-change-the-currentuser-in-abp-i3uu1m7g) by [Engincan Veske](https://github.com/EngincanV) + + +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/create) to the ABP Community. + +## Conclusion + +This version comes with some new features and a lot of enhancements to the existing features. You can see the [Road Map](https://abp.io/docs/9.2/release-info/road-map) documentation to learn about the release schedule and planned features for the next releases. Please try ABP v9.2 RC and provide feedback to help us release a more stable version. + +Thanks for being a part of this community! diff --git a/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/after.png b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/after.png new file mode 100644 index 0000000000..f9f72302a5 Binary files /dev/null and b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/after.png differ diff --git a/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/before.png b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/before.png new file mode 100644 index 0000000000..14340f91ee Binary files /dev/null and b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/before.png differ diff --git a/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/configure-timezone.png b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/configure-timezone.png new file mode 100644 index 0000000000..d383eee6f6 Binary files /dev/null and b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/configure-timezone.png differ diff --git a/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/cover-image.png b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/cover-image.png new file mode 100644 index 0000000000..dd40555c8e Binary files /dev/null and b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/cover-image.png differ diff --git a/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/require-email-verification-for-register.png b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/require-email-verification-for-register.png new file mode 100644 index 0000000000..597eaa6118 Binary files /dev/null and b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/require-email-verification-for-register.png differ diff --git a/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/select-account.png b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/select-account.png new file mode 100644 index 0000000000..39d8c1b421 Binary files /dev/null and b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/select-account.png differ diff --git a/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/studio-switch-to-preview.png b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/studio-switch-to-preview.png new file mode 100644 index 0000000000..32f6d01edb Binary files /dev/null and b/docs/en/Blog-Posts/2025-03-27 v9_2_Preview/studio-switch-to-preview.png differ diff --git a/docs/en/Community-Articles/2022-03-09-abpio-platform-52-rc-has-been-published/post.md b/docs/en/Community-Articles/2022-03-09-abpio-platform-52-rc-has-been-published/post.md index be89ccb092..e43ba9a529 100644 --- a/docs/en/Community-Articles/2022-03-09-abpio-platform-52-rc-has-been-published/post.md +++ b/docs/en/Community-Articles/2022-03-09-abpio-platform-52-rc-has-been-published/post.md @@ -186,7 +186,7 @@ We organized the 3rd live [ABP Community Talks](https://community.abp.io/events) * [Enis Necipoğlu](https://twitter.com/EnisNecipoglu) has also created [an article](https://community.abp.io/posts/using-autofilterer-with-abp-framework-uuqv81jm) to demonstrate how to use his own open source [AutoFilterer](https://github.com/enisn/AutoFilterer) library with the ABP Framework. * [Jonathan Potts](https://github.com/jonathanpotts) has created his first ABP Community article that shows how to use Bootswatch themes with the ABP Framework. [See it here](https://community.abp.io/posts/customizing-the-abp-basic-theme-with-bootswatch-4luoqzr0). -Thanks to all the contributors. It is appreciated if you want to submit your post and share your knowledge with the ABP community: https://community.abp.io/posts/submit +Thanks to all the contributors. It is appreciated if you want to submit your post and share your knowledge with the ABP community: https://community.abp.io/posts/create ## Conclusion diff --git a/docs/en/Community-Articles/2024-02-19-abpio-platform-81-rc-has-been-published/post.md b/docs/en/Community-Articles/2024-02-19-abpio-platform-81-rc-has-been-published/post.md index 569e566af7..fbe2c2cbad 100644 --- a/docs/en/Community-Articles/2024-02-19-abpio-platform-81-rc-has-been-published/post.md +++ b/docs/en/Community-Articles/2024-02-19-abpio-platform-81-rc-has-been-published/post.md @@ -228,7 +228,7 @@ There are exciting articles contributed by the ABP community as always. I will h * [A Best Practice for Designing Interfaces in .NET C#](https://community.abp.io/posts/a-best-practice-for-designing-interfaces-in-.net-c-9xqc4h8d) * [Invariance, Covariance, and Contravariance in .NET C#](https://community.abp.io/posts/invariance-covariance-and-contravariance-in-.net-c-9blmuhme) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/create) to the ABP Community. ## Conclusion diff --git a/docs/en/Community-Articles/2024-05-24-abpio-platform-82-rc-has-been-published/post.md b/docs/en/Community-Articles/2024-05-24-abpio-platform-82-rc-has-been-published/post.md index 6574012f24..c3eadb6419 100644 --- a/docs/en/Community-Articles/2024-05-24-abpio-platform-82-rc-has-been-published/post.md +++ b/docs/en/Community-Articles/2024-05-24-abpio-platform-82-rc-has-been-published/post.md @@ -259,7 +259,7 @@ There are exciting articles contributed by the ABP community as always. I will h * [Using FluentValidation with ABP Framework](https://community.abp.io/posts/using-fluentvalidation-with-abp-framework-2cxuwl70) by [Enes Döner](https://community.abp.io/members/Enes) * [Using Blob Storage with ABP](https://community.abp.io/posts/using-blob-storage-with-abp-framework-jygtmhn4) by [Emre Kendirli](https://community.abp.io/members/emrekenderli) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/create) to the ABP Community. ## Conclusion diff --git a/docs/en/Community-Articles/2024-08-01-abpio-platform-83-rc-has-been-published/post.md b/docs/en/Community-Articles/2024-08-01-abpio-platform-83-rc-has-been-published/post.md index 05c18fde03..4ddd247a63 100644 --- a/docs/en/Community-Articles/2024-08-01-abpio-platform-83-rc-has-been-published/post.md +++ b/docs/en/Community-Articles/2024-08-01-abpio-platform-83-rc-has-been-published/post.md @@ -165,7 +165,7 @@ There are exciting articles contributed by the ABP community as always. I will h * [How to use .NET Aspire with ABP framework](https://abp.io/community/articles/how-to-use-.net-aspire-with-abp-framework-h29km4kk) by [Berkan Şaşmaz](https://twitter.com/berkansasmazz) * [Exciting New Feature in ABP.IO CMS Kit: Marked Item System](https://abp.io/community/articles/exciting-new-feature-in-abp.io-cms-kit-marked-item-system.-2hvpq0me) by [Suhaib Mousa](https://abp.io/community/members/suhaibmousa032@gmail.com) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/create) to the ABP Community. ## Conclusion diff --git a/docs/en/Community-Articles/2024-09-12-abpio-platform-83-final-has-been-released/post.md b/docs/en/Community-Articles/2024-09-12-abpio-platform-83-final-has-been-released/post.md index 55f7b80294..de27f8cdbd 100644 --- a/docs/en/Community-Articles/2024-09-12-abpio-platform-83-final-has-been-released/post.md +++ b/docs/en/Community-Articles/2024-09-12-abpio-platform-83-final-has-been-released/post.md @@ -70,7 +70,7 @@ As always, exciting articles have been contributed by the ABP community. I will * [Introducing the Google Cloud Storage BLOB Provider](https://abp.io/community/articles/introducing-the-google-cloud-storage-blob-provider-yrt6azc0) by [Engincan Veske](https://twitter.com/EngincanVeske) * [Switching Between Organization Units](https://abp.io/community/articles/switching-between-organization-units-i5tokpzt) by [Liming Ma](https://github.com/maliming) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/create) to the ABP Community. ## About the Next Version diff --git a/docs/en/Community-Articles/2024-10-23-abpio-platform-90-rc-has-been-published/post.md b/docs/en/Community-Articles/2024-10-23-abpio-platform-90-rc-has-been-published/post.md index 9e22660332..a7dfa896bd 100644 --- a/docs/en/Community-Articles/2024-10-23-abpio-platform-90-rc-has-been-published/post.md +++ b/docs/en/Community-Articles/2024-10-23-abpio-platform-90-rc-has-been-published/post.md @@ -210,7 +210,7 @@ There are exciting articles contributed by the ABP community as always. I will h * [ABP-Powered Web App with Inertia.js, React, and Vite](https://abp.io/community/articles/abppowered-web-app-with-inertia.js-react-and-vite-j7cccvad) by [Anto Subash](https://antosubash.com/) * [Multi-Tenancy Support in Angular Apps with ABP.IO](https://abp.io/community/articles/multitenancy-support-in-angular-apps-with-abp.io-lw9l36c5) by [HeadChannel Team](https://headchannel.co.uk/) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/create) to the ABP Community. ## Conclusion diff --git a/docs/en/Community-Articles/2024-11-21-abpio-platform-90-has-been-released-based-on-net-90/post.md b/docs/en/Community-Articles/2024-11-21-abpio-platform-90-has-been-released-based-on-net-90/post.md index 30ff978de6..931379eac0 100644 --- a/docs/en/Community-Articles/2024-11-21-abpio-platform-90-has-been-released-based-on-net-90/post.md +++ b/docs/en/Community-Articles/2024-11-21-abpio-platform-90-has-been-released-based-on-net-90/post.md @@ -80,7 +80,7 @@ In addition to [the articles to highlight .NET 9.0 features written by our team] * [How to create your Own AI Bot on WhatsApp Using an ABP.io Template](https://abp.io/community/articles/how-to-create-your-own-ai-bot-on-whatsapp-using-the-abp-framework-c6jgvt9c) by [Michael Kokula](https://abp.io/community/members/Michal_Kokula) * [ABP Now Supports .NET 9](https://abp.io/community/articles/abp-now-supports-.net-9-zpkznc4f) by [Alper Ebiçoğlu](https://x.com/alperebicoglu) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP related (text or video) content](https://abp.io/community/posts/create) to the ABP Community. ## Conclusion diff --git a/docs/en/Community-Articles/2025-01-21-abp-platform-91-rc-has-been-released/post.md b/docs/en/Community-Articles/2025-01-21-abp-platform-91-rc-has-been-released/post.md index 2c3a98ac49..615457a7ff 100644 --- a/docs/en/Community-Articles/2025-01-21-abp-platform-91-rc-has-been-released/post.md +++ b/docs/en/Community-Articles/2025-01-21-abp-platform-91-rc-has-been-released/post.md @@ -136,7 +136,7 @@ There are exciting articles contributed by the ABP community as always. I will h * [The new Unit Test structure in ABP application](https://abp.io/community/articles/the-new-unit-test-structure-in-abp-application-4vvvp2oy) by [Liming Ma](https://github.com/maliming) * [How to Use OpenAI API with ABP Framework](https://abp.io/community/articles/how-to-use-openai-api-with-abp-framework-rsfvihla) by [Berkan Şaşmaz](https://github.com/berkansasmaz) -Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/submit) to the ABP Community. +Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://abp.io/community/posts/create) to the ABP Community. ## Conclusion diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list-utc1.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list-utc1.png new file mode 100644 index 0000000000..9adbd41062 Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list-utc1.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list-utc3.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list-utc3.png new file mode 100644 index 0000000000..51256b2d62 Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list-utc3.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list.png new file mode 100644 index 0000000000..cc0a92b591 Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/auth-list.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/berlin.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/berlin.png new file mode 100644 index 0000000000..07715caabd Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/berlin.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-create.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-create.png new file mode 100644 index 0000000000..5385c14167 Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-create.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-edit.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-edit.png new file mode 100644 index 0000000000..95c0357171 Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-edit.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-list.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-list.png new file mode 100644 index 0000000000..181983492c Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/blazor-list.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-create.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-create.png new file mode 100644 index 0000000000..827e8911d7 Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-create.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-edit.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-edit.png new file mode 100644 index 0000000000..a3cc8a6a0c Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-edit.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list-utc1.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list-utc1.png new file mode 100644 index 0000000000..9904c56df5 Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list-utc1.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list-utc3.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list-utc3.png new file mode 100644 index 0000000000..7d472296fb Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list-utc3.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list.png new file mode 100644 index 0000000000..282f288855 Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-list.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-post.png b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-post.png new file mode 100644 index 0000000000..35bff4aded Binary files /dev/null and b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/mvc-post.png differ diff --git a/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/post.md b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/post.md new file mode 100644 index 0000000000..09d7f917bb --- /dev/null +++ b/docs/en/Community-Articles/2025-03-11-Developing-A-Multi-Timezone-Application-Using-The-ABP-Framework/post.md @@ -0,0 +1,781 @@ +# Developing a Multi-Timezone Application Using the ABP Framework + +When developing multi-timezone applications, we need to handle users from different time zones and make sure they see the correct time. The system also needs to support users changing their timezone (like when traveling or moving) and make sure all time displays update correctly to show accurate time information. + +All these scenarios require us to handle timezone conversions correctly in our application. The ABP framework provides a complete solution for these challenges. + +In this article, we'll show you step by step how to handle multi-timezone in the ABP framework. + +> The content mentioned in this article will be available after the ABP 9.2 version + +## Timezone Settings + +The ABP framework provides a setting called `Abp.Timing.TimeZone` for setting and getting the timezone of users, tenants, or applications. The default value is empty, which means the application will use the server's time zone. Check out the [Timing documentation](https://abp.io/docs/latest/framework/infrastructure/timing) for more information. + +## ISO 8601 Date Time Format + +Different countries and regions may use different time formats: + +* Year-Month-Day (YYYY-MM-DD): Mainly used in China, Japan, Korea, Canada (official standard), Germany (ISO standard), ISO 8601 international standard, etc. Example: 2025-03-11 +* Day-Month-Year (DD-MM-YYYY): Mainly used in UK, India, Australia, New Zealand, most European countries (like France, Germany, Italy, Spain), some South American countries, etc. Example: 11-03-2025 or 11/03/2025 +* Month-Day-Year (MM-DD-YYYY): Mainly used in USA, Philippines, some parts of Canada, etc. Example: 03-11-2025 or 03/11/2025 +* Day.Month.Year (DD.MM.YYYY): Mainly used in Germany, Russia, Switzerland, Hungary, Czech Republic, etc. Example: 11.03.2025 + +Also, different countries/regions might use different separators (like slash /, hyphen -, dot .), and some countries use different month abbreviations or full names (like March 11, 2025). + +ISO 8601 uses a standard format to avoid confusion between different date formats and ensure global compatibility. + +It has 4 parts: + +* Date part: `YYYY-MM-DD` +* `T` as a separator +* Time part: `HH:MM:SS` +* Timezone part: `Z` or `+/-HH:MM` + +You'll usually see formats like: `YYYY-MM-DDTHH:MM:SSZ` or `YYYY-MM-DDTHH:MM:SS+/-HH:MM`, for example: `2025-03-11T10:30:00Z` or `2025-03-11T22:30:00+03:00` + +When our application needs to handle multiple timezones, we usually use ISO 8601 to represent time. + +## Enabling Multi-Timezone Support + +When we set the `Kind` of `AbpClockOptions` to `DateTimeKind.Utc`, the ABP framework will normalize all times. Times written to the database and returned to the frontend will be in `UTC`. the `SupportsMultipleTimezone` property will be `true` in the `IClock` service. + +```csharp +Configure(options => +{ + options.Kind = DateTimeKind.Utc; +}); +``` + +### Using DateTime to Store Time + +Assuming the `DateTime` stored in the database is `2025-03-01 10:30:00`, then the time returned to the front end will be `2025-03-01T10:30:00Z`. This is a time in ISO 8601 format. Because `DateTime` does not have timezone information, the framework will assume it is `UTC` time. + +### Using DateTimeOffset to Store Time + +If you use `DateTimeOffset` to store time, the ABP framework will not normalize `DateTimeOffset`, but will return it directly to the front end. + +Assuming the `DateTimeOffset` stored in the database is `2025-03-01 13:30:00 +03:00`, then the time returned to the front end will be `2025-03-01T13:30:00+03:00`. This is also a time in ISO 8601 format. + +We recommend using `DateTimeOffset` to store time because it has timezone information. + +## Timezone Conversion + +### Converting UTC Time to User Time + +The `IClock` service has 2 methods to convert a given `UTC` time to the user time: + +```csharp +DateTime ConvertToUserTime(utcDateTime dateTime) +DateTimeOffset ConvertToUserTime(DateTimeOffset dateTimeOffset) +``` + +> If `SupportsMultipleTimezone` is `false` or `dateTime.Kind` is not `Utc` or no timezone is set, it will return the given `DateTime` or `DateTimeOffset` without any changes. + +**Example:** + +If the user's timezone is `Europe/Istanbul` + +```csharp +// 2025-03-01T05:30:00Z +var utcTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc); + +var userTime = Clock.ConvertToUserTime(utcTime); + +// Europe/Istanbul has 3 hours difference with UTC. So, the result will be 3 hours later. +userTime.Kind.ShouldBe(DateTimeKind.Unspecified); +userTime.ToString("O").ShouldBe("2025-03-01T08:30:00"); +``` + +```csharp +// 2025-03-01T05:30:00Z +var utcTime = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc), TimeSpan.Zero); + +var userTime = Clock.ConvertToUserTime(utcTime); + +// Europe/Istanbul has 3 hours difference with UTC. So, the result will be 3 hours later. +userTime.Offset.ShouldBe(TimeSpan.FromHours(3)); +userTime.ToString("O").ShouldBe("2025-03-01T08:30:00.0000000+03:00"); +``` + +### Converting User Time to UTC + +The `IClock` service has 1 method to convert a given user time to UTC. + +```csharp +DateTime ConvertToUtc(DateTime dateTime) +``` + +> If `SupportsMultipleTimezone` is `false` or `dateTime.Kind` is `Utc` or no timezone is set, it will return the given `DateTime` without any changes. + +**Example:** + +If the user's timezone is `Europe/Istanbul` + +```csharp +// 2025-03-01T05:30:00 +var userTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified); //Same as Local + +var utcTime = Clock.ConvertToUtc(userTime); + +// Europe/Istanbul has 3 hours difference with UTC. So, the result will be 3 hours earlier. +utcTime.Kind.ShouldBe(DateTimeKind.Utc); +utcTime.ToString("O").ShouldBe("2025-03-01T02:30:00.0000000Z"); +``` + +## Handling Timezone in Different UIs + +We'll use the `TimeZoneApp` project to demonstrate handling timezone in different UIs. It has a `Meeting` entity, with several time properties. + +```csharp +public class Meeting : AggregateRoot +{ + public string Subject { get; set; } + + public DateTime StartTime { get; set; } + + public DateTime EndTime { get; set; } + + public DateTime ActualStartTime { get; set; } + + public DateTime? CanceledTime { get; set; } + + public DateTimeOffset ReminderTime { get; set; } + + public DateTimeOffset? FollowUpTime { get; set; } + + public string Description { get; set; } +} +``` + +`TimeZoneApp` project is an ABP layered architecture project, it sets a global `Europe/Istanbul` timezone, it contains 4 websites + +* `API.Host`: API website, it does not have UI, it returns data in JSON format +* `AuthServer`: Authentication server, it uses Razor Pages as UI +* `Web`: Razor Pages website, it uses JavaScript to manage Meeting creation and editing and display +* `Blazor`: Blazor Server website, it uses Blazor to manage Meeting creation and editing and display + +All 4 applications are enabled for multi-timezone support, and use the `UseAbpTimeZone` middleware. + +> Blazor WASM and Angular do not need to use the `UseAbpTimeZone` middleware + + +### DateTime in API Response + +In the API response, we usually use the ISO 8601 format time, as you can see, after enabling multi-timezone support, the API returns time to the front end as UTC time. + +`2025-03-01T09:30:00Z` and `2025-03-01T12:30:00+00:00` are ISO 8601 format time. + +```json +[ + { + "subject": "ABP Developer Guide", + "startTime": "2025-03-01T09:30:00Z", + "endTime": "2025-03-01T10:30:00Z", + "actualStartTime": "2025-03-01T11:30:00Z", + "canceledTime": null, + "reminderTime": "2025-03-01T12:30:00+00:00", + "followUpTime": "2025-03-01T13:30:00+00:00", + "description": "We will discuss the ABP developer guide.", + "id": "2af0abd3-be06-ecff-5d4c-3a1895ac7950" + }, + { + "subject": "ABP Training", + "startTime": "2025-03-01T09:30:00Z", + "endTime": "2025-03-01T10:30:00Z", + "actualStartTime": "2025-03-01T11:30:00Z", + "canceledTime": "2025-03-01T12:00:00Z", + "reminderTime": "2025-03-01T12:30:00+00:00", + "followUpTime": "2025-03-01T13:30:00+00:00", + "description": "ABP training for the new developers.", + "id": "290b0cb6-3e50-6324-1e79-3a1895ac795f" + } +] +``` + +### Handling Timezone in MVC/Razor Pages + +In the `AuthServer` project, we handle time conversion in a simple way: +1. First, we get the `Meeting` entities from the database using `IRepository`. At this point, all `DateTime` values are in UTC. +2. Then, when displaying the times in the view, we use `Clock.ConvertToUserTime` to show them in the user's timezone. + +> Note: The `ConvertToUserTime` method will only convert times if multi-timezone support is enabled in the application. + +```csharp +public class IndexModel : AbpPageModel +{ + public List? Meetings { get; set; } + + protected IRepository MeetingRepository { get; } + + public IndexModel(IRepository meetingRepository) + { + MeetingRepository = meetingRepository; + } + + public async Task OnGetAsync() + { + Meetings = await MeetingRepository.GetListAsync(); + } +} +``` + +```html +
+ +
+ + + + + + + + + + + + + + @foreach (var meeting in Model.Meetings) + { + + + + + + + + + + } + +
@L["Subject"]@L["StartTime"] / @L["EndTime"]@L["ActualStartTime"]@L["CanceledTime"]@L["ReminderTime"]@L["FollowUpTime"]@L["Description"]
@meeting.Subject@Clock.ConvertToUserTime(meeting.StartTime) ➡️ @Clock.ConvertToUserTime(meeting.EndTime)@Clock.ConvertToUserTime(meeting.ActualStartTime)@(meeting.CanceledTime.HasValue ? Clock.ConvertToUserTime(meeting.CanceledTime.Value) : "N/A")@Clock.ConvertToUserTime(meeting.ReminderTime).DateTime@(meeting.FollowUpTime.HasValue ? Clock.ConvertToUserTime(meeting.FollowUpTime.Value).DateTime : "N/A")@meeting.Description
+
+
+
+``` + +![](auth-list.png) + +### Handling Timezone in JavaScript + +In the `Web` project, we use JavaScript to handle timezone. + +#### Displaying Time in UI + +* `timeZoneApp.meetings.meeting.getList` gets all `Meeting` entities and displays them in `DataTables` +* `abp.clock.normalizeToLocaleString()` is the ABP JavaScript API, it converts `UTC` time to the current user's timezone, and then calls its `toLocaleString` method to format time +* `dataFormat: "datetime"` is the ABP DataTable extension method, it calls the `abp.clock.normalizeToLocaleString` method to convert and format time + +> If the current application is not enabled for multi-timezone support, then the `abp.clock.normalizeToLocaleString` method will not convert the time, it will just call the `Date` object's `toLocaleString` method. + +```js +var dataTable = $('#MeetingsTable').DataTable( + abp.libs.datatables.normalizeConfiguration({ + serverSide: true, + paging: true, + order: [[1, "asc"]], + searching: false, + scrollX: true, + ajax: abp.libs.datatables.createAjax(timeZoneApp.meetings.meeting.getList), + columnDefs: [ + { + title: l('Actions'), + rowAction: { + items: + [ + { + text: l('Edit'), + visible: abp.auth.isGranted('TimeZoneApp.Meetings.Edit'), + action: function (data) { + editModal.open({ id: data.record.id }); + }, + }, + { + text: l('Delete'), + visible: abp.auth.isGranted('TimeZoneApp.Meetings.Delete'), + confirmMessage: function (data) { + return l('MeetingDeletionConfirmationMessage', data.record.subject); + }, + action: function (data) { + timeZoneApp.meetings.meeting + .delete(data.record.id) + .then(function() { + abp.notify.info(l('SuccessfullyDeleted')); + dataTable.ajax.reload(); + }); + } + } + ] + } + }, + { + title: l('Subject'), + data: "subject" + }, + { + title: l('StartTime') + ' / ' + l('StartTime'), + data: "startTime", + render: function (data, type, row) { + return abp.clock.normalizeToLocaleString(row.startTime) + ' ➡️ ' + abp.clock.normalizeToLocaleString(row.endTime); + } + }, + { + title: l('ActualStartTime'), + data: "actualStartTime", + dataFormat: "datetime" + }, + { + title: l('CanceledTime'), + data: "canceledTime", + render: function (data, type, row) { + return data ? abp.clock.normalizeToLocaleString(data) : 'N/A'; + } + }, + { + title: l('ReminderTime'), + data: "reminderTime", + dataFormat: "datetime" + }, + { + title: l('FollowUpTime'), + data: "followUpTime", + render: function (data, type, row) { + return data ? abp.clock.normalizeToLocaleString(data) : 'N/A'; + } + }, + { + title: l('Description'), + data: "description" + } + ] + }) +); +``` + +Below is the screenshot of `DataTables`: + +![](mvc-list.png) + + +#### Creating and Editing Meeting + +We use `JavaScript` to create and edit `Meeting`. + +ABP's [TagHelper](https://abp.io/docs/latest/framework/ui/mvc-razor-pages/tag-helpers) can automatically create forms based on the model, it will generate corresponding HTML tags based on the attributes in the model. For `DateTime` and `DateTimeOffset` attributes, it will generate and initialize a [DateTimePicker](https://www.daterangepicker.com/) component. + +**CreateModal** and **EditModal** : + +```html + + + + + + + + + +``` + +```html + + + + + + + + + + +``` + +You can see that the time in the control has been converted to the current user's timezone. + +![](mvc-create.png) + +![](mvc-edit.png) + +When we submit the form, we need to convert the time to `UTC`. In the `JavaScript` of the `Create` and `Edit` pages, we use the `handleDatepicker` this `jQuery` extension method to handle time in the form, it internally gets the user's local time from the selector `input[type="hidden"][data-hidden-datepicker]`, and then uses the `abp.clock.normalizeToString` method to convert the date field in the form to the `ISO 8601` format `UTC` time string. + +> If the current application is not enabled for multi-timezone support, then the `abp.clock.normalizeToString` method will not convert the time, it will just convert to the ISO 8601 format time string without timezone. + +```js +var abp = abp || {}; +$(function () { + abp.modals.meetingCreate = function () { + var initModal = function (publicApi, args) { + var $form = publicApi.getForm(); + $form.find('button[type="submit"]').on('click', function (e) { + $form.handleDatepicker('input[type="hidden"][data-hidden-datepicker]'); + }); + }; + + return { + initModal: initModal + } + }; +}); +``` + +The requested data is as follows: + +```csharp +Request URL: Meetings/EditModal +Request Method: POST +Payload: + Id: 0803780b-3762-2af8-1c75-3a1895d59c89 + Meeting.Subject: ABP Developer Guide + Meeting.StartTime: 2025-03-01T09:30:00.000Z + Meeting.EndTime: 2025-03-01T10:30:00.000Z + Meeting.ActualStartTime: 2025-03-01T11:30:00.000Z + Meeting.CanceledTime: + Meeting.ReminderTime: 2025-03-01T12:30:00.000Z + Meeting.FollowUpTime: 2025-03-01T13:30:00.000Z + Meeting.Description: We will discuss the ABP developer guide. +``` + +![](mvc-post.png) + +In short, we use the `abp.clock.normalizeToLocaleString` method to display time, and use the `abp.clock.normalizeToString` method to modify the time to be submitted. If you submit data via `ajax`, please remember to use the `abp.clock.normalizeToString` method to convert time. + +### Handling Timezone in Blazor + +We cannot automatically complete some work in `Blazor UI`, we need to inject `IClock` and use the `ConvertToUserTime` and `ConvertToUtc` methods to display and create/update entities. + +Below is a complete `Meeting` page, please refer to the usage of `Clock` in it. + +```csharp +@page "/meetings" +@using Volo.Abp.Application.Dtos +@using Microsoft.Extensions.Localization +@using TimeZoneApp.Meetings +@using TimeZoneApp.Localization +@using TimeZoneApp.Permissions +@using Volo.Abp.AspNetCore.Components.Web +@inject IStringLocalizer L +@inject AbpBlazorMessageLocalizerHelper LH +@inherits AbpCrudPageBase + + + + + +

@L["Meetings"]

+
+ + @if (HasCreatePermission) + { + + } + +
+
+ + + + + + + + + + + + + + + @Clock.ConvertToUserTime(context.StartTime).ToString("yyyy-MM-dd HH:mm:ss") ➡️ @Clock.ConvertToUserTime(context.EndTime).ToString("yyyy-MM-dd HH:mm:ss") + + + + + @Clock.ConvertToUserTime(context.ActualStartTime).ToString("yyyy-MM-dd HH:mm:ss") + + + + + @(context.CanceledTime.HasValue ? Clock.ConvertToUserTime(context.CanceledTime.Value).ToString("yyyy-MM-dd HH:mm:ss") : "N/A") + + + + + @(Clock.ConvertToUserTime(context.ReminderTime).ToString("yyyy-MM-dd HH:mm:ss") ) + + + + + @(context.FollowUpTime.HasValue ? Clock.ConvertToUserTime(context.FollowUpTime.Value).ToString("yyyy-MM-dd HH:mm:ss") : "N/A") + + + + + + + +
+ + + +
+ + @L["NewMeeting"] + + + + + + + @L["Subject"] + + + + + + + + + @L["StartTime"] / @L["EndTime"] + + + + @L["ActualStartTime"] + + + + @L["CanceledTime"] + + + + @L["ReminderTime"] + + + + @L["FollowUpTime"] + + + + + @L["Description"] + + + + + + + + + + + + + +
+
+
+ + + +
+ + @EditingEntity.Subject + + + + + + + @L["Subject"] + + + + + + + + + @L["StartTime"] / @L["EndTime"] + + + + @L["ActualStartTime"] + + + + @L["CanceledTime"] + + + + @L["ReminderTime"] + + + + @L["FollowUpTime"] + + + + + @L["Description"] + + + + + + + + + + + + + +
+
+
+ + +@code { + IReadOnlyList SelectedDates; + + public Meeting() + { + CreatePolicyName = TimeZoneAppPermissions.Meetings.Create; + UpdatePolicyName = TimeZoneAppPermissions.Meetings.Edit; + DeletePolicyName = TimeZoneAppPermissions.Meetings.Delete; + } + + protected override async Task OpenCreateModalAsync() + { + await base.OpenCreateModalAsync(); + + var now = DateTime.Now; + SelectedDates = new List { now.Date.AddHours(10),now.Date.AddDays(7).AddHours(10) }; + NewEntity.ActualStartTime = now.Date.AddHours(11); + NewEntity.CanceledTime = now.Date.AddHours(12); + NewEntity.ReminderTime = now.Date.AddHours(13); + NewEntity.FollowUpTime = now.Date.AddHours(14); + } + + protected override Task OnCreatingEntityAsync() + { + if (SelectedDates.Count == 2 && SelectedDates[0].HasValue && SelectedDates[1].HasValue) + { + NewEntity.StartTime = Clock.ConvertToUtc(SelectedDates[0]!.Value); + NewEntity.EndTime = Clock.ConvertToUtc(SelectedDates[1]!.Value); + } + + NewEntity.ActualStartTime = Clock.ConvertToUtc(NewEntity.ActualStartTime); + NewEntity.CanceledTime = NewEntity.CanceledTime.HasValue ? Clock.ConvertToUtc(NewEntity.CanceledTime.Value) : null; + + NewEntity.ReminderTime = Clock.ConvertToUtc(NewEntity.ReminderTime.DateTime); + NewEntity.FollowUpTime = NewEntity.FollowUpTime.HasValue ? Clock.ConvertToUtc(NewEntity.FollowUpTime.Value.DateTime) : null; + + return Task.CompletedTask; + } + + protected override async Task OpenEditModalAsync(MeetingDto entity) + { + await base.OpenEditModalAsync(entity); + + SelectedDates = new List { Clock.ConvertToUserTime(EditingEntity.StartTime), Clock.ConvertToUserTime(EditingEntity.EndTime) }; + EditingEntity.ActualStartTime = Clock.ConvertToUserTime(EditingEntity.ActualStartTime); + EditingEntity.CanceledTime = EditingEntity.CanceledTime.HasValue ? Clock.ConvertToUserTime(EditingEntity.CanceledTime.Value) : null; + EditingEntity.ReminderTime = Clock.ConvertToUserTime(EditingEntity.ReminderTime); + EditingEntity.FollowUpTime = EditingEntity.FollowUpTime.HasValue ? Clock.ConvertToUserTime(EditingEntity.FollowUpTime.Value) : null; + } + + protected override Task OnUpdatingEntityAsync() + { + if (SelectedDates.Count == 2 && SelectedDates[0].HasValue && SelectedDates[1].HasValue) + { + EditingEntity.StartTime = Clock.ConvertToUtc(SelectedDates[0]!.Value); + EditingEntity.EndTime = Clock.ConvertToUtc(SelectedDates[1]!.Value); + } + + EditingEntity.ActualStartTime = Clock.ConvertToUtc(EditingEntity.ActualStartTime); + EditingEntity.CanceledTime = EditingEntity.CanceledTime.HasValue ? Clock.ConvertToUtc(EditingEntity.CanceledTime.Value) : null; + + return Task.CompletedTask; + } +} +``` + +![](blazor-list.png) + +![](blazor-create.png) + +![](blazor-edit.png) + +## Timezone Settings Change + +If the timezone settings change, then all times will be converted to the new timezone. For example, if the current timezone changes from `Europe/Istanbul` to `Europe/Berlin`, then all times will be converted to the `Europe/Berlin` timezone. + +![](berlin.png) + +`Europe/Istanbul`: + +![](auth-list-utc3.png) + +![](mvc-list-utc3.png) + +`Europe/Berlin`: + +![](auth-list-utc1.png) + +![](mvc-list-utc1.png) + +## Browser Timezone Detection + +When no timezone setting is configured, ABP's MVC, Blazor, and Angular applications will automatically detect the browser's timezone during initialization. The detected timezone is then stored in either the request's Cookie or Header. + +This functionality is implemented by the `UseAbpTimeZone` middleware, which follows a specific order to determine the appropriate timezone: + +1. First, it attempts to retrieve the timezone from the application/tenant/user settings +2. If no setting is found, it tries to get the timezone from the request information, including Cookie, Header, QueryString, and Form +3. Finally, if no timezone information is found, it falls back to using the server's timezone as the default + +> The timezone information is stored using the key `__timezone` + +## TimeZoneApp Source Code + +You can download and view the [TimeZoneApp source code](https://github.com/maliming/TimeZone) for detailed implementation. + +## Summary + +Through this article, we learned how to handle timezone in different types of UIs. I hope this article is helpful to you. If you have any questions, please contact me at any time. diff --git a/docs/en/Community-Articles/2025-03-18-Using-Vue-Components/post.md b/docs/en/Community-Articles/2025-03-18-Using-Vue-Components/post.md index ad5d19e22d..09998169e6 100644 --- a/docs/en/Community-Articles/2025-03-18-Using-Vue-Components/post.md +++ b/docs/en/Community-Articles/2025-03-18-Using-Vue-Components/post.md @@ -4,6 +4,10 @@ In modern web development, integrating dynamic front-end frameworks with server- This article won't use any SPA approach. The goal of this article is to use Razor Pages with simple Vue components to eliminate jQuery while developing MVC application. +> **🎉 Also video version is available!** +> +> [Watch on YouTube Now!](https://youtu.be/sZ8iSMovHZs?si=GynuJjsLEI1p2g6w) + ## Creating the Solution Let's create a simple TODO list application to demonstrate how to use Vue components in Razor Pages. I'll build a really simple backend without a connection to a database for demonstration purposes. We will focus on the frontend part. @@ -71,13 +75,13 @@ Let's create a simple Vue component to display the TODO list. ```html
- +
``` - Navigate to the `Index.cshtml.js` file and add the following code: ```js -Vue.component('todo-component', { +Vue.component('message-component', { template: '
Hello, {{ message }}
', props: ['message'] }); diff --git a/docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/1.png b/docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/1.png new file mode 100644 index 0000000000..f5cabbd4ef Binary files /dev/null and b/docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/1.png differ diff --git a/docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/2.png b/docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/2.png new file mode 100644 index 0000000000..0aee0bf048 Binary files /dev/null and b/docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/2.png differ diff --git a/docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/3.png b/docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/3.png new file mode 100644 index 0000000000..6c008bc372 Binary files /dev/null and b/docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/3.png differ diff --git a/docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/post.md b/docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/post.md new file mode 100644 index 0000000000..5664fd8091 --- /dev/null +++ b/docs/en/Community-Articles/2025-03-23-Understanding-the-Embedded-Files-in-ABP-Framework/post.md @@ -0,0 +1,248 @@ +# Understanding the Embedded Files in ABP Framework + +Embedded Files functionality in .NET applications allows external files (like configuration files, images, etc.) to be directly embedded into assemblies (.exe or .dll). This simplifies deployment, prevents file loss or tampering, improves security and performance, and reduces path and dependency management issues. Through embedded resources, programs can access these files more conveniently without additional file operations. + +## Embedding Files in Your Project + +We embed `Volo\Abp\MyModule\Localization\*.json` files into the assembly in our `MyModule.csproj`. + +```xml + + + + net9.0 + Exe + enable + + + + + + + + + + + + + +``` + +If we check the `en.json` file in our IDE, we'll see it's embedded in the assembly. + +![image](1.png) + +When we decompile the built `MyModule.dll` file, we can also see the `en.json` file. + +![image](2.png) + +## Accessing Embedded Files in Code + +```csharp +public class Program +{ + public static async Task Main(string[] args) + { + var embeddedFiles = typeof(Program).Assembly.GetManifestResourceNames(); + foreach (var embeddedFile in embeddedFiles) + { + Console.WriteLine(embeddedFile); + var fileStream = typeof(Program).Assembly.GetManifestResourceStream(embeddedFile); + if (fileStream != null) + { + using var reader = new System.IO.StreamReader(fileStream); + var content = await reader.ReadToEndAsync(); + Console.WriteLine(content); + } + } + } +} +``` +This code will output the embedded file names and their contents. + +``` +MyModule.Volo.Abp.MyModule.Localization.en.json + +{ + "key":"value" +} +``` + +## Integrating with ABP Virtual File System + +The ABP Virtual File System makes it possible to manage files that don't physically exist on the file system (disk). It's mainly used to embed (js, css, image..) files into assemblies and use them like physical files at runtime. + +The following code shows how to add embedded files from the current application assembly to the ABP virtual file system: + +```csharp +[DependsOn(typeof(AbpVirtualFileSystemModule))] +public class MyModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + } +} +``` + +ABP creates an `AbpEmbeddedFileProvider` to access the embedded files. + +The full name of `en.json` is `MyModule.Volo.Abp.MyModule.Localization.en.json`. Without directory information, ABP uses `.` to split and assume directory information. This creates the following directory structure in the virtual file system: + +``` +[Dir] [/MyModule] +[Dir] [/MyModule/Volo] +[Dir] [/MyModule/Volo/Abp] +[Dir] [/MyModule/Volo/Abp/MyModule] +[Dir] [/MyModule/Volo/Abp/MyModule/Localization] +[File] [/MyModule/Volo/Abp/MyModule/Localization/en.json] +``` + +Now you can inject `IVirtualFileProvider` to access embedded files using the directory/file structure above. + +## Manifest Embedded File Provider + +You might have noticed that using `.` to split and assume directory information can cause confusion if filenames contain dots. + +For example, if your filename is `zh.hans.json`, ABP will generate the following directory structure, which isn't what we want: + +``` +[Dir] [/MyModule] +[Dir] [/MyModule/Volo] +[Dir] [/MyModule/Volo/Abp] +[Dir] [/MyModule/Volo/Abp/MyModule] +[Dir] [/MyModule/Volo/Abp/MyModule/Localization] +[Dir] [/MyModule/Volo/Abp/MyModule/Localization/zh] +[File] [/MyModule/Volo/Abp/MyModule/Localization/zh/hans.json] +``` + +Microsoft provides the `Microsoft.Extensions.FileProviders.Manifest` library to solve this problem. + +We need to add this package dependency and set `true` in our project: + +```xml + + + + net9.0 + Exe + enable + true + + + + + + + + + + + + + + +``` + +After rebuilding the project, when we decompile `MyModule.dll`, we'll see an additional `Microsoft.Extensions.FileProviders.Embedded.Manifest.xml` file. + +![image](3.png) + +This manifest file stores all the directory and file information of embedded resources. When ABP finds this file, it will use `ManifestEmbeddedFileProvider` instead of `AbpEmbeddedFileProvider` to access embedded files: + +```xml + + + 1.0 + + + Microsoft.Extensions.FileProviders.Embedded.Manifest.xml + + + + + + + MyModule.Volo.Abp.MyModule.Localization.zh.hans.json + + + + + + + +``` + +## Parameters of AddEmbedded Method + +The `AddEmbedded` method can take two parameters: + +### baseNamespace + +This may only be needed if you haven't used the `Manifest Embedded File Provider` and your project's `root namespace` isn't empty. In this case, set your root namespace here. + +The `root namespace` is your project's name by default. You can change it or set it to empty in the `csproj` file. + +```xml + + + MyModule + + +``` + +```xml + + + + + +``` + +```csharp +Configure(options => +{ + options.FileSets.AddEmbedded(baseNamespace: "MyModule"); +}); +``` + +``` +[Dir] [/Volo] +[Dir] [/Volo/Abp] +[Dir] [/Volo/Abp/MyModule] +[Dir] [/Volo/Abp/MyModule/Localization] +[File] [/Volo/Abp/MyModule/Localization/en.json] +``` + +### baseFolder + +If you don't want to expose all embedded files in the project, but only want to expose a specific folder (and sub folders/files), you can set the base folder relative to your project root folder. + +> baseFolder is only effective when using `Manifest Embedded File Provider`. + +You can set the `baseFolder` parameter to `/Volo/Abp/MyModule`, resulting in this directory structure: + +```csharp +Configure(options => +{ + options.FileSets.AddEmbedded(baseFolder: "/Volo/Abp/MyModule"); +}); +``` + +``` +[Dir] [Localization] +[File] [Localization/en.json] +``` + +## Summary + +We recommend using the `Manifest Embedded File Provider` in your projects and libraries. Hope this article has been helpful. + +## References + +[ABP Virtual File System](https://abp.io/docs/latest/framework/infrastructure/virtual-file-system) + +[Manifest Embedded File Provider](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/file-providers#manifest-embedded-file-provider) diff --git a/docs/en/Community-Articles/2025-03-24-How-to-Change-the-CurrentUser-in-ABP/POST.md b/docs/en/Community-Articles/2025-03-24-How-to-Change-the-CurrentUser-in-ABP/POST.md new file mode 100644 index 0000000000..b74aecdbe4 --- /dev/null +++ b/docs/en/Community-Articles/2025-03-24-How-to-Change-the-CurrentUser-in-ABP/POST.md @@ -0,0 +1,115 @@ +# How to Change the CurrentUser in ABP? + +[ABP Framework](https://abp.io/) provides a powerful service for accessing information about the currently authenticated user in your application. Understanding how to use and modify this service (`ICurrentUser`) is essential for both basic and certain advanced scenarios. + +In this article, we'll explore the [`CurrentUser` service](https://abp.io/docs/latest/framework/infrastructure/current-user), its use cases, and how to change it when necessary. + +## Understanding the ICurrentUser Service + +The `ICurrentUser` interface is the primary service in ABP Framework for obtaining information about the logged-in user. It provides some key properties, such as `Id`, `UserName`, `TenantId`, `Roles` (roleNames), and more... + +`ICurrentUser` is implemented on the `ICurrentPrincipalAccessor` service and works with claims as well. So, all of these properties are actually retrieved from the claims. ICurrentUser has some methods to directly work with the claims, such as: + +* FindClaim (finds a single claim by name) +* FindClaims (gets all claims with the given name) +* IsInRole (checks if the user has a specific role) +* GetAllClaims (gets all claims of the user) + +## Where the CurrentUser Service is Used? + +The CurrentUser service is used extensively throughout ABP applications whenever there's a need to access information about the logged-in user. Common scenarios include: authorization checks, logging, setting common properties like `CreatorId`, `LastModifierId`, `DeleterId`, and more... + +## When to Change the CurrentUser Service? + +While the CurrentUser service works automatically in the context of HTTP requests (it gets the `User` property of the current `HttpContext`), there are advanced scenarios where you might need to manually set or change the current user: + +1. **Background workers:** When executing code outside the context of a user request +2. **Event handlers:** When processing events that may run in a different context +3. **Unit & integration tests:** When simulating a user for testing purposes + +## How to Change the CurrentUser Service? + +If you need to change the CurrentUser service, you can inject the `ICurrentPrincipalAccessor` service, use its `Change` method to change the current user, and then use the `CurrentUser` service as usual. + +Here's how to change the current user for a specific scope: + +```csharp +using System.Security.Claims; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.Identity; +using Volo.Abp.Security.Claims; + +namespace MyProject.Products; + +public class ProductEventHandler : IDistributedEventHandler, ITransientDependency +{ + private readonly IProductRepository _productRepository; + private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor; + private readonly IdentityUserManager _userManager; + + public ProductEventHandler( + IProductRepository productRepository, + ICurrentPrincipalAccessor currentPrincipalAccessor, + IdentityUserManager userManager + ) + { + _productRepository = productRepository; + _currentPrincipalAccessor = currentPrincipalAccessor; + _userManager = userManager; + } + + public async Task HandleEventAsync(OrderPlacedEto eventData) + { + var product = await _productRepository.FindAsync(eventData.ProductId); + if (product == null) + { + return; + } + + //Get the admin user + var adminUser = await _userManager.FindByNameAsync("admin"); + if (adminUser == null) + { + return; + } + + var newPrincipal = new ClaimsPrincipal(new ClaimsIdentity( + new Claim[] { + new Claim(AbpClaimTypes.UserId, adminUser.Id.ToString()), + new Claim(AbpClaimTypes.UserName, "admin"), + })); + + //IMPORTANT: It will set the CreatorId, LastModifierId, etc. with the admin user + using (_currentPrincipalAccessor.Change(newPrincipal)) + { + product.StockCount -= eventData.Quantity; + + // Update the product + await _productRepository.UpdateAsync(product); + } + } +} +``` + +In this example, we have a distributed event handler that processes an `OrderPlacedEto` event. When an order is placed, we need to update the product's stock count. However, we want this operation to be performed under an admin user's context for auditing purposes. + +Here's what the code does step by step: + +1. First, it retrieves the product using the product ID from the event data. +2. Then, it finds the admin user by username using the `_userManager.FindByNameAsync("admin")`. +3. A new `ClaimsPrincipal` is created with the admin user's claims (`UserId` and `UserName`). +4. Using the `_currentPrincipalAccessor.Change()` method within a `using` statement, it temporarily changes the current user context to the admin user. +5. Inside this scope, it updates the product's stock count by subtracting the ordered quantity. +6. Finally, it saves the changes to the database using the repository. + +**The important part here is that any audit properties (like `CreatorId`, `LastModifierId`, etc.) will be set to the admin user's ID because we changed the current principal. Once the using block ends, the original user context is automatically restored.** + +This pattern is particularly useful in background jobs, event handlers, or any scenario where you need to perform operations under a specific user's context, regardless of the actual authenticated user. + +## Conclusion + +The `CurrentUser` service in ABP Framework provides a simple way to access information about the authenticated user. While it works automatically in most scenarios, there are cases where you need to explicitly change the current user identity, particularly in background processing scenarios. + +By using the ICurrentPrincipalAccessor.Change() method within a using statement, you can temporarily change the current user for a specific scope of execution, enabling your background processes, event handlers, or tests to execute with the identity of a specific user. \ No newline at end of file diff --git a/docs/en/Community-Articles/2025-03-27-Announcing-the-ABP-Contributor-Program/post.md b/docs/en/Community-Articles/2025-03-27-Announcing-the-ABP-Contributor-Program/post.md new file mode 100644 index 0000000000..7767a6673b --- /dev/null +++ b/docs/en/Community-Articles/2025-03-27-Announcing-the-ABP-Contributor-Program/post.md @@ -0,0 +1,38 @@ +We are excited to introduce the ABP Contributor Program, which is an amazing opportunity for community enthusiasts to contribute to the ABP ecosystem while getting valuable benefits\! + +**Create, Contribute and Be Part of Something Bigger** + +This is your opportunity to not only share your knowledge but also to grow alongside a passionate community of developers and creators. When you create and contribute to the ABP ecosystem, you’re not just building content, you’re also helping shape the future of ABP. + +## **Why Become an ABP Contributor?** + +By sharing your knowledge, creating valuable content, and engaging with the ABP Community, you’ll enjoy a range of benefits, including: + +* Free ABP Personal License – If you meet all the requirements, you’ll receive an ABP Personal License at no cost + +* Community Badge & Title – Stand out in the ABP Community with a special contributor badge and label next to your name. + +* Exclusive Discord Role – Get a unique Contributor role on the ABP Community Discord server. + +* Increased Visibility – Your contributions will be recognized by hundreds of thousands of developers worldwide. + + Completely Free – No fees, just log in, contribute, and start collecting benefits\! + +## **How to Apply?** + +If you’re passionate about ABP and think you meet the required criteria, follow these simple steps: + +Create content related to ABP: blog posts, tutorials, videos, or documentation. You can publish it on the ABP Community website or any external platform (just make sure to submit it to the ABP Community site). + +Apply by email: Send your application to marketing@volosoft.com, and the ABP Team will review your eligibility. + +Periodic Assessments: The team reviews submissions every three months to determine and renew contributor status. Keep contributing to continue enjoying the perks\! + +## **Join Us & Grow\!** + +This is your chance to give back to the community, grow your presence, and get rewarded for your contributions. Whether you love writing documentation, sharing tutorials, or building open-source projects, your efforts matter. + +Apply today and be part of something impactful\! + + +[image1]: images/img_1.png \ No newline at end of file diff --git a/docs/en/Community-Articles/2025-04-20-IDX10204/POST.md b/docs/en/Community-Articles/2025-04-20-IDX10204/POST.md new file mode 100644 index 0000000000..5de0650d11 --- /dev/null +++ b/docs/en/Community-Articles/2025-04-20-IDX10204/POST.md @@ -0,0 +1,170 @@ +# Common Errors in JWT Bearer Authentication + +When implementing JWT Bearer authentication in an ABP(tiered) application, you might occasionally encounter errors starting with `IDX`. These errors are related to JWT Bearer Token validation and this article will help you understand and resolve them. + +## Enable JWT Bearer authentication + +Your API project usually contains the following code, which enables JWT Bearer authentication and makes it as the default authentication scheme. + +We simply configure the JWT's `Authority` and `Audience` properties, and it will work fine. + +```csharp +context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.Authority = "https://localhost:44301/"; //configuration["AuthServer:Authority"]; + options.Audience = "MyProjectName"; + }); +``` + +> `AddJwtBearer` and `AddAbpJwtBearer` will do the same thing, but `AddAbpJwtBearer` is recommended. + +## JWT authentication process + +Let's take a look at how the above code works. + +A JWT Token usually consists of three parts: `Header`, `Payload`, and `Signature`. + +- `Header`: Contains the type and signing algorithm of the token +- `Payload`: Contains the claims of the token, including `sub`, `aud`, `exp`, `iat`, `iss`, `jti`, `preferred_username`, `given_name`, `role`, `email`, etc. +- `Signature`: The cryptographic signature of the token used to verify its authenticity + +Here is an example of a JWT Token issued by `AuthServer(OpenIddict)`: + +The `Header` part: + +![JWT Header](header.png) + +The `Payload` part: + +![JWT Payload](payload.png) + +### TokenValidationParameters + +In the `JwtBearerOptions`, there is a `TokenValidationParameters` property, which is used to validate the JWT Token. + +The default implementation for JWT Token validation is `JsonWebTokenHandler`, which comes from the [Microsoft.IdentityModel.JsonWebTokens](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/) package. + +We didn't set the `TokenValidationParameters` property in the code above, so the default values below will be used: + +```csharp +//... +TokenValidationParameters.ValidateAudience = true +TokenValidationParameters.ValidAudience = "MyProjectName" +TokenValidationParameters.ValidAudiences = null + +TokenValidationParameters.ValidateIssuer = true +TokenValidationParameters.ValidIssuer = null +TokenValidationParameters.ValidIssuers = null +//... +``` + +### JWT Bearer Token Validation Process + +During JWT Bearer authentication, API website will get the token from the HTTP request and validate it. + +The `JsonWebTokenHandler` will get the `OpenID Connect` metadata from the `AuthServer`, it will be used in the validation process, the current metadata request address is: https://localhost:44301/.well-known/openid-configuration , it is a fixed address calculated from the `Authority` property. + +First, the token's Signature is verified using the public key obtained from `OpenID Connect` metadata(https://localhost:44301/.well-known/jwks). + +Then, the payload is validated. The payload is a JSON object containing essential information such as the `token type`, `expiration time`, `issuer`, and `audience` etc. + +Most of the validation problems we may encounter are payload validation failures, for example: + +#### Lifetime + +If the token in your request has expired, the validation will fail. You will see the exception information like `IDX10230` in the log. + +#### Audience + +The `ValidAudience` of `TokenValidationParameters` is `MyProjectName`, the `aud` in the payload of the token is also `MyProjectName`, if the token does not contain `aud` or the `aud` does not match, the validation will fail. You may see the exception information like `IDX10206`, `IDX10277` or `IDX10208`. + +> If the `ValidateAudience` of `TokenValidationParameters` is `false`, then the `aud` will not be validated. + +#### Issuer + +The default value of `TokenValidationParameters.ValidateIssuer` is `true`, it requires the token's payload to contain the `issuer` field, and it must match one of `TokenValidationParameters.ValidIssuer` or `TokenValidationParameters.ValidIssuers`. + +> The default value of `ValidIssuer` or `ValidIssuers` is `null`, it will use the `issuer` from the `OpenID Connect` metadata as the default value. + +1. If the token's payload does not contain the `issuer` field, you may see the error `IDX10211`. +2. If the API website cannot get the `OpenID Connect` metadata from AuthServer website, the validation will fail. You may see the error `IDX10204`, the full exception message is: `IDX10204: Unable to validate issuer. validationParameters.ValidIssuer is null or whitespace AND validationParameters.ValidIssuers is null or empty.` +3. If the `issuer` does not match, the validation will fail. You may see the error `IDX10205` in the log. + +> If the `ValidateIssuer` of `TokenValidationParameters` is `false`, then the `issuer` will not be validated. + +> Please note that `OpenIddict` will use the current HTTP request information as the value of `issuer`. If the AuthServer website is deployed behind a reverse proxy or similar deployment configurations, the `issuer` in the token may not be the value you expect. In this case, please specify it manually. + +```csharp +PreConfigure(serverBuilder => +{ + serverBuilder.SetIssuer("https://localhost:44301/"); +}); +``` + +## Troubleshooting + +To troubleshoot any `IDX` errors during JWT authentication, you can enable detailed logging by configuring the `identitymodel` logs as follows: + +```csharp +using System.Diagnostics.Tracing; +using Microsoft.IdentityModel.Logging; + +public class Program +{ + public async static Task Main(string[] args) + { + IdentityModelEventSource.ShowPII = true; + IdentityModelEventSource.Logger.LogLevel = EventLevel.Verbose; + var wilsonTextLogger = newTextWriterEventListener("Logs/identitymodel.txt"); + wilsonTextLogger.EnableEvents(IdentityModelEventSource.Logger, EventLevel.Verbose); + + //... + } +} +``` + +Additionally, you can enable `OpenIddict`'s `Verbose` logs for more detailed debugging information: + +```csharp +var loggerConfiguration = new LoggerConfiguration() + .MinimumLevel.Debug() + .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning) + .MinimumLevel.Override("OpenIddict", LogEventLevel.Verbose) + .Enrich.FromLogContext() + .WriteTo.Async(c => c.File("Logs/logs.txt")) +``` + +## Summary + +For JWT authentication, you need to pay attention to the following key points: + +1. Ensure your API website can communicate with the AuthServer properly +2. Verify that the `aud` claim in your token matches the expected audience +3. Confirm that the `issuer` claim in your token is valid and matches the configuration + +You can customize the `JwtBearerOptions`'s `TokenValidationParameters` to modify the validation rules to meet your actual needs. + +For example, if your `issuer` needs to support multiple subdomains, you can use the [Owl.TokenWildcardIssuerValidator](https://github.com/maliming/Owl.TokenWildcardIssuerValidator) library to customize the validation. + +```csharp +services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.Authority = "https://abp.io"; + options.Audience = "abp_io"; + + options.TokenValidationParameters.IssuerValidator = TokenWildcardIssuerValidator.IssuerValidator; + options.TokenValidationParameters.ValidIssuers = new[] + { + "https://{0}.abp.io" + }; + }); +``` + +## References + +- [Configure JWT bearer authentication in ASP.NET Core]([https://learn.microsoft.com/en-us/aspnet/core/security/authentication/jwt-auth?view=aspnetcore-8.0](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/configure-jwt-bearer-authentication)) +- [OpenIddict](https://github.com/openiddict/openiddict-core) +- [IdentityModel](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) +- [Owl.TokenWildcardIssuerValidator](https://github.com/maliming/Owl.TokenWildcardIssuerValidator) diff --git a/docs/en/Community-Articles/2025-04-20-IDX10204/header.png b/docs/en/Community-Articles/2025-04-20-IDX10204/header.png new file mode 100644 index 0000000000..a71c9a66ff Binary files /dev/null and b/docs/en/Community-Articles/2025-04-20-IDX10204/header.png differ diff --git a/docs/en/Community-Articles/2025-04-20-IDX10204/payload.png b/docs/en/Community-Articles/2025-04-20-IDX10204/payload.png new file mode 100644 index 0000000000..a83d6e1051 Binary files /dev/null and b/docs/en/Community-Articles/2025-04-20-IDX10204/payload.png differ diff --git a/docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/POST.md b/docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/POST.md new file mode 100644 index 0000000000..f79fa21ce8 --- /dev/null +++ b/docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/POST.md @@ -0,0 +1,262 @@ +# Using Microsoft AI Extensions Library and OpenAI to Summarize User Comments + +Either you are building an e-commerce application or a simple blog, **user comments** (about your products or blog posts) **can grow rapidly**, making it harder for users to get the gist of discussions at a glance. AI is a pretty good tool to solve the problem. By using AI, you can **summarize all the user comments** and show a single paragraph to your users, so they can easily understand the overall thought of users about the product or the blog post. + +In this tutorial, we’ll walk through a real-life implementation of using AI to summarize multiple user comments in an application. I will implement the solution based on ABP's **[CMS Kit](https://abp.io/docs/latest/modules/cms-kit)** library, as it already features a **[commenting system](https://abp.io/docs/latest/modules/cms-kit/comments)** and a [demo application](https://cms-kit-demo.abpdemo.com/) that displays user comments on **[gallery images](https://cms-kit-demo.abpdemo.com/image-gallery)** (it has not a comment summary feature yet, we will implement it in this tutorial). + +## A Screenshot + +Here, an example screenshot from the application with the comment summary feature: + +![comment-example](comment-example.png) + +## Cloning the Repository + +If you want to follow the development, you can clone the [CMS Kit Demo repository](https://github.com/abpframework/cms-kit-demo) to your computer and make it running by following the instructions on the [README file](https://github.com/abpframework/cms-kit-demo?tab=readme-ov-file#cms-kit-demo). + +I suggest to you to play a little with [the application](https://cms-kit-demo.abpdemo.com/) (create a new user for yourself, add some comments to the images in the gallery), so you understand how it works. + +## Preparing the Solution for AI + +I will use [Microsoft AI Extensions Library](https://learn.microsoft.com/en-us/dotnet/ai/ai-extensions) to use the AI features. It is an abstraction library that can work with multiple AI models and tools. I will use an OpenAI model in the demo. + +The first step is to add the [Microsoft.Extensions.AI.OpenAI](http://nuget.org/packages/Microsoft.Extensions.AI.OpenAI) NuGet package to the project: + +````bash +dotnet add package Microsoft.Extensions.AI.OpenAI --prerelease +```` + +>The Microsoft AI Extensions Library was in preview at the time when I wrote this article. If it has a stable release now, you can remove the `--prerelease` parameter for the preceding command. + +We will store the OpenAI key and model name in user secrets. So, locate the root path of the CMS Kit project (`src\CmsKitDemo` folder) and execute the following commands in order in a command-line terminal: + +````bash +dotnet user-secrets init +dotnet user-secrets set OpenAIKey +dotnet user-secrets set ModelName +```` + +For this example, you need to have an [OpenAI API Key](https://platform.openai.com/). That's all. Now, we are ready to use the AI. + +## Implementing the AI Summarization + +Let's start from the most important point of this article: Comment summarization. I will create a class named `AiCommentSummarizer` to implement the summarization work. Here, the full content of that class: + +````csharp +using System.Text; +using Microsoft.Extensions.AI; +using OpenAI; +using Volo.Abp.DependencyInjection; + +namespace CmsKitDemo.Utils; + +public class AiCommentSummarizer : ITransientDependency +{ + private readonly IConfiguration _configuration; + + public AiCommentSummarizer(IConfiguration configuration) + { + _configuration = configuration; + } + + public async Task SummarizeAsync(string[] commentTexts) + { + // Get the model and key from the configuration + var aiModel = _configuration["ModelName"]; + var apiKey = _configuration["OpenAIKey"]; + + if (aiModel.IsNullOrEmpty() || apiKey.IsNullOrEmpty()) + { + return ""; + } + + // Create the IChatClient + var client = new OpenAIClient(apiKey) + .GetChatClient(aiModel) + .AsIChatClient(); + + // Create a prompt (input for AI) + var promptBuilder = new StringBuilder(); + + promptBuilder.AppendLine( + @"There are comments from different users of our website about an image. +We want to summarize the comments into a single comment. +Return a single comment with a maximum of 512 characters. Comments are separated by a newline character and given below." + ); + promptBuilder.AppendLine(); + + foreach (var commentText in commentTexts) + { + promptBuilder.AppendLine("User comment:"); + promptBuilder.AppendLine(commentText); + promptBuilder.AppendLine(); + } + + // Submit the prompt and get the response + var response = await client.GetResponseAsync( + promptBuilder.ToString(), + new ChatOptions { MaxOutputTokens = 1024 } + ); + + return response.Text; + } +} +```` + +That class is pretty simple and already decorated with comments: + +* First, we are getting the API Key and an OpenAI model name from user secrets. I used `gpt-4.1` as the model name, but you can use another available model. +* Then we are obtaining an `IChatClient` reference for OpenAI. `IChatClient` interface is an abstraction that is provided by the [Microsoft AI Extensions Library](https://learn.microsoft.com/en-us/dotnet/ai/ai-extensions) library, so we can implement rest of the code independently from OpenAI. +* Then we continue by building a proper prompt (input) for the AI operation. +* And finally we are using the AI to generate a response (the summary). + +At this point, all the AI-related work has already been done. The rest of this article explains how to integrate that summarization feature with the [CMS Kit Demo application](https://cms-kit-demo.abpdemo.com/). + +## Adding a CommentsSummary Property to the GalleryImage Entity + +The `GalleryImage` entity is used to represent an image on [the image gallery](https://cms-kit-demo.abpdemo.com/image-gallery). I add a `CommentsSummary` property to that entity: + +````csharp +public class GalleryImage : CreationAuditedAggregateRoot +{ + public string Description { get; set; } + + public Guid CoverImageMediaId { get; set; } + + public string CommentsSummary { get; set; } // The new property is here + + //... +} +```` + +Since the CMS Kit Demo application uses Entity Framework Core, I need to add a new database schema migration and update the database: + +````bash +dotnet ef migrations add Added_Summary_To_GalleryImage +dotnet ef database update +```` + +## Updating the Summary + +Great, we have a `GalleryImage.CommentsSummary` property now. But, how will it be updated when a users adds or removes a comment for an image? To implement that; + +* We will listen all the change events for user comments (when a user adds, removes or updates a comment). +* Whenever a comment is changed, we will find the related gallery image, retrieve all the user comments for this image, use the `AiCommentSummarizer` class to summarize all the comments. +* Finally, we wil set the `GalleryImage.CommentsSummary` property with the generated summary text. + +Here, the implementation: + +````csharp +using CmsKitDemo.Entities; +using CmsKitDemo.Utils; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.EventBus; +using Volo.CmsKit.Comments; + +namespace CmsKitDemo.EventHandlers; + +public class GalleryImageCommentListener : + ILocalEventHandler>, + ITransientDependency +{ + private readonly IRepository _galleryImageRepository; + private readonly IRepository _commentRepository; + private readonly AiCommentSummarizer _aiCommentSummarizer; + + public GalleryImageCommentListener( + IRepository galleryImageRepository, + IRepository commentRepository, + AiCommentSummarizer aiCommentSummarizer) + { + _galleryImageRepository = galleryImageRepository; + _commentRepository = commentRepository; + _aiCommentSummarizer = aiCommentSummarizer; + } + + public async Task HandleEventAsync(EntityChangedEventData eventData) + { + var comment = eventData.Entity; + + //Here, we only interest in comments related to image gallery items + if (comment.EntityType != CmsKitDemoConsts.ImageGalleryEntityType) + { + return; + } + + if (!Guid.TryParse(comment.EntityId, out var galleryImageId)) + { + return; + } + + // Get the related image from database + var galleryImage = await _galleryImageRepository.FindAsync(galleryImageId); + if (galleryImage == null) + { + return; + } + + // Get all the comments related to the image + var queryable = await _commentRepository.GetQueryableAsync(); + var allCommentTexts = await queryable + .Where(c => c.EntityType == CmsKitDemoConsts.ImageGalleryEntityType && + c.EntityId == comment.EntityId) + .Select(c => c.Text) + .ToArrayAsync(); + + // Update the summary of comments related to the image + if (allCommentTexts.Length <= 0) + { + galleryImage.CommentsSummary = ""; + } + else + { + galleryImage.CommentsSummary = + await _aiCommentSummarizer.SummarizeAsync(allCommentTexts); + } + + // Update the image in database + await _galleryImageRepository.UpdateAsync(galleryImage); + } +} +```` + +Let's explain that class: + +* `GalleryImageCommentListener` implements the `ILocalEventHandler>` interface. In this way, it can handle an event whenever a `Comment` [entity](https://abp.io/docs/latest/framework/architecture/domain-driven-design/entities) is changed (created, updated or deleted). We are using ABP's [local event bus](https://abp.io/docs/latest/framework/infrastructure/event-bus/local) and its [pre-defined events](https://abp.io/docs/latest/framework/infrastructure/event-bus/local#pre-built-events). +* `HandleEventAsync` is called by the ABP Framework whenever a new `Comment` is created, or an existing `Comment` is deleted or updated. +* ABP's `Comment` entity is reusable and it can be associated with any kind of objects (blog posts, images, etc). So, first we are checking if this comment is related to an image gallery item. +* Then we are getting the related `GalleryImage` entity from the database. +* And getting all comments (including the new one) from the database for this image. +* Finally, using the `AiCommentSummarizer` class to generate the summary and set the `CommentsSummary` property. + +## Show the Summary Card on the UI + +Everything is ready on the backend. Now, we can show the summary text on the user interface. To do, that, I added `CommentsSummary` property also to the `GalleryImageDto` class and used it on the `/Pages/Gallery/Detail.cshtml` view: + +````csharp +@if (!Model.Image.CommentsSummary.IsNullOrEmpty()) +{ +
+
+
Summary of the User Comments
+

@Model.Image.CommentsSummary

+
+
+} +```` + +That section renders the following card on the user interface: + +![summary-card](summary-card.png) + +## Conclusion + +In this article, I demonstrated how to use [Microsoft AI Extensions Library](https://learn.microsoft.com/en-us/dotnet/ai/ai-extensions) to work with OpenAI for summarization of multiple user comments. I reused the [ABP's CMS Kit Demo application](https://github.com/abpframework/cms-kit-demo) to show it in a more real world example. + +## Source Code + +* [Source code of the CMS Kit Demo application](https://github.com/abpframework/cms-kit-demo) +* [All the changes made for this article (as a pull request)](https://github.com/abpframework/cms-kit-demo/pull/18) \ No newline at end of file diff --git a/docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/comment-example.png b/docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/comment-example.png new file mode 100644 index 0000000000..4ac33b60f9 Binary files /dev/null and b/docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/comment-example.png differ diff --git a/docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/summary-card.png b/docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/summary-card.png new file mode 100644 index 0000000000..35151efafb Binary files /dev/null and b/docs/en/Community-Articles/2025-04-25-AI-Comment-Summarization/summary-card.png differ diff --git a/docs/en/cli/index.md b/docs/en/cli/index.md index 38503b73be..02521e72cb 100644 --- a/docs/en/cli/index.md +++ b/docs/en/cli/index.md @@ -342,6 +342,7 @@ Note that this command can upgrade your solution from a previous version, and al * `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default. * `--check-all`: Check the new version of each package separately. Default is `false`. * `--version` or `-v`: Specifies the version to use for update. If not specified, latest version is used. +* * `--leptonx-version` or `-lv`: Specifies the LeptonX version to use for update. If not specified, latest version or the version that is compatible with `--version` argument is used. ### clean @@ -888,6 +889,7 @@ Some features of the CLI requires to be logged in to ABP Platform. The login com ```bash abp login # Opens a default browser to log in to ABP Platform via abp.io abp login --device # Use device login flow +abp login username -p ****** --password # Use user password login ``` A new login with an already active session overwrites the previous session. diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index d6d24fa271..fc3844317d 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -613,6 +613,10 @@ "text": "Google Cloud Storage Provider", "path": "framework/infrastructure/blob-storing/google.md" }, + { + "text": "Bunny.Net Provider", + "path": "framework/infrastructure/blob-storing/bunny.md" + }, { "text": "Create a Custom Provider", "path": "framework/infrastructure/blob-storing/custom-provider.md" diff --git a/docs/en/framework/api-development/static-csharp-clients.md b/docs/en/framework/api-development/static-csharp-clients.md index c8eaea4231..b3cf80fcfc 100644 --- a/docs/en/framework/api-development/static-csharp-clients.md +++ b/docs/en/framework/api-development/static-csharp-clients.md @@ -47,7 +47,7 @@ Implement this class in your service application. You can use [auto API controll First, add [Volo.Abp.Http.Client](https://www.nuget.org/packages/Volo.Abp.Http.Client) nuget package to your client project: ```` -Install-Package Volo.Abp.Http.Client +dotnet add package Volo.Abp.Http.Client ```` Then add `AbpHttpClientModule` dependency to your module: diff --git a/docs/en/framework/api-development/swagger.md b/docs/en/framework/api-development/swagger.md index 1699e4d0d2..fb27bcf27b 100644 --- a/docs/en/framework/api-development/swagger.md +++ b/docs/en/framework/api-development/swagger.md @@ -29,7 +29,7 @@ If you want to manually install; 1. Add the [Volo.Abp.Swashbuckle](https://www.nuget.org/packages/Volo.Abp.Swashbuckle) NuGet package to your `Web` or `HttpApi.Host` project: - `Install-Package Volo.Abp.Swashbuckle` + `dotnet add package Volo.Abp.Swashbuckle` 2. Add the `AbpSwashbuckleModule` to the dependency list of your module: diff --git a/docs/en/framework/architecture/microservices/index.md b/docs/en/framework/architecture/microservices/index.md index a6e9091f2d..eb3b2ccef6 100644 --- a/docs/en/framework/architecture/microservices/index.md +++ b/docs/en/framework/architecture/microservices/index.md @@ -25,6 +25,8 @@ However, developing such a well-modular application can be a problem since it is ABP can help you in that point by offering a **microservice-compatible, strict module architecture** where your module is split into multiple layers/projects and developed in its own VS solution completely isolated and independent from other modules. Such a developed module is a natural microservice yet it can be easily plugged-in a monolithic application. See the [module development best practice guide](../best-practices) that offers a **microservice-first module design**. All [standard ABP modules](https://github.com/abpframework/abp/tree/master/modules) are developed based on this guide. So, you can use these modules by embedding into your monolithic solution or deploy them separately and use via remote APIs. They can share a single database or can have their own database based on your simple configuration. -## Microservice Demo Solution: eShopOnAbp +## Microservice Solution Template -The [eShopOnAbp project](https://github.com/abpframework/eShopOnAbp) demonstrates a complete microservice solution based on the ABP. +ABP provides a pre-architected and production-ready microservice solution template that includes multiple services, API gateways and applications well integrated with each other. This template helps you quickly start building distributed systems with common microservice patterns. + +See the [Microservice Solution Template](../../../solution-templates/microservice/index.md) documentation for details. diff --git a/docs/en/framework/architecture/modularity/basics.md b/docs/en/framework/architecture/modularity/basics.md index f92e6edc3b..2b3aaad214 100644 --- a/docs/en/framework/architecture/modularity/basics.md +++ b/docs/en/framework/architecture/modularity/basics.md @@ -1,7 +1,5 @@ # Modularity -## Introduction - ABP was designed to support to build fully modular applications and systems where every module may have entities, services, database integration, APIs, UI components and so on; * This document introduces the basics of the module system. diff --git a/docs/en/framework/architecture/modularity/extending/module-entity-extensions.md b/docs/en/framework/architecture/modularity/extending/module-entity-extensions.md index 8a5b50652e..ac041895d7 100644 --- a/docs/en/framework/architecture/modularity/extending/module-entity-extensions.md +++ b/docs/en/framework/architecture/modularity/extending/module-entity-extensions.md @@ -151,6 +151,17 @@ property => > Tip: Use `DefaultValueFactory` option only if the default value may change over the time (like `DateTime.Now` in this example). If it is a constant value, then use the `DefaultValue` option. +### DataTypeAttribute + +`DataTypeAttribute` is used to specify the type of the property. It is used to determine how to render the property on the user interface: + +```csharp +property => +{ + property.Attributes.Add(new DataTypeAttribute(DataType.Date)); +} +``` + ### Validation Entity extension system allows you to define validation for extension properties in a few ways. diff --git a/docs/en/framework/data/entity-framework-core/index.md b/docs/en/framework/data/entity-framework-core/index.md index 293e58daad..a50edb2d4a 100644 --- a/docs/en/framework/data/entity-framework-core/index.md +++ b/docs/en/framework/data/entity-framework-core/index.md @@ -755,7 +755,17 @@ public static class QADbContextModelCreatingExtensions > The `Object Extension` feature need the `Change Tracking`, which means you can't use the read-only repositories for the entities that have `extension properties(MapEfCoreProperty)`, Please see the [Repositories documentation](../../architecture/domain-driven-design/repositories.md) to learn the change tracking behavior. -See the "*ConfigureByConvention Method*" section above for more information. +See the **ConfigureByConvention Method** section above for more information. + +### Accessing Extra Properties(Shadow Properties) + +Extra properties stored in separate fields in the database are known as **Shadow Properties**. These properties are not defined in the entity class, but are part of the EF Core model and can be referenced in LINQ queries using the EF.Property static method + +```csharp +var query = (await GetQueryableAsync()).Where(x => EF.Property(x, "Title") == "MyTitle"); +``` + +See the [EF Core Shadow and Indexer Properties document](https://learn.microsoft.com/en-us/ef/core/modeling/shadow-properties) for more information. ## Advanced Topics diff --git a/docs/en/framework/data/entity-framework-core/postgresql.md b/docs/en/framework/data/entity-framework-core/postgresql.md index 8561e8bada..e25cc2c5fb 100644 --- a/docs/en/framework/data/entity-framework-core/postgresql.md +++ b/docs/en/framework/data/entity-framework-core/postgresql.md @@ -21,6 +21,15 @@ Find `UseSqlServer()` call in *YourProjectName*DbContextFactory.cs inside the `. > Depending on your solution structure, you may find more `UseSqlServer()` calls that needs to be changed. + +## EnableLegacyTimestampBehavior + +Please enable the `Npgsql.EnableLegacyTimestampBehavior` in `PreConfigureServices` method of your EF Core module and `DbContextFactory(IDesignTimeDbContextFactory)` class. + +`AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);` + +See https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic + ## Change the Connection Strings PostgreSql connection strings are different than SQL Server connection strings. So, check all `appsettings.json` files in your solution and replace the connection strings inside them. See the [connectionstrings.com](https://www.connectionstrings.com/postgresql/) for details of PostgreSql connection string options. diff --git a/docs/en/framework/fundamentals/caching.md b/docs/en/framework/fundamentals/caching.md index bdef675f64..c1a720b394 100644 --- a/docs/en/framework/fundamentals/caching.md +++ b/docs/en/framework/fundamentals/caching.md @@ -270,7 +270,7 @@ Distributed cache service provides an interesting feature. Assume that you've up ### IDistributedCacheSerializer -`IDistributedCacheSerializer` service is used to serialize and deserialize the cache items. The default implementation is the `Utf8JsonDistributedCacheSerializer` class that uses `IJsonSerializer` service to convert objects to [JSON](../../json-serialization.md) and vice verse. Then it uses UTC8 encoding to convert the JSON string to a byte array which is accepted by the distributed cache. +`IDistributedCacheSerializer` service is used to serialize and deserialize the cache items. The default implementation is the `Utf8JsonDistributedCacheSerializer` class that uses `IJsonSerializer` service to convert objects to [JSON](../../json-serialization.md) and vice verse. Then it uses UTF8 encoding to convert the JSON string to a byte array which is accepted by the distributed cache. You can [replace](./dependency-injection.md) this service with your own implementation if you want to implement your own serialization logic. diff --git a/docs/en/framework/fundamentals/configuration.md b/docs/en/framework/fundamentals/configuration.md index 7611017b7e..997d347d2a 100644 --- a/docs/en/framework/fundamentals/configuration.md +++ b/docs/en/framework/fundamentals/configuration.md @@ -1,3 +1,52 @@ # Configuration -ASP.NET Core has an flexible and extensible key-value based configuration system. In fact, the configuration system is a part of Microsoft.Extensions libraries and it is independent from ASP.NET Core. That means it can be used in any type of application. See [Microsoft's documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/) to learn the configuration infrastructure. ABP is 100% compatible with the configuration system. \ No newline at end of file +ASP.NET Core has an flexible and extensible key-value based configuration system. The configuration system is a part of Microsoft.Extensions libraries and it is independent from ASP.NET Core. That means it can be used in any type of application. + +See [Microsoft's documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/) to learn the configuration infrastructure. ABP is 100% compatible with the configuration system. + +## Getting the Configuration + +You may need to get the `IConfiguration` service in various places in your codebase. The following section shows two common ways. + +### In Module Classes + +You typically need to get configuration while initializing your application. You can get the `IConfiguration` service using the `ServiceConfigurationContext.Configuration` property inside your [module class](../architecture/modularity/basics.md) as the following example: + +````csharp +public class MyAppModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var connectionString = context.Configuration["ConnectionStrings:Default"]; + } +} +```` + +`context.Configuration` is a shortcut property for the `context.Services.GetConfiguration()` method. In general, prefer using `context.Configuration` for simplicity and readability when working within module classes. Use `context.Services.GetConfiguration()` in other contexts where you have an `IServiceCollection` object but do not have access to the `context.Configuration` property. (`IServiceCollection.GetConfiguration` is an extension method that can be used whenever you have an `IServiceCollection` object). + +### In Your Services + +You can directly [inject](dependency-injection.md) the `IConfiguration` service into your services: + +````csharp +public class MyService : ITransientDependency +{ + private readonly IConfiguration _configuration; + + public MyService(IConfiguration configuration) + { + _configuration = configuration; + } + + public string? GetConnectionString() + { + return _configuration["ConnectionStrings:Default"]; + } +} +```` + +## See Also + +* [Microsoft's Configuration Documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/) +* [The Options Pattern](options.md) + diff --git a/docs/en/framework/fundamentals/fluent-validation.md b/docs/en/framework/fundamentals/fluent-validation.md index d3af69bae9..207cddcbd3 100644 --- a/docs/en/framework/fundamentals/fluent-validation.md +++ b/docs/en/framework/fundamentals/fluent-validation.md @@ -21,7 +21,7 @@ If you want to manually install; 1. Add the [Volo.Abp.FluentValidation](https://www.nuget.org/packages/Volo.Abp.FluentValidation) NuGet package to your project: ```` - Install-Package Volo.Abp.FluentValidation + dotnet add package Volo.Abp.FluentValidation ```` 2. Add the `AbpFluentValidationModule` to the dependency list of your module: diff --git a/docs/en/framework/fundamentals/localization.md b/docs/en/framework/fundamentals/localization.md index 61118fd742..6e8a3f678a 100644 --- a/docs/en/framework/fundamentals/localization.md +++ b/docs/en/framework/fundamentals/localization.md @@ -248,6 +248,17 @@ namespace MyProject The `L` property is also available for some other base classes like `AbpController` and `AbpPageModel`. +## Supported Languages + +You can configure the `AbpLocalizationOptions`'s `Languages` property to add the languages supported by the application. The template already sets common languages, but you can add new languages as shown below: + +```csharp +Configure(options => +{ + options.Languages.Add(new LanguageInfo("uz", "uz", "Uzbek")); +}); +``` + ## The Client Side See the following documents to learn how to reuse the same localization texts in the JavaScript side; diff --git a/docs/en/framework/infrastructure/background-jobs/hangfire.md b/docs/en/framework/infrastructure/background-jobs/hangfire.md index 396c7498b5..f5991ee19b 100644 --- a/docs/en/framework/infrastructure/background-jobs/hangfire.md +++ b/docs/en/framework/infrastructure/background-jobs/hangfire.md @@ -25,7 +25,7 @@ If you want to manually install; 1. Add the [Volo.Abp.BackgroundJobs.HangFire](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.HangFire) NuGet package to your project: ```` - Install-Package Volo.Abp.BackgroundJobs.HangFire + dotnet add package Volo.Abp.BackgroundJobs.HangFire ```` 2. Add the `AbpBackgroundJobsHangfireModule` to the dependency list of your module: @@ -84,6 +84,24 @@ After you have installed these NuGet packages, you need to configure your projec } ```` +### AbpHangfireOptions + +You can configure the [BackgroundJobServerOptions](https://api.hangfire.io/html/T_Hangfire_BackgroundJobServerOptions.htm) of `AbpHangfireOptions` to customize the server. + +````csharp +Configure(options => +{ + // If no ServerOptions is set, ABP will use the default BackgroundJobServerOptions instance. + options.ServerOptions = new BackgroundJobServerOptions + { + Queues = ["default", "alpha"], + //... other properties + }; +}); +```` + +> You don't need to call `AddHangfireServer` method, ABP will use AbpHangfireOptions's `ServerOptions` to create a server. + ### Specifying Queue You can use the [`QueueAttribute`](https://docs.hangfire.io/en/latest/background-processing/configuring-queues.html) to specify the queue: diff --git a/docs/en/framework/infrastructure/background-jobs/quartz.md b/docs/en/framework/infrastructure/background-jobs/quartz.md index 93cf5d53bf..1b18fd36ae 100644 --- a/docs/en/framework/infrastructure/background-jobs/quartz.md +++ b/docs/en/framework/infrastructure/background-jobs/quartz.md @@ -25,7 +25,7 @@ If you want to manually install; 1. Add the [Volo.Abp.BackgroundJobs.Quartz](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.Quartz) NuGet package to your project: ```` - Install-Package Volo.Abp.BackgroundJobs.Quartz + dotnet add package Volo.Abp.BackgroundJobs.Quartz ```` 2. Add the `AbpBackgroundJobsQuartzModule` to the dependency list of your module: diff --git a/docs/en/framework/infrastructure/background-workers/hangfire.md b/docs/en/framework/infrastructure/background-workers/hangfire.md index 3ccbaf35bd..7fec8be273 100644 --- a/docs/en/framework/infrastructure/background-workers/hangfire.md +++ b/docs/en/framework/infrastructure/background-workers/hangfire.md @@ -23,7 +23,7 @@ If you want to manually install; 1. Add the [Volo.Abp.BackgroundWorkers.Hangfire](https://www.nuget.org/packages/Volo.Abp.BackgroundWorkers.Hangfire) NuGet package to your project: ```` - Install-Package Volo.Abp.BackgroundWorkers.Hangfire + dotnet add package Volo.Abp.BackgroundWorkers.Hangfire ```` 2. Add the `AbpBackgroundWorkersHangfireModule` to the dependency list of your module: @@ -84,6 +84,24 @@ After you have installed these NuGet packages, you need to configure your projec } ```` +### AbpHangfireOptions + +You can configure the [BackgroundJobServerOptions](https://api.hangfire.io/html/T_Hangfire_BackgroundJobServerOptions.htm) of `AbpHangfireOptions` to customize the server. + +````csharp +Configure(options => +{ + // If no ServerOptions is set, ABP will use the default BackgroundJobServerOptions instance. + options.ServerOptions = new BackgroundJobServerOptions + { + Queues = ["default", "alpha"], + //... other properties + }; +}); +```` + +> You don't need to call `AddHangfireServer` method, ABP will use AbpHangfireOptions's `ServerOptions` to create a server. + ## Create a Background Worker `HangfireBackgroundWorkerBase` is an easy way to create a background worker. diff --git a/docs/en/framework/infrastructure/background-workers/quartz.md b/docs/en/framework/infrastructure/background-workers/quartz.md index 663fe22a7f..909371adb0 100644 --- a/docs/en/framework/infrastructure/background-workers/quartz.md +++ b/docs/en/framework/infrastructure/background-workers/quartz.md @@ -21,7 +21,7 @@ If you want to manually install; 1. Add the [Volo.Abp.BackgroundWorkers.Quartz](https://www.nuget.org/packages/Volo.Abp.BackgroundWorkers.Quartz) NuGet package to your project: ```` - Install-Package Volo.Abp.BackgroundWorkers.Quartz + dotnet add package Volo.Abp.BackgroundWorkers.Quartz ```` 2. Add the `AbpBackgroundWorkersQuartzModule` to the dependency list of your module: diff --git a/docs/en/framework/infrastructure/current-user.md b/docs/en/framework/infrastructure/current-user.md index d9082633f5..aa1974ab92 100644 --- a/docs/en/framework/infrastructure/current-user.md +++ b/docs/en/framework/infrastructure/current-user.md @@ -138,7 +138,7 @@ public class MyAppService : ApplicationService { new Claim(AbpClaimTypes.UserId, Guid.NewGuid().ToString()), new Claim(AbpClaimTypes.UserName, "john"), - new Claim("MyCustomCliam", "42") + new Claim("Claim", "42") } ) ); diff --git a/docs/en/framework/infrastructure/event-bus/distributed/index.md b/docs/en/framework/infrastructure/event-bus/distributed/index.md index 9ed2121057..d56215fa12 100644 --- a/docs/en/framework/infrastructure/event-bus/distributed/index.md +++ b/docs/en/framework/infrastructure/event-bus/distributed/index.md @@ -297,10 +297,14 @@ Configure(options => options.AutoEventSelectors.Add( type => type.Namespace.StartsWith("MyProject.") ); + + //Ignore for a single entity + options.IgnoredEventSelectors.Add(); }); ```` -* The last one provides flexibility to decide if the events should be published for the given entity type. Returns `true` to accept a `Type`. +* The `type.Namespace.StartsWith("MyProject.")` provides flexibility to decide if the events should be published for the given entity type. Returns `true` to accept a `Type`. +* The `IgnoredEventSelectors` is used to ignore the events for the specified entity types. It is useful if you enabled for all entities and want to ignore for some entities. You can add more than one selector. If one of the selectors match for an entity type, then it is selected. diff --git a/docs/en/framework/infrastructure/image-manipulation.md b/docs/en/framework/infrastructure/image-manipulation.md index 7ab8dc562e..a1ee6e1584 100644 --- a/docs/en/framework/infrastructure/image-manipulation.md +++ b/docs/en/framework/infrastructure/image-manipulation.md @@ -22,7 +22,7 @@ If you want to manually install; 1. Add the [Volo.Abp.Imaging.Abstractions](https://www.nuget.org/packages/Volo.Abp.Imaging.Abstractions) NuGet package to your project: ``` -Install-Package Volo.Abp.Imaging.Abstractions +dotnet add package Volo.Abp.Imaging.Abstractions ``` 2. Add the `AbpImagingAbstractionsModule` to the dependency list of your module: @@ -240,7 +240,7 @@ If you want to manually install; 1. Add the [Volo.Abp.Imaging.MagickNet](https://www.nuget.org/packages/Volo.Abp.Imaging.MagickNet) NuGet package to your project: ``` -Install-Package Volo.Abp.Imaging.MagickNet +dotnet add package Volo.Abp.Imaging.MagickNet ``` 2. Add `AbpImagingMagickNetModule` to your [module](../architecture/modularity/basics.md)'s dependency list: @@ -284,7 +284,7 @@ If you want to manually install; 1. Add the [Volo.Abp.Imaging.ImageSharp](https://www.nuget.org/packages/Volo.Abp.Imaging.ImageSharp) NuGet package to your project: ``` -Install-Package Volo.Abp.Imaging.ImageSharp +dotnet add package Volo.Abp.Imaging.ImageSharp ``` 2. Add `AbpImagingImageSharpModule` to your [module](../architecture/modularity/basics.md)'s dependency list: @@ -350,7 +350,7 @@ If you want to manually install; 1. Add the [Volo.Abp.Imaging.AspNetCore](https://www.nuget.org/packages/Volo.Abp.Imaging.AspNetCore) NuGet package to your project: ``` -Install-Package Volo.Abp.Imaging.AspNetCore +dotnet add package Volo.Abp.Imaging.AspNetCore ``` 2. Add `AbpImagingAspNetCoreModule` to your [module](../architecture/modularity/basics.md)'s dependency list: diff --git a/docs/en/framework/infrastructure/settings.md b/docs/en/framework/infrastructure/settings.md index ef164e939b..f3784b1616 100644 --- a/docs/en/framework/infrastructure/settings.md +++ b/docs/en/framework/infrastructure/settings.md @@ -24,6 +24,8 @@ public class EmailSettingProvider : SettingDefinitionProvider } ```` +> If you're developing a [DDD module](../architecture/domain-driven-design), you usually create this class in the `Domain` layer, of course, that's not mandatory. + ABP automatically discovers this class and registers the setting definitions. ### SettingDefinition diff --git a/docs/en/framework/infrastructure/sms-sending.md b/docs/en/framework/infrastructure/sms-sending.md index e6bc9135e5..ef2a86445f 100644 --- a/docs/en/framework/infrastructure/sms-sending.md +++ b/docs/en/framework/infrastructure/sms-sending.md @@ -26,7 +26,7 @@ If you want to manually install; 1. Add the [Volo.Abp.Sms](https://www.nuget.org/packages/Volo.Abp.Sms) NuGet package to your project: ``` -Install-Package Volo.Abp.Sms +dotnet add package Volo.Abp.Sms ``` 2. Add the `AbpSmsModule` to the dependency list of your module: diff --git a/docs/en/framework/infrastructure/string-encryption.md b/docs/en/framework/infrastructure/string-encryption.md index 5f37f1fa99..bb398425f6 100644 --- a/docs/en/framework/infrastructure/string-encryption.md +++ b/docs/en/framework/infrastructure/string-encryption.md @@ -22,7 +22,7 @@ If you want to manually install; 1. Add the [Volo.Abp.Security](https://www.nuget.org/packages/Volo.Abp.Security) NuGet package to your project: - `Install-Package Volo.Abp.Security` + `dotnet add package Volo.Abp.Security` 2. Add the `AbpSecurityModule` to the dependency list of your module: @@ -52,11 +52,11 @@ All encryption operations are included in `IStringEncryptionService`. You can in public string Encrypt(string value) { - // To enrcypt a value + // To encrypt a value return StringEncryptionService.Encrypt(value); } - public string Decrpyt(string value) + public string Decrypt(string value) { // To decrypt a value return StringEncryptionService.Decrypt(value); @@ -122,4 +122,4 @@ Configure(opts => - **Keysize:** This constant is used to determine the keysize of the encryption algorithm. - Default value: `256` \ No newline at end of file + Default value: `256` diff --git a/docs/en/framework/infrastructure/text-templating/razor.md b/docs/en/framework/infrastructure/text-templating/razor.md index 1d17d4337e..77db5467aa 100644 --- a/docs/en/framework/infrastructure/text-templating/razor.md +++ b/docs/en/framework/infrastructure/text-templating/razor.md @@ -25,7 +25,7 @@ If you want to manually install; 1. Add the [Volo.Abp.TextTemplating.Razor](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Razor) NuGet package to your project: ```` -Install-Package Volo.Abp.TextTemplating.Razor +dotnet add package Volo.Abp.TextTemplating.Razor ```` 2. Add the `AbpTextTemplatingRazorModule` to the dependency list of your module: diff --git a/docs/en/framework/infrastructure/text-templating/scriban.md b/docs/en/framework/infrastructure/text-templating/scriban.md index bd54914411..99d160ce85 100644 --- a/docs/en/framework/infrastructure/text-templating/scriban.md +++ b/docs/en/framework/infrastructure/text-templating/scriban.md @@ -19,7 +19,7 @@ If you want to manually install; 1. Add the [Volo.Abp.TextTemplating.Scriban](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Scriban) NuGet package to your project: ```` -Install-Package Volo.Abp.TextTemplating.Scriban +dotnet add package Volo.Abp.TextTemplating.Scriban ```` 2. Add the `AbpTextTemplatingScribanModule` to the dependency list of your module: diff --git a/docs/en/framework/infrastructure/timing.md b/docs/en/framework/infrastructure/timing.md index 7ddf1b730a..9f104b6336 100644 --- a/docs/en/framework/infrastructure/timing.md +++ b/docs/en/framework/infrastructure/timing.md @@ -8,11 +8,11 @@ ABP provides a basic infrastructure to make it easy and handle automatically whe ## IClock -`DateTime.Now` returns a `DateTime` object with the **local date & time of the server**. A `DateTime` object **doesn't store the time zone information**. So, you can not know the **absolute date & time** stored in this object. You can only make **assumptions**, like assuming that it was created in UTC+05 time zone. The things especially gets complicated when you save this value to a database and read later, or send it to a client in a **different time zone**. +`DateTime.Now` returns a `DateTime` object with the **local date & time of the server**. A `DateTime` object **doesn't store the time zone information**. So, you can not know the **absolute date & time** stored in this object. You can only make **assumptions**, like assuming that it was created in UTC+05 time zone. Things especially get complicated when you save this value to a database and read it later, or send it to a client in a **different time zone**. -One solution to this problem is always use `DateTime.UtcNow` and assume all `DateTime` objects as UTC time. In this way, you can convert it to the time zone of the target client when needed. +One solution to this problem is always using `DateTime.UtcNow` and assuming all `DateTime` objects as UTC time. In this way, you can convert it to the time zone of the target client when needed. -`IClock` provides an abstraction while getting the current time, so you can control the kind of the date time (UTC or local) in a single point in your application. +`IClock` provides an abstraction while getting the current time, so you can control the kind of the datetime (UTC or local) in a single point in your application. **Example: Getting the current time** @@ -40,14 +40,14 @@ namespace AbpDemo } ```` -* Inject the `IClock` service when you need to get the current time. Common base classes (like ApplicationService) already injects it and provides as a base property - so, you can directly use as `Clock`. +* Inject the `IClock` service when you need to get the current time. Common base classes (like ApplicationService) already inject it and provide it as a base property - so, you can directly use it as `Clock`. * Use the `Now` property to get the current time. -> Most of the times, `IClock` is the only service you need to know and use in your application. +> Most of the time, `IClock` is the only service you need to know and use in your application. ### Clock Options -`AbpClockOptions` is the [options](../fundamentals/options.md) class that used to set the clock kind. +`AbpClockOptions` is the [options](../fundamentals/options.md) class that is used to set the clock kind. **Example: Use UTC Clock** @@ -58,13 +58,12 @@ Configure(options => }); ```` -Write this inside the `ConfigureServices` method of your [module](../architecture/modularity/basics.md). - -> Default `Kind` is `Unspecified`, that actually make the Clock as it doesn't exists at all. Either make it `Utc` or `Local` if you want to get benefit of the Clock system. +Write this inside of the `ConfigureServices` method of your [module](../architecture/modularity/basics.md). +> The default `Kind` is `Unspecified`, which effectively disables the **Clock** functionality. Either make it `Utc` or `Local` if you want to get the benefit of the Clock system. ### DateTime Normalization -Other important function of the `IClock` is to normalize `DateTime` objects. +Another important function of the `IClock` is to normalize `DateTime` objects. **Example usage:** @@ -75,11 +74,11 @@ var normalizedDateTime = Clock.Normalize(dateTime) `Normalize` method works as described below: -* Converts the given `DateTime` to the UTC (by using the `DateTime.ToUniversalTime()` method) if current Clock is UTC and given `DateTime` is local. -* Converts the given `DateTime` to the local (by using the `DateTime.ToLocalTime()` method) if current Clock is local and given `DateTime` is UTC. +* Converts the given `DateTime` to the UTC (by using the `DateTime.ToUniversalTime()` method) if the current Clock is UTC and the given `DateTime` is local. +* Converts the given `DateTime` to the local (by using the `DateTime.ToLocalTime()` method) if the current Clock is local and the given `DateTime` is UTC. * Sets `Kind` of the given `DateTime` (using the `DateTime.SpecifyKind(...)` method) to the `Kind` of the current Clock if given `DateTime`'s `Kind` is `Unspecified`. -`Normalize` method is used by the ABP when the it gets a `DateTime` that is not created by `IClock.Now` and may not be compatible with the current Clock type. Examples; +`Normalize` method is used by the ABP when it gets a `DateTime` that is not created by `IClock.Now` and may not be compatible with the current Clock type. Examples; * `DateTime` type binding in the ASP.NET Core MVC model binding. * Saving data to and reading data from database via [Entity Framework Core](../data/entity-framework-core). @@ -89,12 +88,67 @@ var normalizedDateTime = Clock.Normalize(dateTime) `DisableDateTimeNormalization` attribute can be used to disable the normalization operation for desired classes or properties. +### DateTime Converter Between UTC and User's Time Zone + +#### Convert given UTC to user's time zone. + +`DateTime ConvertToUserTime(DateTime utcDateTime)` and `DateTimeOffset ConvertToUserTime(DateTimeOffset dateTimeOffset)` methods convert given UTC `DateTime` or `DateTimeOffset` to the user's time zone. + +> If `SupportsMultipleTimezone` is `false` or `dateTime.Kind` is not `Utc` or these is no timezone setting, it returns the given `DateTime` or `DateTimeOffset` without any changes. + +**Example:** + +If user's `TimeZone Setting` is `Europe/Istanbul` + +````csharp +// 2025-03-01T05:30:00Z +var utcTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc); + +var userTime = Clock.ConvertToUserTime(utcTime); + +// Europe/Istanbul has 3 hours difference with UTC. So, the result will be 3 hours later. +userTime.Kind.ShouldBe(DateTimeKind.Unspecified); +userTime.ToString("O").ShouldBe("2025-03-01T08:30:00"); +```` + +````csharp +// 2025-03-01T05:30:00Z +var utcTime = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc), TimeSpan.Zero); + +var userTime = Clock.ConvertToUserTime(utcTime); + +// Europe/Istanbul has 3 hours difference with UTC. So, the result will be 3 hours later. +userTime.Offset.ShouldBe(TimeSpan.FromHours(3)); +userTime.ToString("O").ShouldBe("2025-03-01T08:30:00.0000000+03:00"); +```` + +#### Converts given user's DateTime to UTC + +`DateTime ConvertToUtc(DateTime dateTime)` method convert given user's `DateTime` to UTC. + +> If `SupportsMultipleTimezone` is `false` or `dateTime.Kind` is `Utc` or these is no timezone setting, it returns the given `DateTime` without any changes. + +**Example:** + +If user's `TimeZone Setting` is `Europe/Istanbul` + +````csharp +// 2025-03-01T05:30:00 +var userTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified); //Same as Local + +var utcTime = Clock.ConvertToUtc(userTime); + +// Europe/Istanbul has 3 hours difference with UTC. So, the result will be 3 hours earlier. +utcTime.Kind.ShouldBe(DateTimeKind.Utc); +utcTime.ToString("O").ShouldBe("2025-03-01T02:30:00.0000000Z"); +```` + ### Other IClock Properties In addition to the `Now`, `IClock` service has the following properties: * `Kind`: Returns a `DateTimeKind` for the currently used clock type (`DateTimeKind.Utc`, `DateTimeKind.Local` or `DateTimeKind.Unspecified`). -* `SupportsMultipleTimezone`: Returns `true` if currently used clock is UTC. +* `SupportsMultipleTimezone`: Returns `true` if the currently used clock is UTC. ## Time Zones @@ -102,12 +156,26 @@ This section covers the ABP infrastructure related to managing time zones. ### TimeZone Setting -ABP defines **a setting**, named `Abp.Timing.TimeZone`, that can be used to set and get the time zone for a user, [tenant](../architecture/multi-tenancy) or globally for the application. The default value is `UTC`. +ABP defines **a setting**, named `Abp.Timing.TimeZone`, that can be used to set and get the time zone for a user, [tenant](../architecture/multi-tenancy) or globally for the application. The default value is empty, which means the application will use the server's time zone. + +You can change your host/tenant global time zone in the [Settings Management UI](../../modules/setting-management#setting-management-ui) + +The [Account Pro Module](../../modules/account-pro#Time-Zone-Setting) supports user to set their own time zone in the account settings page. See the [setting documentation](../infrastructure/settings.md) to learn more about the setting system. +### UseAbpTimeZone Middleware + +The `app.UseAbpTimeZone()` middleware is used to set the time zone for the current request. + + * It will get timezone from settings, the order is `User` -> `Tenant` -> `Application/Global`. + * If current request is anonymous, it will get timezone from the request header/cookie/form/query string. the key is `__timezone`. + +> If you want to get current timezone, you can inject `ICurrentTimezoneProvider` service. +> Please add this middleware after authentication. + ### ITimezoneProvider -`ITimezoneProvider` is a service to simple convert [Windows Time Zone Id](https://support.microsoft.com/en-us/help/973627/microsoft-time-zone-index-values) values to [Iana Time Zone Name](https://www.iana.org/time-zones) values and vice verse. It also provides methods to get list of these time zones and get a `TimeZoneInfo` with a given name. +`ITimezoneProvider` is a service to simply convert [Windows Time Zone Id](https://support.microsoft.com/en-us/help/973627/microsoft-time-zone-index-values) values to [Iana Time Zone Name](https://www.iana.org/time-zones) values and vice verse. It also provides methods to get the list of these time zones and get a `TimeZoneInfo` with a given name. It has been implemented using the [TimeZoneConverter](https://github.com/mj1856/TimeZoneConverter) library. diff --git a/docs/en/framework/real-time/signalr.md b/docs/en/framework/real-time/signalr.md index d6cbbfbada..2d2710c77d 100644 --- a/docs/en/framework/real-time/signalr.md +++ b/docs/en/framework/real-time/signalr.md @@ -27,7 +27,7 @@ If you want to manually install; 1. Add the [Volo.Abp.AspNetCore.SignalR](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR) NuGet package to your project: ``` - Install-Package Volo.Abp.AspNetCore.SignalR + dotnet add package Volo.Abp.AspNetCore.SignalR ``` Or use the Visual Studio NuGet package management UI to install it. diff --git a/docs/en/framework/ui/maui/index.md b/docs/en/framework/ui/maui/index.md index 573278a060..28f74aa6a3 100644 --- a/docs/en/framework/ui/maui/index.md +++ b/docs/en/framework/ui/maui/index.md @@ -37,10 +37,15 @@ Open a command line terminal and run the `adb reverse` command to expose a port `adb reverse tcp:44305 tcp:44305` -> You should replace "44305" with the real port. -> You should run the command after starting the emulator. +> Replace `44305` with the port number your backend application is running on. +> +> Run this command **after** the Android emulator has started. -> If you don't have a separate installation of Android Debug Bridge, you can open it from **Visual Studio** by following toolbar menu `Tools` > `Android` > `Android Adb Command Prompt`. Android emulator has to be running for this operation. +> [!IMPORTANT] +> If your project uses a **tiered** or **microservice** architecture, ensure that both the **auth server** and all **remote service ports** are properly proxied using the `adb reverse` command. You can find all the required remote service ports and AuthServer configurations in your `YourProjectName.Maui/appsettings.json` file. + +> [!NOTE] +> If you don't have a separate installation of **Android Debug Bridge** _(adb)_, you can open it from **Visual Studio** by following toolbar menu `Tools` > `Android` > `Android Adb Command Prompt`. Android emulator has to be running for this operation. ### iOS diff --git a/docs/en/framework/ui/mvc-razor-pages/customization-user-interface.md b/docs/en/framework/ui/mvc-razor-pages/customization-user-interface.md index bc3b7d8fc2..c279f685c0 100644 --- a/docs/en/framework/ui/mvc-razor-pages/customization-user-interface.md +++ b/docs/en/framework/ui/mvc-razor-pages/customization-user-interface.md @@ -89,7 +89,10 @@ This example overrides the **login page** defined by the [Account Module](../../ Create a page model class deriving from the ` LoginModel ` (defined in the ` Volo.Abp.Account.Web.Pages.Account ` namespace): +> If you are using the `AbpAccountWebOpenIddictModule` or `AbpAccountPublicWebOpenIddictModule`, the base class is `OpenIddictSupportedLoginModel` instead of `LoginModel`. And you should change the `ExposeServices` attribute to `[ExposeServices(typeof (MyLoginModel), typeof(OpenIddictSupportedLoginModel), typeof(LoginModel))]` + ````csharp +[ExposeServices(typeof (MyLoginModel), typeof(LoginModel))] public class MyLoginModel : LoginModel { public MyLoginModel( @@ -114,8 +117,6 @@ public class MyLoginModel : LoginModel You can override any method or add new properties/methods if needed. -> Notice that we didn't use `[Dependency(ReplaceServices = true)]` or `[ExposeServices(typeof(LoginModel))]` since we don't want to replace the existing class in the dependency injection, we define a new one. - Copy `Login.cshtml` file into your solution as just described above. Change the **@model** directive to point to the `MyLoginModel`: ````xml diff --git a/docs/en/get-started/empty-aspnet-core-application.md b/docs/en/get-started/empty-aspnet-core-application.md index b0457db71b..95861719ce 100644 --- a/docs/en/get-started/empty-aspnet-core-application.md +++ b/docs/en/get-started/empty-aspnet-core-application.md @@ -104,7 +104,7 @@ Replacing ASP.NET Core's DI system by Autofac and integrating to ABP is pretty e 1. Install [Volo.Abp.Autofac](https://www.nuget.org/packages/Volo.Abp.Autofac) package ```` -Install-Package Volo.Abp.Autofac +dotnet add package Volo.Abp.Autofac ```` 2. Add the ``AbpAutofacModule`` Dependency diff --git a/docs/en/images/account-pro-external-login-settings.png b/docs/en/images/account-pro-external-login-settings.png index da10489599..3dca9cc644 100644 Binary files a/docs/en/images/account-pro-external-login-settings.png and b/docs/en/images/account-pro-external-login-settings.png differ diff --git a/docs/en/images/account-pro-select-account-parameter.png b/docs/en/images/account-pro-select-account-parameter.png new file mode 100644 index 0000000000..0037fe50e8 Binary files /dev/null and b/docs/en/images/account-pro-select-account-parameter.png differ diff --git a/docs/en/images/account-pro-select-account.png b/docs/en/images/account-pro-select-account.png new file mode 100644 index 0000000000..559b33f9c8 Binary files /dev/null and b/docs/en/images/account-pro-select-account.png differ diff --git a/docs/en/images/account-pro-time-zone-setting.png b/docs/en/images/account-pro-time-zone-setting.png new file mode 100644 index 0000000000..687ad7db70 Binary files /dev/null and b/docs/en/images/account-pro-time-zone-setting.png differ diff --git a/docs/en/images/create-aspnet-core-application.png b/docs/en/images/create-aspnet-core-application.png index afd1447ba8..372ba114bb 100644 Binary files a/docs/en/images/create-aspnet-core-application.png and b/docs/en/images/create-aspnet-core-application.png differ diff --git a/docs/en/images/create-new-aspnet-core-application-v2.png b/docs/en/images/create-new-aspnet-core-application-v2.png index d2bce84775..4dccf0c139 100644 Binary files a/docs/en/images/create-new-aspnet-core-application-v2.png and b/docs/en/images/create-new-aspnet-core-application-v2.png differ diff --git a/docs/en/images/pen-test-alert-list-9.1.png b/docs/en/images/pen-test-alert-list-9.1.png new file mode 100644 index 0000000000..caad96f862 Binary files /dev/null and b/docs/en/images/pen-test-alert-list-9.1.png differ diff --git a/docs/en/images/samples-cms-kit.png b/docs/en/images/samples-cms-kit.png new file mode 100644 index 0000000000..0c4e3e5877 Binary files /dev/null and b/docs/en/images/samples-cms-kit.png differ diff --git a/docs/en/images/samples-easycrm.png b/docs/en/images/samples-easycrm.png new file mode 100644 index 0000000000..72924ae3e7 Binary files /dev/null and b/docs/en/images/samples-easycrm.png differ diff --git a/docs/en/images/samples-eshoponabp.png b/docs/en/images/samples-eshoponabp.png new file mode 100644 index 0000000000..f8e22fff7d Binary files /dev/null and b/docs/en/images/samples-eshoponabp.png differ diff --git a/docs/en/images/samples-eventhub.png b/docs/en/images/samples-eventhub.png new file mode 100644 index 0000000000..548412d62c Binary files /dev/null and b/docs/en/images/samples-eventhub.png differ diff --git a/docs/en/images/select-empty-web-application-v2.png b/docs/en/images/select-empty-web-application-v2.png index 9bfd2ec6a8..83724fd4f4 100644 Binary files a/docs/en/images/select-empty-web-application-v2.png and b/docs/en/images/select-empty-web-application-v2.png differ diff --git a/docs/en/images/setting-management-email-ui.png b/docs/en/images/setting-management-email-ui.png index ab26a1d180..f02e8cd1c4 100644 Binary files a/docs/en/images/setting-management-email-ui.png and b/docs/en/images/setting-management-email-ui.png differ diff --git a/docs/en/images/setting-management-feature-management-ui.png b/docs/en/images/setting-management-feature-management-ui.png new file mode 100644 index 0000000000..b534fe830b Binary files /dev/null and b/docs/en/images/setting-management-feature-management-ui.png differ diff --git a/docs/en/images/setting-management-time-zone-ui.png b/docs/en/images/setting-management-time-zone-ui.png new file mode 100644 index 0000000000..fe65256ab5 Binary files /dev/null and b/docs/en/images/setting-management-time-zone-ui.png differ diff --git a/docs/en/modules/account-pro.md b/docs/en/modules/account-pro.md index e3e7884cc0..c53a6f24ec 100644 --- a/docs/en/modules/account-pro.md +++ b/docs/en/modules/account-pro.md @@ -104,6 +104,48 @@ If you use `Social / External Logins`, It is automatically called for authentica ![account-pro-module-local-login-setting](../images/account-pro-module-local-login-setting.png) +### Switching users during OAuth login + +If you have an OAuth/Auth Server application using the Account Pro module, you can pass the `prompt=select_account` parameter to force the user to select an account. + +Example to pass `prompt=select_account` parameter in OpenIdConnect: + +```csharp +.AddAbpOpenIdConnect("oidc", options => +{ + // ... + options.Events = new OpenIdConnectEvents + { + OnRedirectToIdentityProvider = redirectContext => + { + redirectContext.ProtocolMessage.Prompt = "select_account"; + return Task.CompletedTask; + } + }; + // ... +}); +``` + +![account-pro-secect-account](../images/account-pro-select-account-parameter.png) + +You have three options: + +- Continue: The login process will continue with the current account. +- Switch to another account: Will be redirected to the login page to log in with another account. +- Create a new account: Will be redirected to the register page to create a new account. + +> The OAuth login process will continue after the user selects one of the options. + +![account-pro-secect-account](../images/account-pro-select-account.png) + +All available prompt parameters: + +| **Parameter** | **Description** | +|------------------|-------------------------------------------------------------------------------------------------------------| +| `login` | Forces the user to re-authenticate, even if they are already logged in. | +| `consent` | Forces the user to re-consent to the requested permissions, even if they have consented before. | +| `select_account` | Forces the user to select an account, even if they are already logged in (especially relevant if multiple accounts are available). | +| `none` | Does not trigger any prompt. If the user is not logged in, or their consent is not granted, it will return an error or redirect accordingly. | ## Social / External Logins @@ -113,7 +155,7 @@ The application startup template comes with **Twitter**, **Google** and **Micros ![account-pro-external-login-settings](../images/account-pro-external-login-settings.png) -The social/External login system is compatible with the multi-tenancy. Each tenant can configure their own provider settings if your application is multi-tenant. +The social/External login system is compatible with the multi-tenancy. Each tenant can enable or disable the external login provider and configure their own provider settings if your application is multi-tenant. ### Install a new External Login @@ -137,7 +179,7 @@ context.Services.AddAuthentication() facebook.Scope.Add("public_profile"); }) .WithDynamicOptions( - FacebookDefaults.AuthenticationScheme, + FacebookDefaults.AuthenticationScheme, // Facebook options => { options.WithProperty(x => x.AppId); @@ -149,6 +191,18 @@ context.Services.AddAuthentication() * `AddFacebook()` is the standard method that you can set hard-coded configuration. * `WithDynamicOptions` is provided by the Account Module which makes possible to configure the provided properties on the UI. +#### Localize Provider Properties + +You can add following translation to localize the properties of the external login providers: + +`en.json`: + +````json +"ExternalProvider:Facebook": "Facebook", +"ExternalProvider:Facebook:AppId": "App ID", +"ExternalProvider:Facebook:AppSecret": "App Secret", +```` + ### IPostConfigureAccountExternalProviderOptions Some external logins may be initialized based on dynamic properties. You can implement an `IPostConfigureAccountExternalProviderOptions` to initialize again after dynamic properties are initialized. @@ -219,6 +273,12 @@ Users who register via both local registration and external/social login using t ![require-local-password-on-social-account-linking](../images/require-local-password-on-social-account-linking.png) +### Time Zone Setting + +Users can to set their own time zone in the account settings page if application is [supports multiple timezones](../framework/infrastructure/timing.md#clock-options). + +![account-pro-time-zone-setting](../images/account-pro-time-zone-setting.png) + ## Internals ### Settings diff --git a/docs/en/modules/cms-kit-pro/url-forwarding.md b/docs/en/modules/cms-kit-pro/url-forwarding.md index a059ef1305..10ca0a7f98 100644 --- a/docs/en/modules/cms-kit-pro/url-forwarding.md +++ b/docs/en/modules/cms-kit-pro/url-forwarding.md @@ -37,6 +37,20 @@ You can create new forwardings or update/delete existing ones, in the admin side ![url-forwarding-page](../../images/url-forwarding-page.png) + +## Options + +### ShortenedUrlCacheOptions + +`ShortenedUrlCacheOptions` is used to configure the cache settings for the URL forwarding system. Example: + +```csharp +Configure(options => +{ + options.CacheAllOnStartup = true; // Cache all shortened URLs on startup +}); +``` + # Internals ## Domain Layer diff --git a/docs/en/modules/docs.md b/docs/en/modules/docs.md index 69de3362a6..b564980ba3 100644 --- a/docs/en/modules/docs.md +++ b/docs/en/modules/docs.md @@ -73,25 +73,25 @@ Or you can also manually install nuget package to each project: * Install [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Domain` project. ```bash - Install-Package Volo.Docs.Domain + dotnet add package Volo.Docs.Domain ``` * Install [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) nuget package to `Acme.MyProject.EntityFrameworkCore` project. ```bash - Install-Package Volo.Docs.EntityFrameworkCore + dotnet add package Volo.Docs.EntityFrameworkCore ``` * Install [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) nuget package to `Acme.MyProject.Application` project. ```bash - Install-Package Volo.Docs.Application + dotnet add package Volo.Docs.Application ``` * Install [Volo.Docs.Web](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Web` project. ```bash - Install-Package Volo.Docs.Web + dotnet add package Volo.Docs.Web ``` ##### 3.2.1- Adding Module Dependencies diff --git a/docs/en/modules/gdpr.md b/docs/en/modules/gdpr.md index 76875e0403..bef763a41a 100644 --- a/docs/en/modules/gdpr.md +++ b/docs/en/modules/gdpr.md @@ -48,7 +48,14 @@ The "Personal Data" page is used to manage personal data requests. You can view ![gdpr](../images/gdpr-personal-data-page.png) -To see the other features of the GDPR module, visit [the module description page](https://abp.io/modules/Volo.Gdpr). +The GDPR module is designed for distributed architectures. When a user requests their personal data, the module publishes two events: + +- `GdprUserDataRequestedEto`: Triggers personal data collectors to prepare user data +- `GdprUserDataDeletionRequestedEto`: Triggers personal data collectors to delete user data + +You can subscribe to these events to implement custom data collection and deletion logic in your modules. See the [Distributed Events](#distributed-events) section for more details. + +> To see the other features of the GDPR module, visit [the module description page](https://abp.io/modules/Volo.Gdpr). ## Options @@ -227,8 +234,13 @@ This [Event Transfer Object](../framework/infrastructure/event-bus/distributed#e ### GdprUserDataPreparedEto -This [Event Transfer Object](../framework/infrastructure/event-bus/distributed#event-transfer-object) is used to save the collected personal data into a single JSON file by module. +This [Event Transfer Object](../framework/infrastructure/event-bus/distributed#event-transfer-object) is used to save the collected personal data into a single JSON file per module. Typically, you don't need to implement this event handler since the module already has an implementation that returns the collected data within a zip file containing multiple JSON files, with each file containing data collected from a specific module. + +### GdprUserDataDeletionRequestedEto + +This [Event Transfer Object](../framework/infrastructure/event-bus/distributed#event-transfer-object) is published when a user requests to permanently delete their personal data and account. By default, only the `IdentityGdprEventHandler` in the [Identity Pro Module](../modules/identity-pro) subscribes to this event to anonymize the user's data and delete their account (using soft-delete unless configured otherwise). +If you want to delete additional sensitive user data stored in other modules, you can subscribe to this event and implement custom deletion (or anonymization) logic in those modules. ## Cookie Consent @@ -248,9 +260,11 @@ If you want to override the texts in the Cookie Consent component, you just need > Refer to the [Localization documentation](../framework/fundamentals/localization.md) for more info about defining localization resources and overriding existing localization entries that comes from pre-built modules. -### Configuring the Cookie Consent +### Configuring Cookie Consent -You can add Cookie Consent to your application by configuring the `AddAbpCookieConsent` in your module class as below: +To enable cookie consent in your application, follow these two steps: + +**1. Configure the service in your module class (inside the `ConfigureServices` method):** ```csharp context.Services.AddAbpCookieConsent(options => @@ -261,4 +275,23 @@ context.Services.AddAbpCookieConsent(options => }); ``` -After configuring the `AddAbpCookieConsent` and setting it enabled, a cookie consent text will be prepared according to the options and a cookie consent banner will be seen at the bottom of the page. Thus, the users of the application will be informed about the Cookie Policy and Privacy Policy of the company/application. +**2. Add the middleware (`UseAbpCookieConsent`) to the request pipeline (in the `OnApplicationInitialization` method):** + +```diff +public override void OnApplicationInitialization(ApplicationInitializationContenxt context) +{ + var app = context.GetApplicationBuilder(); + //... + ++ app.UseAbpCookieConsent(); + app.UseCorrelationId(); + app.UseRouting(); + app.MapAbpStaticAssets(); + app.UseAbpSecurityHeaders(); + app.UseAuthentication(); + + //... +} +``` + +Once configured, a cookie consent banner will be shown at the bottom of the page. It includes links to your _Cookie Policy_ and _Privacy Policy_, helping inform users and support GDPR compliance. diff --git a/docs/en/modules/language-management.md b/docs/en/modules/language-management.md index 77490f28ef..3bce0d46b0 100644 --- a/docs/en/modules/language-management.md +++ b/docs/en/modules/language-management.md @@ -62,7 +62,7 @@ This module adds some initial data (see [the data seed system](../framework/infr * Creates language records configured using `AbpLocalizationOptions`. -If you want to change the seeded language list, see the next section. +If you want to change the seeded language list, see the [Localization](../framework/fundamentals/localization.md#Supported-Languages) document. ## Internals diff --git a/docs/en/modules/setting-management.md b/docs/en/modules/setting-management.md index 32dd52f4b6..842233e8b4 100644 --- a/docs/en/modules/setting-management.md +++ b/docs/en/modules/setting-management.md @@ -118,12 +118,16 @@ The order of the providers are important. Providers are executed in the reverse ## Setting Management UI -Setting Mangement module provided the email setting UI by default. +Setting Mangement module provided the Email setting, Feature management and Timezone setting UI by default. ![EmailSettingUi](../images/setting-management-email-ui.png) > You can click the Send test email button to send a test email to check your email settings. +![FeatureManagementUi](../images/setting-management-feature-management-ui.png) + +![TimeZoneSettingUi](../images/setting-management-time-zone-ui.png) + Setting it is extensible; You can add your tabs to this page for your application settings. ### MVC UI diff --git a/docs/en/modules/virtual-file-explorer.md b/docs/en/modules/virtual-file-explorer.md index 21bccfd999..3157198c30 100644 --- a/docs/en/modules/virtual-file-explorer.md +++ b/docs/en/modules/virtual-file-explorer.md @@ -24,7 +24,7 @@ Or you can also manually install nuget package to `Acme.MyProject.Web` project: * Install [Volo.Abp.VirtualFileExplorer.Web](https://www.nuget.org/packages/Volo.Abp.VirtualFileExplorer.Web/) nuget package to `Acme.MyProject.Web` project. - `Install-Package Volo.Abp.VirtualFileExplorer.Web` + `dotnet add package Volo.Abp.VirtualFileExplorer.Web` ##### 2.1- Adding Module Dependencies diff --git a/docs/en/others/penetration-test-report.md b/docs/en/others/penetration-test-report.md index c9d8280405..0ed0628033 100644 --- a/docs/en/others/penetration-test-report.md +++ b/docs/en/others/penetration-test-report.md @@ -1,6 +1,6 @@ # ABP Penetration Test Report -The ABP Commercial MVC `v9.0.0` application template has been tested against security vulnerabilities by the [OWASP ZAP v2.14.0](https://www.zaproxy.org/) tool. The demo web application was started on the `https://localhost:44349` address. The below alerts have been reported by the pentest tool. These alerts are sorted by the risk level as high, medium, and low. The informational alerts are not mentioned in this document. +The ABP Commercial MVC `v9.1.0` application template has been tested against security vulnerabilities by the [OWASP ZAP v2.14.0](https://www.zaproxy.org/) tool. The demo web application was started on the `https://localhost:44349` address. The below alerts have been reported by the pentest tool. These alerts are sorted by the risk level as high, medium, and low. The informational alerts are not mentioned in this document. Many of these alerts are **false-positive**, meaning the vulnerability scanner detected these issues, but they are not exploitable. It's clearly explained for each false-positive alert why this alert is a false-positive. @@ -10,53 +10,23 @@ In the next sections, you will find the affected URLs, attack parameters (reques There are high _(red flag)_, medium _(orange flag)_, low _(yellow flag)_, and informational _(blue flag)_ alerts. -![penetration-test-9.0.0](../images/pen-test-alert-list-9.0.png) +![penetration-test-9.1.0](../images/pen-test-alert-list-9.1.png) > The informational alerts are not mentioned in this document. These alerts are not raising any risks on your application and they are optional. -### Path Traversal [Risk: High] - False Positive +### Spring4Shell [Risk: High] - False Positive -- *[POST] - https://localhost:44349/Account/Login* (attack: **\Login**) -- *[POST] - https://localhost:44349/Account/LinkLogin* (attack: **\LinkLogin**) -- *[POST] - https://localhost:44349/Account/Register* (attack: **\Register**) -- *[POST] - https://localhost:44349/Account/SecurityLogs* (attack: **\SecurityLogs**) -- *[POST] - https://localhost:44349/Identity/SecurityLogs* (attack: **\SecurityLogs**) +- *[POST] - https://localhost:44349/Account/ForgotPassword* (attack: **class.module.classLoader.DefaultAssertionStatus=nonsense**) +- *[POST] - https://localhost:44349/Account/Login* (attack: **class.module.classLoader.DefaultAssertionStatus=nonsense**) +- *[POST] - https://localhost:44349/Account/Login?ReturnUrl=%2FSettingManagement* (attack: **class.module.classLoader.DefaultAssertionStatus=nonsense**) **Description**: -The Path Traversal attack technique allows an attacker access to files, directories, and commands that potentially reside outside the web document root directory. An attacker may manipulate a URL in such a way that the website will execute or reveal the contents of arbitrary files anywhere on the web server. Any device that exposes an HTTP-based interface is potentially vulnerable to Path Traversal. - -**Solution**: - -This is a **false-positive** alert since ABP does all related checks for this kind of attacks on the backend side for these endpoints. - -### SQL Injection [Risk: High] - False Positive - -* *[POST] — https://localhost:44349/Account/Login* (attack: **1q2w3E* AND 1=1 --**) -* *[POST] — https://localhost:44349/Account/ImpersonateUser* (attack: **CfDJ8Pyqeg0vtHtJpnK-9eLaft7-JxLJfJ6WHKPOdBZVxz14BDo061qpJ2NLplgAn2Hw16ec0IR38_wWAUkJGxP8hL6PcLfH0bh-ATNTspWyWYTGGbiH-zeKWiS5vWX-br2BA1hE7Dc45eWGUZNcVc_vm2s AND 1=1 --**) -* *[POST] — https://localhost:44349/Abp/MultiTenancy/TenantSwitchModal* (attack: **CfDJ8Pyqeg0vtHtJpnK-9eLaft7-JxLJfJ6WHKPOdBZVxz14BDo061qpJ2NLplgAn2Hw16ec0IR38_wWAUkJGxP8hL6PcLfH0bh-ATNTspWyWYTGGbiH-zeKWiS5vWX-br2BA1hE7Dc45eWGUZNcVc_vm2s AND 1=1 --**) -* *[POST] — https://localhost:44349/Identity/OrganizationUnits/\** (attack: **6f4cd0ab-f4eb-7ce0-8b26-3a138af1840d" AND '1'='1**) (also, several other URLs...) -* *[POST] — https://localhost:44349/Identity/ClaimTypes/CreateModal* (attack: **aaaad AND '1'='1**) - -**Description**: - -SQL injection may be possible. SQL injection is a web security vulnerability that allows an attacker to interfere with the queries that an application makes to its database. It allows an attacker to view data that they are not normally able to retrieve and perform unauthorized actions. +The application appears to be vulnerable to CVE-2022-22965 (otherwise known as Spring4Shell) - remote code execution (RCE) via data binding. **Explanation**: -ABP uses Entity Framework Core and LINQ. **It's safe against SQL Injection because it passes all data to the database via SQL parameters.** LINQ queries are not composed by using string manipulation or concatenation, that's why they are not susceptible to traditional SQL injection attacks. Therefore, this is a **false-positive** alert. - -### SQL Injection - Authentication Bypass [Risk: High] - False Positive - -* *[POST] — https://localhost:44349/Account/Login* (attack: **false AND 1=1 --**) - -**Description**: - -SQL injection may be possible on a login page, potentially allowing the application's authentication mechanism to be bypassed. - -**Solution**: - -This alert indicates that we must not trust client side input (even if there is client side validation in place) and check all data on the server side. ABP Framework already does that and makes server-side validations while authenticating a user. Therefore this is a **false-positive** alert. +ABP Framework is built on top of ASP.NET Core and does not use the Spring Framework. This application does not rely on Java-based technologies, making it immune to vulnerabilities like Spring4Shell. The detection is a false positive as there are no Spring dependencies in the project. ### Absence of Anti-CSRF Tokens [Risk: Medium] — False Positive @@ -130,6 +100,10 @@ Configure(options => - *[GET] — https://localhost:44349/Abp/Languages/Switch?culture=ZAP%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%0A&returnUrl=%2F&uiCulture=ar* (with combination of different parameters) - *[GET] — https://localhost:44349/Abp/ApplicationLocalizationScript?cultureName=ZAP%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%0A* (with combination of different parameters) +- *[GET] — https://localhost:44349/api/language-management/language-texts?filter=aa&resourceName=&baseCultureName=es&targetCultureName=ZAP%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%0A&getOnlyEmptyValues=false&sorting=name+asc&skipCount=0&maxResultCount=10* (with combination of different parameters) +- *[GET] — https://localhost:44349/LanguageManagement/Texts/Edit?name=IncorrectCaptchaAnswer&targetCultureName=sv&resourceName=AbpAccount&baseCultureName=ZAP%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%25n%25s%0A* (with combination of different parameters) +- *[POST] — https://localhost:44349/Account/Login?ReturnUrl=%2FSettingManagement* +- *[POST] — https://localhost:44349/Account/Manage* (with combination of different parameters) **Description:** @@ -149,14 +123,9 @@ The second URL is also a **false-positive** alert because there is no bad charac ### XSLT Injection [Risk: Medium] - False Positive -- *[GET] — https://localhost:44349/Abp/Languages/Switch?culture=%3Cxsl%3Avalue-of+select%3D%22system-property%28%27xsl%3Avendor%27%29%22%2F%3E&returnUrl=%2F&uiCulture=ar* -- *[POST] — https://localhost:44349/Account/Login _(same URL with different parameters...)_* -- *[POST] — https://localhost:44349/Account/ImpersonateUser _(same URL with different parameters...)_* -- *[POST] — https://localhost:44349/Account/Register _(same URL with different parameters...)_* -- *[POST] — https://localhost:44349/Account/Manage _(same URL with different parameters...)_* +- *[GET] — https://localhost:44349/Abp/Languages/Switch?culture=%3Cxsl%3Avalue-of+select%3D%22system-property%28%27xsl%3Avendor%27%29%22%2F%3E&returnUrl=%2F&uiCulture=tr _(same URL with different parameters...)_* - *[POST] — https://localhost:44349/Account/ForgotPassword _(same URL with different parameters...)_* -- *[POST] — https://localhost:44349/SaasWidgets/LatestTenants _(same URL with different parameters...)_* -- *[POST] — https://localhost:44349/AuditLogs* +- *[GET] — https://localhost:44349/SaasWidgets/LatestTenants _(same URL with different parameters...)_* **Description**: @@ -168,10 +137,12 @@ This is a **false-positive** alert. v9.0 uses .NET 9 and the XSLT transformation ### Application Error Disclosure [Risk: Low] — False Positive -- *[GET] — https://localhost:44349/Abp/Languages/Switch* - *[POST] — https://localhost:44349/Account/ImpersonateUser* - *[GET] — https://localhost:44349/Account/ExternalLogins* -- *[GET] — https://localhost:44349/Account/Logout* +- *[GET] — https://localhost:44349/OrganizationUnits* +- *[GET] — https://localhost:44349/HostDashboard* +- *[GET] — https://localhost:44349/Saas/Host/Editions* +- *[GET] — https://localhost:44349/Saas/Host/Tenants* **Description:** @@ -326,7 +297,7 @@ This vulnerability was reported as a positive alert, because ABP uses the [zxcvb - *[GET] — https://localhost:44349/client-proxies/account-proxy.js?_v=638550091940000000 (and other client-proxies related URLs...)* - *[GET] — https://localhost:44349/favicon.svg* -- *[GET] — https://localhost:44349/images/getting-started/bg-01.png* (and other image URLs...) +- *[GET] — https://localhost:44349/images/getting-started/bg-01.png* (and other image URLs...) - *[GET] — https://localhost:44349/global-styles.css?_v=638556076064360335* - *[GET] — https://localhost:44349/libs/@fortawesome/fontawesome-free/css/all.css?_v=%5CWEB-INF%5Cweb.xml (other several URLs...)* - other URLs... diff --git a/docs/en/release-info/migration-guides/abp-8-2.md b/docs/en/release-info/migration-guides/abp-8-2.md index 15de922d4f..5f811da2d4 100644 --- a/docs/en/release-info/migration-guides/abp-8-2.md +++ b/docs/en/release-info/migration-guides/abp-8-2.md @@ -28,6 +28,7 @@ With this version on, ABP Framework allows you to use single blog mode, without * `Volo.Blogging.Pages.Members` -> `Volo.Blogging.Pages.Blogs.Members` (members folder) > If you haven't overridden the pages above, then you don't need to make any additional changes. See [#19418](https://github.com/abpframework/abp/pull/19418) for more information. + ## Removed `FlagIcon` property from the `ILanguageInfo` The `FlagIcon` property has been removed from the `ILanguageInfo` interface since we removed the flag icon library in the earlier versions from all of our themes and none of them using it now. @@ -57,11 +58,20 @@ In this version, the Angular UI has been updated to use the Angular version 17.3 The **Session Management** feature allows you to prevent concurrent login and manage user sessions. -In this version, a new entity called `IdentitySession` has been added to the framework and you should create a new migration and apply it to your database. +In this version, a new entity called `IdentitySession` has been added to the framework and you need to add new `DbSet` to your `DbContext` class if it implements `IIdentityDbContext` interface. + +```csharp +public class YourDbContext : AbpDbContext, IIdentityDbContext +{ + public DbSet Sessions { get; set; } +} +``` + +You should also create a new migration and apply it to your database. ## Upgraded NuGet Dependencies -You can see the following list of NuGet libraries that have been upgraded with this release, if you are using one of these packages explicitly, you may consider upgrading them in your solution: +You can see the following list of NuGet libraries that have been upgraded with this release, **if you are using one of these packages explicitly**, you may consider upgrading them in your solution, especially **Microsoft.IdentityModel.*** packages: | Package | Old Version | New Version | | ---------------------------------------------------------- | ----------- | ----------- | diff --git a/docs/en/release-info/migration-guides/abp-9-2.md b/docs/en/release-info/migration-guides/abp-9-2.md new file mode 100644 index 0000000000..b5596b3415 --- /dev/null +++ b/docs/en/release-info/migration-guides/abp-9-2.md @@ -0,0 +1,64 @@ +# ABP Version 9.2 Migration Guide + +This document is a guide for upgrading ABP v9.x solutions to ABP v9.2. There are some changes in this version that may affect your applications, please read it carefully and apply the necessary changes to your application. + +## Open-Source (Framework) + +### Added `ApplicationName` Property to Isolate Background Jobs & Background Workers + +When multiple applications share the same storage for [ABP's Background Jobs Module](../../modules/background-jobs.md), jobs from one application might be visible to another application. This can lead to the following issues: + +1. Applications may attempt to process jobs that don't belong to them +2. These attempts fail with "Undefined background job for the job name" error +3. Failed jobs are marked as `IsAbandoned = true` +4. The original application can no longer process these abandoned jobs + +To fix this, we added the `ApplicationName` property to the `AbpBackgroundJobWorkerOptions` class. This property allows you to specify the application name, which helps isolate jobs between different applications. (See the [PR](https://github.com/abpframework/abp/pull/22169) for more details.) + +**By default there is no breaking change. However, you need to create a migration for the database to add the `ApplicationName` column to the relevant table and apply it to your database.** + +### Upgraded `MongoDB.Driver` to `3.1.0` + +In this version, we upgraded `MongoDB.Driver` to `3.1.0`. To migrate your application, please refer to our [MongoDB Driver 2 to 3 Migration Guide](./MongoDB-Driver-2-to-3.md) document. + +### Replaced Toastr with Custom Implementation (without depending on any 3rd party library) + +In this version, we replaced Toastr with a custom implementation that does not depend on any 3rd party library. This is a breaking change if you are using the `Toastr` library. + +Here are the migration steps: + +1. Remove any direct Toastr dependencies from your application +2. Update your notification calls to use the new API +3. Migrate any custom styles or configurations + +For detailed implementation guidelines and API documentation, see: + +- [Notify Documentation](../../framework/ui/mvc-razor-pages/javascript-api/notify.md) +- [PR #21940 for more information](https://github.com/abpframework/abp/pull/21940) + + +## PRO + +> Please check the **Open-Source (Framework)** section before reading this section. The listed topics might affect your application and you might need to take care of them. + +If you are a paid-license owner and using the ABP's paid version, then please follow the following sections to get informed about the breaking changes and apply the necessary ones: + +### Identity Pro Module: Require Email Verification to Register + +In this version, we added a new setting to the Identity Pro module to require email verification to register. This is a security measure to prevent spam registrations: + +![](./images/require-email-verification-for-register.png) + +Typically, no changes are required. However, if you have inherited from the `AccountAppService` class and implemented your own logic, you'll need to update your constructor to match the new signature since two new services are now injected: + +```diff +//code omitted for brevity... + ++ protected IDistributedCache EmailConfirmationCodeCache { get; } ++ protected IdentityErrorDescriber IdentityErrorDescriber { get; } + + public AccountAppService( +- IdentityUserTwoFactorChecker identityUserTwoFactorChecker) ++ IdentityUserTwoFactorChecker identityUserTwoFactorChecker, ++ IdentityErrorDescriber identityErrorDescriber) +``` diff --git a/docs/en/release-info/migration-guides/identityserver4-step-by-step.md b/docs/en/release-info/migration-guides/identityserver4-step-by-step.md index 6e58edc023..12973b6073 100644 --- a/docs/en/release-info/migration-guides/identityserver4-step-by-step.md +++ b/docs/en/release-info/migration-guides/identityserver4-step-by-step.md @@ -76,7 +76,7 @@ typeof(AbpPermissionManagementDomainIdentityServerModule), DataSeeder is the most important part for starting the application since it seeds the initial data for both OpenID providers. -- Create a folder named *IdentityServer* under the Domain project and copy the [IdentityServerDataSeedContributor.cs](https://github.com/abpframework/abp-samples/blob/master/Ids2OpenId/src/Ids2OpenId.Domain/IdentityServer/IdentityServerDataSeedContributor.cs) under this folder. **Rename** all the `OpenId2Ids` with your project name. +- Create a folder named *IdentityServer* under the Domain project and copy the [IdentityServerDataSeedContributor.cs](https://github.com/abpframework/abp-samples/blob/1dc297255ca22af02ef6d71092dbc1b394f9260a/Ids2OpenId/src/Ids2OpenId.Domain/IdentityServer/IdentityServerDataSeedContributor.cs) under this folder. **Rename** all the `OpenId2Ids` with your project name. - Delete *OpenIddict* folder that contains `OpenIddictDataSeedContributor.cs` which is no longer needed. ### EntityFrameworkCore Layer diff --git a/docs/en/release-info/migration-guides/images/require-email-verification-for-register.png b/docs/en/release-info/migration-guides/images/require-email-verification-for-register.png new file mode 100644 index 0000000000..597eaa6118 Binary files /dev/null and b/docs/en/release-info/migration-guides/images/require-email-verification-for-register.png differ diff --git a/docs/en/release-info/migration-guides/index.md b/docs/en/release-info/migration-guides/index.md index 821fbf5469..4a8eaaf467 100644 --- a/docs/en/release-info/migration-guides/index.md +++ b/docs/en/release-info/migration-guides/index.md @@ -2,6 +2,7 @@ The following documents explain how to migrate your existing ABP applications. We write migration documents only if you need to take an action while upgrading your solution. Otherwise, you can easily upgrade your solution using the [abp update command](../upgrading.md). +- [9.x to 9.2](abp-9-2.md) - [9.0 to 9.1](abp-9-1.md) - [8.x to 9.0](abp-9-0.md) - [8.x to 8.3](abp-8-3.md) diff --git a/docs/en/release-info/release-notes.md b/docs/en/release-info/release-notes.md index f0e91ec4a8..4c6bec206e 100644 --- a/docs/en/release-info/release-notes.md +++ b/docs/en/release-info/release-notes.md @@ -1,12 +1,27 @@ # Release Notes -This document contains **brief release notes** for each release. Release notes only include **major features** and **visible enhancements**. They don't include all the development done in the related version. To see raw and detailed change logs for every release, please check the related milestone and [the change logs page](https://abp.io/pro-releases) (only for paid license holders). +This document contains **brief release notes** for each release. Release notes only include **major features** and **visible enhancements**. They don't include all the development done in the related version. -> If you want to read the release notes for each ABP Studio release, check it out from [here](../studio/release-notes.md). +Also see the following notes about ABP releases: -## 9.1 (2025-01-16) +* [ABP Studio release notes](../studio/release-notes.md) +* [Change logs for ABP pro packages](https://abp.io/pro-releases) -This is currently a RC (release-candidate) and you can see the detailed **[blog post / announcement](https://abp.io/blog/abp-9-1-release-candidate)** for the v9.1 release. + +## 9.2 (2025-03-25) + +This is currently a RC (release-candidate) and you can see the detailed **[blog post / announcement](https://abp.io/community/articles/abp-platform-9.2-rc-has-been-released-jpq072nh)** for the v9.2 release. + +* Added `ApplicationName` Property to Isolate Background Jobs & Background Workers +* Docs Module: Added "Alternative Words" to Filter Items +* Introducing the [Bunny BLOB Storage Provider](../framework/infrastructure/blob-storing/bunny.md) +* Upgraded `MongoDB.Driver` to **v3.1.0** +* Identity Pro Module: Require Email Verification to Register +* Switching users during OAuth login + +## 9.1 (2025-03-05) + +See the detailed **[blog post / announcement](https://abp.io/community/articles/abp.io-platform-9.1-final-has-been-released-h96a56qa)** for the v9.1 release. * Upgraded to Angular 19 * Upgraded to OpenIddict 6.0 diff --git a/docs/en/release-info/road-map.md b/docs/en/release-info/road-map.md index 7a5a400bf4..d2ae30999f 100644 --- a/docs/en/release-info/road-map.md +++ b/docs/en/release-info/road-map.md @@ -4,9 +4,9 @@ This document provides a road map, release schedule, and planned features for th ## Next Versions -### v9.2 +### v9.3 -The next version will be 9.2 and planned to release the stable 9.2 version in June 2025. We will be mostly working on the following topics: +The next version will be 9.3 and planned to release the stable 9.3 version in July 2025. We will be mostly working on the following topics: * Framework * Upgrading 3rd-party dependencies @@ -15,22 +15,20 @@ The next version will be 9.2 and planned to release the stable 9.2 version in Ju * ABP Suite * Define navigation properties without target string property dependency * Improvements one-to-many scenarios - * Access to default code generation templates for customized templates * File Upload Modal enhancements + * Master/Detail DataGrid Toggle Detail Row Enhancements for Blazor UI + * ABP Studio + * Allow to directly create new solutions with ABP's RC (Release Candidate) versions * Automate more details on new service creation for a microservice solution * Support multiple concurrent Kubernetes deployment/integration scenarios * Improve the module installation experience / installation guides - * Auto-install 3rd-party dependencies - * Better handle long log files - * Allow to directly create new solutions with ABP's RC (Release Candidate) versions - * Support Intel processors for Mac computers, support ARM chipset for Windows and support Linux OS * Improve client proxy generation experience * Modular Monolith Application Startup Template * Application modules - * Account module: Support mixed social/local login scenarios & enforcing email verification in wide aspect + * Account module: Support mixed social/local login scenarios & adding security related features * UI/UX improvements on existing application modules * Updating existing tutorials & documents (with other UI & DB options) @@ -55,6 +53,8 @@ The ABP framework is [open source](https://github.com/abpframework/abp) and free * [#15932](https://github.com/abpframework/abp/issues/15932) / Introduce ABP Diagnostics Module * [#16744](https://github.com/abpframework/abp/issues/16744) / State Management API * [#17815](https://github.com/abpframework/abp/issues/17815) / Operation Rate Limiting +* [#119](https://github.com/abpframework/abp/issues/119) / REST API Versioning Improvements +* [#2087](https://github.com/abpframework/abp/issues/2087) / RavenDB Database Support ### Application Modules / UI Themes @@ -100,7 +100,7 @@ Here, are some of the important planned features for next ABP Studio versions: Here, are some of the important planned features for the next ABP Suite versions: -* Handle image properties for entities +* Handle image properties for entities (in addition to file properties, which is already supported) * Allow to define extra properties for DTOs those are not a part of the entity * Allow to create pages instead of modals for CRUD page generation * View-only (detail view) modal/page for an entity @@ -111,7 +111,7 @@ Here, are some of the important planned features for the next ABP Suite versions ## Feature Requests -Vote for your favorite feature on the related GitHub issues (and write your thoughts). You can create an issue on [the GitHub repository](https://github.com/abpframework/abp) for your feature requests, but first search in the existing issues please. You can also contact info@abp.io for your feature requests and other suggestions. +Vote for your favorite feature on the related GitHub issues (and write your thoughts). You can create an issue on [the GitHub repository](https://github.com/abpframework/abp) for your feature requests, but please first search the existing issues. You can also contact [info@abp.io](mailto:info@abp.io) for your feature requests and other suggestions. ## See Also diff --git a/docs/en/samples/index.md b/docs/en/samples/index.md index 6aea3d3fa5..39512d0134 100644 --- a/docs/en/samples/index.md +++ b/docs/en/samples/index.md @@ -9,6 +9,8 @@ A reference application built with ABP. It implements the Domain Driven Design w * [Live demo](https://www.openeventhub.com/) * [Source code](https://github.com/abpframework/eventhub) +![samples-event-hub](../images/samples-eventhub.png) + ## eShopOnAbp > ⚠️ **Important Notice** @@ -18,6 +20,8 @@ Reference microservice solution built with ABP and .NET. * [Source code](https://github.com/abpframework/eShopOnAbp) +![eshoponabp](../images/samples-eshoponabp.png) + ## CMS Kit Demo A minimal example website built with the [CMS Kit module](../modules/cms-kit/index.md). @@ -25,6 +29,8 @@ A minimal example website built with the [CMS Kit module](../modules/cms-kit/ind * [Live demo](https://cms-kit-demo.abpdemo.com/) * [Source code](https://github.com/abpframework/cms-kit-demo) +![samples-cms-kit](../images/samples-cms-kit.png) + ## Easy CRM A middle-size CRM application built with ABP. @@ -32,6 +38,8 @@ A middle-size CRM application built with ABP. * [Live demo](http://easycrm.abp.io/) * [Click here](easy-crm.md) to see the details and download the source code. +![samples-easy-crm](../images/samples-easycrm.png) + ## Book Store A simple CRUD application to show basic principles of developing an application with ABP. The same sample was implemented with different technologies and different modules: diff --git a/docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action.md b/docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action.md index a9ec807c64..14f2a48d66 100644 --- a/docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action.md +++ b/docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action.md @@ -480,7 +480,7 @@ push: workflow_dispatch: jobs: -build: + build: runs-on: ubuntu-latest steps: diff --git a/docs/en/solution-templates/layered-web-application/deployment/deployment-iis.md b/docs/en/solution-templates/layered-web-application/deployment/deployment-iis.md index 3d33cbd7a1..0de00c8d35 100644 --- a/docs/en/solution-templates/layered-web-application/deployment/deployment-iis.md +++ b/docs/en/solution-templates/layered-web-application/deployment/deployment-iis.md @@ -283,6 +283,34 @@ See: - https://learn.microsoft.com/en-us/aspnet/web-api/overview/testing-and-debugging/troubleshooting-http-405-errors-after-publishing-web-api-applications#resolve-http-405-errors - https://learn.microsoft.com/en-us/troubleshoot/developer/webapps/iis/site-behavior-performance/http-error-405-website#resolution-for-cause-3 +## Publish the Application(s) as IIS sub-application + +If your MVC application is a sub-application, you need to set the `BaseUrl` property of `AbpThemingOptions` to the sub-application’s path. The `BaseUrl` is used to configure the `base` element in the `head` section of the layout page. + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + Configure(options => + { + options.BaseUrl = "/myapp/"; + }); +} +``` + +```html + + + + ... + + + ... + + +``` + +For Blazor applications, you can to set the `base` tag in the `App.razor` file instead of configure `AbpThemingOptions`. + ## How to get stdout-log If your application is running on IIS and getting errors like `502.5, 500.3x`, you can enable stdout logs to see the error details. diff --git a/docs/en/studio/installation.md b/docs/en/studio/installation.md index 34dde8a5c9..c0e6aebd8f 100644 --- a/docs/en/studio/installation.md +++ b/docs/en/studio/installation.md @@ -5,25 +5,25 @@ ## Pre-requirements -Before you begin the installation process for ABP Studio, ensure that your system meets the following pre-requirements: +ABP Studio now features automatic installation of most required dependencies. When you first launch the application, it will check for and install the following components: -### Node -Make sure [Node.js](https://nodejs.org/en) is installed on your system. If you have not installed Node.js, you can download the `v22+` version from the official [Node.js website](https://nodejs.org/en/download/prebuilt-installer). +* .NET SDK +* Node.js +* ABP CLI +* mkcert (for HTTPS development) +* WireGuard (for Kubernetes operations) -### WireGuard (Optional) -ABP Studio needs [WireGuard](https://www.wireguard.com/) for Kubernetes operations. You can find the installation instructions for your specific operating system below: +The only manual installation required is: -**For Windows:** -Installation instructions for your Windows operating system are on the official [WireGuard website](https://www.wireguard.com/install/#windows-7-81-10-11-2008r2-2012r2-2016-2019-2022). - -**For macOS:** -Installation instructions for your macOS operating system are on the official [WireGuard website](https://www.wireguard.com/install/#macos-homebrew-and-macports-basic-cli-homebrew-userspace-go-homebrew-tools-macports-userspace-go-macports-tools). - -### Docker +### Docker (Required for Kubernetes Operations) ABP Studio needs [Docker](https://www.docker.com/) for [Kubernetes](https://kubernetes.io/) operations. Install Docker by following the guidelines on the official [Docker website](https://docs.docker.com/get-docker/). +### Package Manager Prerequisites +* **Windows:** The automatic installation process uses `winget`. If not already installed, ABP Studio will attempt to install it. +* **macOS:** The automatic installation process uses `brew`. If not already installed, you'll need to install it manually from [brew.sh](https://brew.sh/). + ## Installation -Now you have met the pre-requirements, follow the steps below to install ABP Studio: +Follow these steps to install ABP Studio: 1. **Download ABP Studio:** Visit [abp.io](https://abp.io/studio) to download the latest version of ABP Studio. @@ -31,8 +31,10 @@ Now you have met the pre-requirements, follow the steps below to install ABP Stu 2. **Run the Installer:** Execute the installer and follow the on-screen instructions to install ABP Studio on your computer. +3. **First Launch:** When you first launch ABP Studio, it will automatically check for and install required dependencies. This process may take several minutes, and you'll see progress indicators for each component being installed. + ## Login -After you install ABP Studio, you can log in to access all the features. To log in, follow the below steps: +After installation is complete, you can log in to access all features: 1. **Launch ABP Studio:** Open ABP Studio on your desktop. diff --git a/docs/en/tutorials/microservice/part-06.md b/docs/en/tutorials/microservice/part-06.md index af05bbd792..1cc19bf6d8 100644 --- a/docs/en/tutorials/microservice/part-06.md +++ b/docs/en/tutorials/microservice/part-06.md @@ -107,9 +107,9 @@ public class ProductIntegrationService : ApplicationService, IProductIntegration Now that we have created the `IProductIntegrationService` interface and the `ProductIntegrationService` class, we can consume this service from the Ordering service. -### Adding a Reference to the `CloudCrm.CatalogService.Contracts` Package +### Adding a Reference to the `CloudCrm.OrderingService` Package -First, we need to add a reference to the `CloudCrm.CatalogService.Contracts` package in the Ordering service. Open the ABP Studio, and stop the application(s) if it is running. Then, open the *Solution Explorer* and right-click on the `CloudCrm.OrderingService` package. Select *Add* -> *Package Reference* command: +First, we need to add a reference to the `CloudCrm.OrderingService` package in the Ordering service. Open the ABP Studio, and stop the application(s) if it is running. Then, open the *Solution Explorer* and right-click on the `CloudCrm.OrderingService` package. Select *Add* -> *Package Reference* command: ![add-package-reference-ordering-service](images/add-package-reference-ordering-service.png) @@ -304,3 +304,18 @@ Now, the Ordering service displays the product name instead of the product ID. W > **Design Tip** > > It is suggested that you keep that type of communication to a minimum and not couple your services with each other. It can make your solution complicated and may also decrease your system performance. When you need to do it, think about performance and try to make some optimizations. For example, if the Ordering service frequently needs product data, you can use a kind of [cache layer](../../framework/fundamentals/caching.md), so it doesn't make frequent requests to the Catalog service. + +### Updating the Kubernetes Configuration + +ABP microservice startup template provides [pre-configured Helm charts](../../solution-templates/microservice/helm-charts-and-kubernetes.md) to deploy your solution to Kubernetes. When you develop your solution, you should also care about configurations of these charts if you want to keep them up to date and working. + +In the section *Generating Proxy Classes for the Integration Service* above, we added a new configuration to the `appsettings.json` file of the Ordering microservice. We should configure the corresponding Helm chart configuration to keep it synchronized. + +Open the `etc\helm\cloudcrm\charts\ordering\templates\ordering.yaml` file in a text editor, and add the following lines under the `env` section, just like the other values present (be careful on indents since it is critical in YAML files): + +````yaml +- name: "RemoteServices__CatalogService__BaseUrl" + value: "http://{%{{{ .Release.Name }}}%}-catalog" +```` + +With this simple configuration, now the Ordering module can discover the catalog microservice's URL inside your Kubernetes cluster. diff --git a/docs/en/tutorials/modular-crm/part-03.md b/docs/en/tutorials/modular-crm/part-03.md index 2dabfc54eb..38c6553fa3 100644 --- a/docs/en/tutorials/modular-crm/part-03.md +++ b/docs/en/tutorials/modular-crm/part-03.md @@ -436,7 +436,7 @@ Open the `ModularCrm.Products` .NET solution in your IDE, and find the `Pages/Pr ![visual-studio-products-cshtml](images/visual-studio-products-cshtml.png) -Replace the `IndexModel.cshtml.cs` file with the following content: +Replace the `Index.cshtml.cs` file with the following content: ````csharp using System.Collections.Generic; diff --git a/docs/en/tutorials/modular-crm/part-06.md b/docs/en/tutorials/modular-crm/part-06.md index f06baa93f0..d2820c6bdc 100644 --- a/docs/en/tutorials/modular-crm/part-06.md +++ b/docs/en/tutorials/modular-crm/part-06.md @@ -256,7 +256,7 @@ Let's see what we've changed: * In the last line, we are converting the product list to a dictionary, where the key is `Guid Id` and the value is `string Name`. That way, we can easily find a product's name with its ID. * Finally, we are mapping the orders to `OrderDto` objects and setting the product name by looking up the product ID in the dictionary. -Open the `Index.cshtml` file, and change the `@order.ProductId` part by `@Model.ProductName` to write the product name instead of the product ID. The final `Index.cshtml` content should be the following: +Open the `Index.cshtml` file, and change the `@order.ProductId` part by `@Order.ProductName` to write the product name instead of the product ID. The final `Index.cshtml` content should be the following: ````html @page diff --git a/docs/en/tutorials/todo/layered/index.md b/docs/en/tutorials/todo/layered/index.md index e6e310a39a..86a480ad42 100644 --- a/docs/en/tutorials/todo/layered/index.md +++ b/docs/en/tutorials/todo/layered/index.md @@ -528,7 +528,7 @@ The interesting part here is how we communicate with the server. See the *Dynami ### Index.css -As the final touch, Create a file named `Index.css` in the `Pages` folder of the *TodoApp.Web* project and replace it with the following content: +As the final touch, Create a file named `Index.css` in the `Pages` folder of the *TodoApp.Web* project and add the following content: ```css #TodoList{ @@ -664,7 +664,7 @@ Open the `Index.razor` file in the `Pages` folder of the {{if UI=="Blazor" || UI ### Index.razor.css -As the final touch, open the `Index.razor.css` file in the `Pages` folder of the {{if UI=="Blazor" || UI=="BlazorWebApp"}}*TodoApp.Blazor.Client*{{else if UI=="BlazorServer"}} *TodoApp.Blazor* {{else if UI=="MAUIBlazor"}} *TodoApp.MauiBlazor* {{end}} project and replace it with the following content: +As the final touch, open the `Index.razor.css` file in the `Pages` folder of the {{if UI=="Blazor" || UI=="BlazorWebApp"}}*TodoApp.Blazor.Client*{{else if UI=="BlazorServer"}} *TodoApp.Blazor* {{else if UI=="MAUIBlazor"}} *TodoApp.MauiBlazor* {{end}} project and add the following content: ```css #TodoList{ @@ -827,7 +827,7 @@ Open the `/angular/src/app/home/home.component.html` file and replace its conten ### home.component.scss -As the final touch, open the `/angular/src/app/home/home.component.scss` file and replace its content with the following code block: +As the final touch, open the `/angular/src/app/home/home.component.scss` file and add the following code block: ```css #TodoList{ diff --git a/docs/en/tutorials/todo/single-layer/index.md b/docs/en/tutorials/todo/single-layer/index.md index dc865a0a75..6ce5bec5b0 100644 --- a/docs/en/tutorials/todo/single-layer/index.md +++ b/docs/en/tutorials/todo/single-layer/index.md @@ -309,10 +309,8 @@ public interface ITodoAppService : IApplicationService Create a `TodoAppService` class under the `Services` folder of {{if UI=="Blazor"}}your `TodoApp.Host` project{{else}}your project{{end}}, as shown below: ```csharp -using TodoApp.Services; using TodoApp.Services.Dtos; using TodoApp.Entities; -using Volo.Abp.Application.Services; using Volo.Abp.Domain.Repositories; namespace TodoApp.Services; @@ -517,7 +515,7 @@ The interesting part here is how we communicate with the server. See the *Dynami ### Index.cshtml.css -As for the final touch, open the `Index.cshtml.css` file in the `Pages` folder and replace with the following content: +As for the final touch, open the `Index.cshtml.css` file in the `Pages` folder and add the following code block at the end of the file: ````css #TodoList{ @@ -655,7 +653,7 @@ Open the `Index.razor` file in the `Pages` folder and replace the content with t ### Index.razor.css -As the final touch, open the `Index.razor.css` file in the `Pages` folder and replace it with the following content: +As the final touch, open the `Index.razor.css` file in the `Pages` folder and add the following code block at the end of the file: ````css #TodoList{ @@ -801,7 +799,7 @@ Open the `/angular/src/app/home/home.component.html` file and replace its conten ### home.component.scss -As the final touch, open the `/angular/src/app/home/home.component.scss` file and replace its content with the following code block: +As the final touch, open the `/angular/src/app/home/home.component.scss` file and add the following code block at the end of the file: ````css #TodoList{ diff --git a/docs/en/ui-themes/lepton-x/blazor.md b/docs/en/ui-themes/lepton-x/blazor.md index 5e49752ebb..2287b5aeac 100644 --- a/docs/en/ui-themes/lepton-x/blazor.md +++ b/docs/en/ui-themes/lepton-x/blazor.md @@ -347,8 +347,43 @@ namespace LeptonX.DemoApp.Blazor.MyComponents > Don't forget to remove repeated attributes from the razor page! ---- +#### Customizing the Footer Section + +You can add a new `LeptonXFooter.razor` file and override the `Footer` component as follows: + +```cs +@using Volo.Abp.DependencyInjection +@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu + +@inherits Footer +@attribute [ExposeServices(typeof(Footer))] +@attribute [Dependency(ReplaceServices = true)] + +
+
+ + +
+
+``` + +If you are using **Top Menu Layout**, then you can change the namespace as follows: + +```diff +- @using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu ++ @using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu +``` +> You can use the same approach customize the other components always. ## Common Components diff --git a/docs/en/ui-themes/lepton-x/images/account-layout-background-style.png b/docs/en/ui-themes/lepton-x/images/account-layout-background-style.png new file mode 100644 index 0000000000..5b2f32661d Binary files /dev/null and b/docs/en/ui-themes/lepton-x/images/account-layout-background-style.png differ diff --git a/docs/en/ui-themes/lepton-x/images/leptonx-mvc-customizing-footer.png b/docs/en/ui-themes/lepton-x/images/leptonx-mvc-customizing-footer.png new file mode 100644 index 0000000000..06721044b8 Binary files /dev/null and b/docs/en/ui-themes/lepton-x/images/leptonx-mvc-customizing-footer.png differ diff --git a/docs/en/ui-themes/lepton-x/mvc.md b/docs/en/ui-themes/lepton-x/mvc.md index ce12a0ea95..e41d12a331 100644 --- a/docs/en/ui-themes/lepton-x/mvc.md +++ b/docs/en/ui-themes/lepton-x/mvc.md @@ -47,7 +47,8 @@ Before starting to customize the theme, you can consider downloading the source --- ### Appearance -You can set a default theme, add or remove appearance styles by using **LeptonXThemeOptions**. + +You can set a default theme, add or remove appearance styles and layout background styles by using **LeptonXThemeOptions**. - `DefaultStyle`: Defines the default fallback theme. The default value is **Dim** @@ -133,15 +134,26 @@ Layout options of the MVC Razor Pages UI can be manageable by using **LeptonXThe - `MobileMenuSelector`: Defines items to be displayed at the mobile menu. The default value is the first 2 items from the main menu items. + ```csharp + Configure(options => + { + options.MobileMenuSelector = items => items.Where(x => x.MenuItem.Name == "MyProjectName.Home" || x.MenuItem.Name == "MyProjectName.Dashboard"); + }); + ``` + ![leptonx-mobile-menu-preview](images/mobile-menu-preview.png) +- `AccountLayoutBackgroundStyle`: Defines the background style of the account layout. + ```csharp Configure(options => { - options.MobileMenuSelector = items => items.Where(x => x.MenuItem.Name == "MyProjectName.Home" || x.MenuItem.Name == "MyProjectName.Dashboard"); + options.AccountLayoutBackgroundStyle = "background-image: url('/images/login-background-image.svg') !important;"; }); ``` + ![leptonx-account-layout-background-style](images/account-layout-background-style.png) + ### Layouts **LeptonX** offers two **ready-made layouts** for your web application. One of them is **placed** with the **menu items** on the **top** and the other with the **menu items** on the **sides**. @@ -162,6 +174,22 @@ Layout options of the MVC Razor Pages UI can be manageable by using **LeptonXThe > - **_Sidebar.cshtml** > - **_Toolbar.cshtml** +#### Customizing the Footer Section + +Add the **_Footer.cshtml** file under the **Themes/LeptonX/Layouts/Application** folder to customize the footer: + +```html + +```` + +![Customizing Footer](images/leptonx-mvc-customizing-footer.png) + +> You can customize the **_Sidebar.cshtml** and **_Toolbar.cshtml** files as well. + ### Account Layout ![Account layout](images/leptonx-account-layout-new.png) diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index ca471bb304..78f7ae0011 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -481,10 +481,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Sms.TencentCloud", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Sms.TencentCloud.Tests", "test\Volo.Abp.Sms.TencenCloud.Tests\Volo.Abp.Sms.TencentCloud.Tests.csproj", "{C753DDD6-5699-45F8-8669-08CE0BB816DE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Bundling", "src\Volo.Abp.AspNetCore.Bundling\Volo.Abp.AspNetCore.Bundling.csproj", "{75AA8A90-B3F6-43DF-ADA7-0990DEF44E2C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling", "src\Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling\Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling.csproj", "{70720321-DED4-464F-B913-BDA5BBDD7982}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Bunny", "src\Volo.Abp.BlobStoring.Bunny\Volo.Abp.BlobStoring.Bunny.csproj", "{1BBCBA72-CDB6-4882-96EE-D4CD149433A2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Bunny.Tests", "test\Volo.Abp.BlobStoring.Bunny.Tests\Volo.Abp.BlobStoring.Bunny.Tests.csproj", "{BC4BB2D6-DFD8-4190-AAC3-32C0A7A8E915}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Timing.Tests", "test\Volo.Abp.Timing.Tests\Volo.Abp.Timing.Tests.csproj", "{58FCF22D-E8DB-4EB8-B586-9BB6E9899D64}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1439,6 +1445,14 @@ Global {C753DDD6-5699-45F8-8669-08CE0BB816DE}.Debug|Any CPU.Build.0 = Debug|Any CPU {C753DDD6-5699-45F8-8669-08CE0BB816DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {C753DDD6-5699-45F8-8669-08CE0BB816DE}.Release|Any CPU.Build.0 = Release|Any CPU + {75AA8A90-B3F6-43DF-ADA7-0990DEF44E2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75AA8A90-B3F6-43DF-ADA7-0990DEF44E2C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75AA8A90-B3F6-43DF-ADA7-0990DEF44E2C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75AA8A90-B3F6-43DF-ADA7-0990DEF44E2C}.Release|Any CPU.Build.0 = Release|Any CPU + {70720321-DED4-464F-B913-BDA5BBDD7982}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70720321-DED4-464F-B913-BDA5BBDD7982}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70720321-DED4-464F-B913-BDA5BBDD7982}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70720321-DED4-464F-B913-BDA5BBDD7982}.Release|Any CPU.Build.0 = Release|Any CPU {1BBCBA72-CDB6-4882-96EE-D4CD149433A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1BBCBA72-CDB6-4882-96EE-D4CD149433A2}.Debug|Any CPU.Build.0 = Debug|Any CPU {1BBCBA72-CDB6-4882-96EE-D4CD149433A2}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -1447,6 +1461,10 @@ Global {BC4BB2D6-DFD8-4190-AAC3-32C0A7A8E915}.Debug|Any CPU.Build.0 = Debug|Any CPU {BC4BB2D6-DFD8-4190-AAC3-32C0A7A8E915}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC4BB2D6-DFD8-4190-AAC3-32C0A7A8E915}.Release|Any CPU.Build.0 = Release|Any CPU + {58FCF22D-E8DB-4EB8-B586-9BB6E9899D64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58FCF22D-E8DB-4EB8-B586-9BB6E9899D64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58FCF22D-E8DB-4EB8-B586-9BB6E9899D64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58FCF22D-E8DB-4EB8-B586-9BB6E9899D64}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1689,8 +1707,11 @@ Global {E50739A7-5E2F-4EB5-AEA9-554115CB9613} = {447C8A77-E5F0-4538-8687-7383196D04EA} {BE7109C5-7368-4688-8557-4A15D3F4776A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {C753DDD6-5699-45F8-8669-08CE0BB816DE} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {75AA8A90-B3F6-43DF-ADA7-0990DEF44E2C} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {70720321-DED4-464F-B913-BDA5BBDD7982} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {1BBCBA72-CDB6-4882-96EE-D4CD149433A2} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {BC4BB2D6-DFD8-4190-AAC3-32C0A7A8E915} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {58FCF22D-E8DB-4EB8-B586-9BB6E9899D64} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Bundling/FodyWeavers.xml b/framework/src/Volo.Abp.AspNetCore.Bundling/FodyWeavers.xml new file mode 100644 index 0000000000..00e1d9a1c1 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Bundling/FodyWeavers.xsd b/framework/src/Volo.Abp.AspNetCore.Bundling/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Bundling/Volo.Abp.AspNetCore.Bundling.csproj b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo.Abp.AspNetCore.Bundling.csproj new file mode 100644 index 0000000000..2654005740 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo.Abp.AspNetCore.Bundling.csproj @@ -0,0 +1,19 @@ + + + + + + + net9.0 + enable + Nullable + + + + + + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/AbpAspNetCoreBundlingModule.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/AbpAspNetCoreBundlingModule.cs new file mode 100644 index 0000000000..5e4967719f --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/AbpAspNetCoreBundlingModule.cs @@ -0,0 +1,15 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.Minify; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.AspNetCore.Bundling; + +[DependsOn( + typeof(AbpAspNetCoreMvcUiBundlingAbstractionsModule), + typeof(AbpMinifyModule), + typeof(AbpVirtualFileSystemModule) +)] +public class AbpAspNetCoreBundlingModule : AbpModule +{ +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleCache.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundleCache.cs similarity index 88% rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleCache.cs rename to framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundleCache.cs index e00bc84d6c..5ade7fc088 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleCache.cs +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundleCache.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; +namespace Volo.Abp.AspNetCore.Bundling; public class BundleCache : IBundleCache, ISingletonDependency { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleCacheItem.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundleCacheItem.cs similarity index 79% rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleCacheItem.cs rename to framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundleCacheItem.cs index f6830cbd74..f75f8b66db 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleCacheItem.cs +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundleCacheItem.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; +namespace Volo.Abp.AspNetCore.Bundling; public class BundleCacheItem { diff --git a/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundleManagerBase.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundleManagerBase.cs new file mode 100644 index 0000000000..8563918113 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundleManagerBase.cs @@ -0,0 +1,248 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Bundling.Scripts; +using Volo.Abp.AspNetCore.Bundling.Styles; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.AspNetCore.Bundling; + +public abstract class BundleManagerBase : IBundleManager +{ + public ILogger Logger { get; set; } + + protected readonly AbpBundlingOptions Options; + protected readonly AbpBundleContributorOptions ContributorOptions; + protected readonly IScriptBundler ScriptBundler; + protected readonly IStyleBundler StyleBundler; + protected readonly IServiceProvider ServiceProvider; + protected readonly IDynamicFileProvider DynamicFileProvider; + protected readonly IBundleCache BundleCache; + + public BundleManagerBase( + IOptions options, + IOptions contributorOptions, + IScriptBundler scriptBundler, + IStyleBundler styleBundler, + IServiceProvider serviceProvider, + IDynamicFileProvider dynamicFileProvider, + IBundleCache bundleCache) + { + Options = options.Value; + ContributorOptions = contributorOptions.Value; + ScriptBundler = scriptBundler; + ServiceProvider = serviceProvider; + DynamicFileProvider = dynamicFileProvider; + BundleCache = bundleCache; + StyleBundler = styleBundler; + + Logger = NullLogger.Instance; + } + + public virtual async Task> GetStyleBundleFilesAsync(string bundleName) + { + return await GetBundleFilesAsync(Options.StyleBundles, bundleName, StyleBundler); + } + + public virtual async Task> GetScriptBundleFilesAsync(string bundleName) + { + return await GetBundleFilesAsync(Options.ScriptBundles, bundleName, ScriptBundler); + } + + protected virtual async Task> GetBundleFilesAsync(BundleConfigurationCollection bundles, + string bundleName, IBundler bundler) + { + var files = new List(); + + var contributors = GetContributors(bundles, bundleName); + var bundleFiles = await GetBundleFilesAsync(contributors); + var dynamicResources = await GetDynamicResourcesAsync(contributors); + + if (!IsBundlingEnabled()) + { + return bundleFiles.Union(dynamicResources).ToImmutableList(); + } + + var localBundleFiles = new List(); + foreach (var bundleFile in bundleFiles) + { + if (!bundleFile.IsExternalFile) + { + localBundleFiles.Add(bundleFile.FileName); + } + else + { + if (localBundleFiles.Count != 0) + { + files.AddRange(AddToBundleCache(bundleName, bundler, localBundleFiles).Files); + localBundleFiles.Clear(); + } + + files.Add(bundleFile); + } + } + + if (localBundleFiles.Count != 0) + { + files.AddRange(AddToBundleCache(bundleName, bundler, localBundleFiles).Files); + } + + return files.Union(dynamicResources).ToImmutableList(); + } + + private BundleCacheItem AddToBundleCache(string bundleName, IBundler bundler, List bundleFiles) + { + var bundleRelativePath = + Options.BundleFolderName.EnsureEndsWith('/') + + bundleName + "." + bundleFiles.JoinAsString("|").ToMd5() + "." + bundler.FileExtension; + + return BundleCache.GetOrAdd(bundleRelativePath, () => + { + var cacheValue = new BundleCacheItem( + new List { new BundleFile("/" + bundleRelativePath) } + ); + + WatchChanges(cacheValue, bundleFiles, bundleRelativePath); + + var bundleResult = bundler.Bundle( + new BundlerContext( + bundleRelativePath, + bundleFiles, + IsMinficationEnabled() + ) + ); + + SaveBundleResult(bundleRelativePath, bundleResult); + + return cacheValue; + }); + } + + private void WatchChanges(BundleCacheItem cacheValue, List files, string bundleRelativePath) + { + lock (cacheValue.WatchDisposeHandles) + { + foreach (var file in files) + { + var watchDisposeHandle = GetFileProvider().Watch(file).RegisterChangeCallback(_ => + { + lock (cacheValue.WatchDisposeHandles) + { + cacheValue.WatchDisposeHandles.ForEach(h => h.Dispose()); + cacheValue.WatchDisposeHandles.Clear(); + } + + BundleCache.Remove(bundleRelativePath); + DynamicFileProvider.Delete("/wwwroot/" + bundleRelativePath); //TODO: get rid of wwwroot! + }, null); + + cacheValue.WatchDisposeHandles.Add(watchDisposeHandle); + } + } + } + + protected virtual void SaveBundleResult(string bundleRelativePath, BundleResult bundleResult) + { + var fileName = bundleRelativePath.Substring(bundleRelativePath.IndexOf('/') + 1); + + DynamicFileProvider.AddOrUpdate( + new InMemoryFileInfo( + "/wwwroot/" + bundleRelativePath, //TODO: get rid of wwwroot! + Encoding.UTF8.GetBytes(bundleResult.Content), + fileName + ) + ); + } + + public abstract bool IsBundlingEnabled(); + + + protected abstract bool IsMinficationEnabled(); + + protected virtual async Task> GetBundleFilesAsync(List contributors) + { + var context = CreateBundleConfigurationContext(); + + foreach (var contributor in contributors) + { + await contributor.PreConfigureBundleAsync(context); + } + + foreach (var contributor in contributors) + { + await contributor.ConfigureBundleAsync(context); + } + + foreach (var contributor in contributors) + { + await contributor.PostConfigureBundleAsync(context); + } + + return context.Files; + } + + protected virtual async Task> GetDynamicResourcesAsync(List contributors) + { + var context = CreateBundleConfigurationContext(); + + foreach (var contributor in contributors) + { + await contributor.ConfigureDynamicResourcesAsync(context); + } + + return context.Files; + } + + protected virtual BundleConfigurationContext CreateBundleConfigurationContext() + { + return new BundleConfigurationContext(ServiceProvider, GetFileProvider(), + Options.Parameters); + } + + protected abstract IFileProvider GetFileProvider(); + + protected virtual List GetContributors(BundleConfigurationCollection bundles, string bundleName) + { + var contributors = new List(); + + AddContributorsWithBaseBundles(contributors, bundles, bundleName); + + for (var i = 0; i < contributors.Count; ++i) + { + var extensions = ContributorOptions.Extensions(contributors[i].GetType()).GetAll(); + if (extensions.Count > 0) + { + contributors.InsertRange(i + 1, extensions); + i += extensions.Count; + } + } + + return contributors; + } + + protected virtual void AddContributorsWithBaseBundles(List contributors, + BundleConfigurationCollection bundles, string bundleName) + { + var bundleConfiguration = bundles.Get(bundleName); + + foreach (var baseBundleName in bundleConfiguration.BaseBundles) + { + AddContributorsWithBaseBundles(contributors, bundles, baseBundleName); //Recursive call + } + + var selfContributors = bundleConfiguration.Contributors.GetAll(); + + if (selfContributors.Any()) + { + contributors.AddRange(selfContributors); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleResult.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundleResult.cs similarity index 75% rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleResult.cs rename to framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundleResult.cs index eb44dd585e..60ac2c0bc6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleResult.cs +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundleResult.cs @@ -1,4 +1,4 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; +namespace Volo.Abp.AspNetCore.Bundling; public class BundleResult { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundlerBase.cs similarity index 93% rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs rename to framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundlerBase.cs index 4b0efe54ab..b52b9c204f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundlerBase.cs @@ -1,31 +1,27 @@ using System; using System.Text; -using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.DependencyInjection; using Volo.Abp.Minify; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; +namespace Volo.Abp.AspNetCore.Bundling; public abstract class BundlerBase : IBundler, ITransientDependency { private static string[] _minFileSuffixes = { "min", "prod" }; public ILogger Logger { get; set; } - - protected IWebHostEnvironment HostEnvironment { get; } protected IMinifier Minifier { get; } protected AbpBundlingOptions BundlingOptions { get; } protected BundlerBase( - IWebHostEnvironment hostEnvironment, IMinifier minifier, IOptions bundlingOptions) { - HostEnvironment = hostEnvironment; Minifier = minifier; BundlingOptions = bundlingOptions.Value; @@ -123,7 +119,7 @@ public abstract class BundlerBase : IBundler, ITransientDependency protected virtual IFileInfo GetFileInfo(IBundlerContext context, string file) { - var fileInfo = HostEnvironment.WebRootFileProvider.GetFileInfo(file); + var fileInfo = FindFileInfo(file); if (!fileInfo.Exists) { @@ -150,7 +146,7 @@ public abstract class BundlerBase : IBundler, ITransientDependency { foreach (var suffix in _minFileSuffixes) { - var fileInfo = HostEnvironment.WebRootFileProvider.GetFileInfo( + var fileInfo = FindFileInfo( $"{file.RemovePostFix($".{FileExtension}")}.{suffix}.{FileExtension}" ); @@ -163,6 +159,8 @@ public abstract class BundlerBase : IBundler, ITransientDependency return null; } + protected abstract IFileInfo FindFileInfo(string file); + protected virtual string ProcessBeforeAddingToTheBundle(IBundlerContext context, string filePath, string fileContent) { return fileContent; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerContext.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundlerContext.cs similarity index 91% rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerContext.cs rename to framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundlerContext.cs index 9c9c60a9eb..e39fa90880 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerContext.cs +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/BundlerContext.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; +namespace Volo.Abp.AspNetCore.Bundling; public class BundlerContext : IBundlerContext { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleCache.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/IBundleCache.cs similarity index 78% rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleCache.cs rename to framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/IBundleCache.cs index 9c2f19d4ca..6f80d23a60 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleCache.cs +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/IBundleCache.cs @@ -1,6 +1,6 @@ using System; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; +namespace Volo.Abp.AspNetCore.Bundling; public interface IBundleCache { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleManager.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/IBundleManager.cs similarity index 76% rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleManager.cs rename to framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/IBundleManager.cs index 067554e102..3c3a50ece1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/IBundleManager.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; using System.Threading.Tasks; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; +namespace Volo.Abp.AspNetCore.Bundling; public interface IBundleManager { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundler.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/IBundler.cs similarity index 58% rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundler.cs rename to framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/IBundler.cs index 9f396767ee..68e50f4ec7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundler.cs +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/IBundler.cs @@ -1,4 +1,6 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +namespace Volo.Abp.AspNetCore.Bundling; public interface IBundler { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundlerContext.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/IBundlerContext.cs similarity index 81% rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundlerContext.cs rename to framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/IBundlerContext.cs index d59bc154b5..83d49df220 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundlerContext.cs +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/IBundlerContext.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; +namespace Volo.Abp.AspNetCore.Bundling; public interface IBundlerContext { diff --git a/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/Scripts/IScriptBundler.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/Scripts/IScriptBundler.cs new file mode 100644 index 0000000000..2af5f655a7 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/Scripts/IScriptBundler.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.AspNetCore.Bundling.Scripts; + +public interface IScriptBundler : IBundler +{ + +} diff --git a/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/Styles/IStyleBundler.cs b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/Styles/IStyleBundler.cs new file mode 100644 index 0000000000..c8fe1cbf2a --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Bundling/Volo/Abp/AspNetCore/Bundling/Styles/IStyleBundler.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.AspNetCore.Bundling.Styles; + +public interface IStyleBundler : IBundler +{ + +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/FodyWeavers.xml b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/FodyWeavers.xml new file mode 100644 index 0000000000..00e1d9a1c1 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/FodyWeavers.xsd b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling.csproj b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling.csproj new file mode 100644 index 0000000000..4b278e61c2 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling.csproj @@ -0,0 +1,29 @@ + + + + + + + + net9.0 + enable + Nullable + Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling + Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/AbpAspNetCoreComponentsMauiBlazorBundlingModule.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/AbpAspNetCoreComponentsMauiBlazorBundlingModule.cs new file mode 100644 index 0000000000..07900b0fb0 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/AbpAspNetCoreComponentsMauiBlazorBundlingModule.cs @@ -0,0 +1,110 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.Bundling.Styles; +using Volo.Abp.Modularity; +using Volo.Abp.Threading; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling; + +[DependsOn( + typeof(AbpAspNetCoreComponentsMauiBlazorModule), + typeof(AbpAspNetCoreBundlingModule) +)] +public class AbpAspNetCoreComponentsMauiBlazorBundlingModule : AbpModule +{ + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context)); + } + + public async override Task OnApplicationInitializationAsync(ApplicationInitializationContext context) + { + await InitialGlobalAssetsAsync(context); + } + + protected virtual async Task InitialGlobalAssetsAsync(ApplicationInitializationContext context) + { + var bundlingOptions = context.ServiceProvider.GetRequiredService>().Value; + var logger = context.ServiceProvider.GetRequiredService>(); + if (!bundlingOptions.GlobalAssets.Enabled) + { + return; + } + + var bundleManager = context.ServiceProvider.GetRequiredService(); + var mauiBlazorContentFileProvider = context.ServiceProvider.GetRequiredService(); + var dynamicFileProvider = context.ServiceProvider.GetRequiredService(); + if (!bundlingOptions.GlobalAssets.GlobalStyleBundleName.IsNullOrWhiteSpace()) + { + var styleFiles = await bundleManager.GetStyleBundleFilesAsync(bundlingOptions.GlobalAssets.GlobalStyleBundleName); + var styles = string.Empty; + foreach (var file in styleFiles) + { + var fileInfo = mauiBlazorContentFileProvider.GetFileInfo(file.FileName); + if (!fileInfo.Exists) + { + logger.LogError($"Could not find the file: {file.FileName}"); + continue; + } + + var fileContent = await fileInfo.ReadAsStringAsync(); + if (!bundleManager.IsBundlingEnabled()) + { + fileContent = CssRelativePath.Adjust(fileContent, + file.FileName, + Path.Combine(Directory.GetCurrentDirectory(), "wwwroot")); + + styles += $"/*{file.FileName}*/{Environment.NewLine}{fileContent}{Environment.NewLine}{Environment.NewLine}"; + } + else + { + styles += $"{fileContent}{Environment.NewLine}{Environment.NewLine}"; + } + } + + dynamicFileProvider.AddOrUpdate( + new InMemoryFileInfo("/wwwroot/" + bundlingOptions.GlobalAssets.CssFileName, + Encoding.UTF8.GetBytes(styles), + bundlingOptions.GlobalAssets.CssFileName)); + } + + if (!bundlingOptions.GlobalAssets.GlobalScriptBundleName.IsNullOrWhiteSpace()) + { + var scriptFiles = await bundleManager.GetScriptBundleFilesAsync(bundlingOptions.GlobalAssets.GlobalScriptBundleName); + var scripts = string.Empty; + foreach (var file in scriptFiles) + { + var fileInfo = mauiBlazorContentFileProvider.GetFileInfo(file.FileName); + if (!fileInfo.Exists) + { + logger.LogError($"Could not find the file: {file.FileName}"); + continue; + } + + var fileContent = await fileInfo.ReadAsStringAsync(); + if (!bundleManager.IsBundlingEnabled()) + { + scripts += $"{fileContent.EnsureEndsWith(';')}{Environment.NewLine}{Environment.NewLine}"; + } + else + { + scripts += $"//{file.FileName}{Environment.NewLine}{fileContent.EnsureEndsWith(';')}{Environment.NewLine}{Environment.NewLine}"; + } + } + + dynamicFileProvider.AddOrUpdate( + new InMemoryFileInfo("/wwwroot/" + bundlingOptions.GlobalAssets.JavaScriptFileName, + Encoding.UTF8.GetBytes(scripts), + bundlingOptions.GlobalAssets.JavaScriptFileName)); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/AbpBlazorWebView.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/AbpBlazorWebView.cs new file mode 100644 index 0000000000..bbb5829162 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/AbpBlazorWebView.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Components.WebView.Maui; +using Microsoft.Extensions.FileProviders; +using Microsoft.Maui; + +namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling; + +public class AbpBlazorWebView : BlazorWebView +{ + public override IFileProvider CreateFileProvider(string contentRootDir) + { + return new CompositeFileProvider(Handler!.GetRequiredService(), base.CreateFileProvider(contentRootDir)); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/BundleManager.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/BundleManager.cs new file mode 100644 index 0000000000..cbcde93e11 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/BundleManager.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.Maui.Storage; +using Volo.Abp.AspNetCore.Bundling; +using Volo.Abp.AspNetCore.Bundling.Scripts; +using Volo.Abp.AspNetCore.Bundling.Styles; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.DependencyInjection; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling; + +public class BundleManager : BundleManagerBase, ITransientDependency +{ + protected IMauiBlazorContentFileProvider MauiBlazorContentFileProvider { get; } + + public BundleManager( + IOptions options, + IOptions contributorOptions, + IScriptBundler scriptBundler, + IStyleBundler styleBundler, + IServiceProvider serviceProvider, + IDynamicFileProvider dynamicFileProvider, + IBundleCache bundleCache, + IMauiBlazorContentFileProvider mauiBlazorContentFileProvider) : base( + options, + contributorOptions, + scriptBundler, + styleBundler, + serviceProvider, + dynamicFileProvider, + bundleCache) + { + MauiBlazorContentFileProvider = mauiBlazorContentFileProvider; + } + + public override bool IsBundlingEnabled() + { + switch (Options.Mode) + { + case BundlingMode.None: + return false; + case BundlingMode.Bundle: + case BundlingMode.BundleAndMinify: + return true; + case BundlingMode.Auto: + return !IsDebug(); + default: + throw new AbpException($"Unhandled {nameof(BundlingMode)}: {Options.Mode}"); + } + } + + protected async override Task> GetBundleFilesAsync(List contributors) + { + var files = await base.GetBundleFilesAsync(contributors); + + foreach (var file in files) + { + await CopyFileToAppDataDirectoryAsync(file); + } + + return files; + } + + protected virtual async Task CopyFileToAppDataDirectoryAsync(BundleFile file) + { + if (file.IsExternalFile) + { + return; + } + + var fileName = Path.Combine("wwwroot", file.FileName); + if(MauiBlazorContentFileProvider.GetFileInfo(fileName).Exists) + { + return; + } + + try + { + await using var inputStream = await FileSystem.Current.OpenAppPackageFileAsync(fileName); + var targetFile = Path.Combine(FileSystem.Current.AppDataDirectory, fileName); + var fileDirectory = Path.GetDirectoryName(targetFile)!; + if (!Path.Exists(fileDirectory)) + { + Directory.CreateDirectory(fileDirectory); + } + await using var outputStream = File.Create(targetFile); + await inputStream.CopyToAsync(outputStream); + } + catch (Exception e) + { + Logger.LogError($"Could not copy the file to the app data directory: {fileName}", e); + } + } + + protected override bool IsMinficationEnabled() + { + switch (Options.Mode) + { + case BundlingMode.None: + case BundlingMode.Bundle: + return false; + case BundlingMode.BundleAndMinify: + return true; + case BundlingMode.Auto: + return !IsDebug(); + default: + throw new AbpException($"Unhandled {nameof(BundlingMode)}: {Options.Mode}"); + } + } + + protected virtual bool IsDebug() + { + #if DEBUG + return true; + #else + return false; + #endif + } + + protected override IFileProvider GetFileProvider() + { + return MauiBlazorContentFileProvider; + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/IMauiBlazorContentFileProvide.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/IMauiBlazorContentFileProvide.cs new file mode 100644 index 0000000000..4c7a50a339 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/IMauiBlazorContentFileProvide.cs @@ -0,0 +1,8 @@ +using Microsoft.Extensions.FileProviders; + +namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling; + +public interface IMauiBlazorContentFileProvider : IFileProvider +{ + string ContentRootPath { get; } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/MauiBlazorBundlerBase.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/MauiBlazorBundlerBase.cs new file mode 100644 index 0000000000..b02c80435a --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/MauiBlazorBundlerBase.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.Minify; + +namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling; + +public abstract class MauiBlazorBundlerBase : BundlerBase +{ + protected IMauiBlazorContentFileProvider MauiBlazorContentFileProvider { get; } + + public MauiBlazorBundlerBase( + IMauiBlazorContentFileProvider mauiBlazorContentFileProvider, + IMinifier minifier, + IOptions bundlingOptions) : base(minifier, + bundlingOptions) + { + MauiBlazorContentFileProvider = mauiBlazorContentFileProvider; + } + + protected override IFileInfo FindFileInfo(string file) + { + return MauiBlazorContentFileProvider.GetFileInfo(file); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/MauiBlazorContentFileProvider.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/MauiBlazorContentFileProvider.cs new file mode 100644 index 0000000000..cc7bbf570e --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/MauiBlazorContentFileProvider.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Primitives; +using Microsoft.Maui.Controls.PlatformConfiguration; +using Microsoft.Maui.Storage; +using Volo.Abp.DependencyInjection; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling; + +public class MauiBlazorContentFileProvider : IMauiBlazorContentFileProvider, ISingletonDependency +{ + private readonly IVirtualFileProvider _virtualFileProvider; + private readonly IFileProvider _fileProvider; + private string _rootPath = "/wwwroot"; + + public MauiBlazorContentFileProvider(IVirtualFileProvider virtualFileProvider) + { + _virtualFileProvider = virtualFileProvider; + _fileProvider = CreateFileProvider(); + } + + public string ContentRootPath => FileSystem.Current.AppDataDirectory; + + public IFileInfo GetFileInfo(string subpath) + { + if (string.IsNullOrEmpty(subpath)) + { + return new NotFoundFileInfo(subpath); + } + + var fileInfo = _fileProvider.GetFileInfo(subpath); + return fileInfo.Exists ? fileInfo : _fileProvider.GetFileInfo( _rootPath + subpath.EnsureStartsWith('/')); + } + + public IDirectoryContents GetDirectoryContents(string subpath) + { + if (string.IsNullOrEmpty(subpath)) + { + return NotFoundDirectoryContents.Singleton; + } + + var directory = _fileProvider.GetDirectoryContents(subpath); + return directory.Exists ? directory : _fileProvider.GetDirectoryContents( _rootPath + subpath.EnsureStartsWith('/')); + } + + public IChangeToken Watch(string filter) + { + return new CompositeChangeToken( + [ + _fileProvider.Watch(_rootPath + filter), + _fileProvider.Watch(filter) + ] + ); + } + + protected virtual IFileProvider CreateFileProvider() + { + var assetsDirectory = Path.Combine(ContentRootPath, _rootPath.TrimStart('/')); + if (!Path.Exists(assetsDirectory)) + { + Directory.CreateDirectory(assetsDirectory); + } + + return new CompositeFileProvider(new PhysicalFileProvider(assetsDirectory), _virtualFileProvider); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/Scripts/ScriptBundler.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/Scripts/ScriptBundler.cs new file mode 100644 index 0000000000..796cbead8a --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/Scripts/ScriptBundler.cs @@ -0,0 +1,29 @@ +using System; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Bundling; +using Volo.Abp.AspNetCore.Bundling.Scripts; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.Minify.Scripts; + +namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling.Scripts; + +public class ScriptBundler : MauiBlazorBundlerBase, IScriptBundler +{ + public override string FileExtension => "js"; + + public ScriptBundler( + IMauiBlazorContentFileProvider mauiBlazorContentFileProvider, + IJavascriptMinifier minifier, + IOptions bundlingOptions) + : base( + mauiBlazorContentFileProvider, + minifier, + bundlingOptions) + { + } + + protected override string ProcessBeforeAddingToTheBundle(IBundlerContext context, string filePath, string fileContent) + { + return fileContent.EnsureEndsWith(';') + Environment.NewLine; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/Styles/StyleBundler.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/Styles/StyleBundler.cs new file mode 100644 index 0000000000..fae44b7a23 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling/Volo/Abp/AspNetCore/Components/MauiBlazor/Bundling/Styles/StyleBundler.cs @@ -0,0 +1,42 @@ +using System; +using System.IO; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Bundling; +using Volo.Abp.AspNetCore.Bundling.Styles; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.Bundling.Styles; +using Volo.Abp.Minify.Styles; + +namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling.Styles; + +public class StyleBundler : MauiBlazorBundlerBase, IStyleBundler +{ + private readonly IMauiBlazorContentFileProvider _mauiBlazorContentFileProvider; + public override string FileExtension => "css"; + + public StyleBundler( + IMauiBlazorContentFileProvider mauiBlazorContentFileProvider, + ICssMinifier minifier, + IOptions bundlingOptions) + : base( + mauiBlazorContentFileProvider, + minifier, + bundlingOptions) + { + _mauiBlazorContentFileProvider = mauiBlazorContentFileProvider; + } + + public string GetAbsolutePath(string relativePath) + { + return Path.Combine(_mauiBlazorContentFileProvider.ContentRootPath, "wwwroot", relativePath.RemovePreFix("/")).Replace("file://", ""); + } + + protected override string ProcessBeforeAddingToTheBundle(IBundlerContext context, string filePath, string fileContent) + { + return CssRelativePath.Adjust( + fileContent, + GetAbsolutePath(filePath), + GetAbsolutePath(context.BundleRelativePath) + ); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/AbpMauiBlazorClientHttpMessageHandler.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/AbpMauiBlazorClientHttpMessageHandler.cs index 9fb31979ac..562f35e5a8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/AbpMauiBlazorClientHttpMessageHandler.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/AbpMauiBlazorClientHttpMessageHandler.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Components.Progression; using Volo.Abp.DependencyInjection; +using Volo.Abp.Timing; namespace Volo.Abp.AspNetCore.Components.MauiBlazor; @@ -13,12 +14,15 @@ public class AbpMauiBlazorClientHttpMessageHandler : DelegatingHandler, ITransie { private readonly IUiPageProgressService _uiPageProgressService; private readonly IMauiBlazorSelectedLanguageProvider _mauiBlazorSelectedLanguageProvider; + private readonly ICurrentTimezoneProvider _currentTimezoneProvider; public AbpMauiBlazorClientHttpMessageHandler( IClientScopeServiceProviderAccessor clientScopeServiceProviderAccessor, - IMauiBlazorSelectedLanguageProvider mauiBlazorSelectedLanguageProvider) + IMauiBlazorSelectedLanguageProvider mauiBlazorSelectedLanguageProvider, + ICurrentTimezoneProvider currentTimezoneProvider) { _mauiBlazorSelectedLanguageProvider = mauiBlazorSelectedLanguageProvider; + _currentTimezoneProvider = currentTimezoneProvider; _uiPageProgressService = clientScopeServiceProviderAccessor.ServiceProvider.GetRequiredService(); } @@ -32,6 +36,7 @@ public class AbpMauiBlazorClientHttpMessageHandler : DelegatingHandler, ITransie }); await SetLanguageAsync(request); + await SetTimeZoneAsync(request); return await base.SendAsync(request, cancellationToken); } @@ -51,4 +56,15 @@ public class AbpMauiBlazorClientHttpMessageHandler : DelegatingHandler, ITransie request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(selectedLanguage!)); } } -} \ No newline at end of file + + private Task SetTimeZoneAsync(HttpRequestMessage request) + { + if (!_currentTimezoneProvider.TimeZone.IsNullOrWhiteSpace()) + { + request.Headers.Remove(TimeZoneConsts.DefaultTimeZoneKey); + request.Headers.Add(TimeZoneConsts.DefaultTimeZoneKey, _currentTimezoneProvider.TimeZone); + } + + return Task.CompletedTask; + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCachedApplicationConfigurationClient.cs index c2fd8e21aa..a48901f6dd 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCachedApplicationConfigurationClient.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCachedApplicationConfigurationClient.cs @@ -1,10 +1,13 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.JSInterop; using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies; using Volo.Abp.AspNetCore.Mvc.Client; using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; +using Volo.Abp.Timing; namespace Volo.Abp.AspNetCore.Components.MauiBlazor { @@ -18,17 +21,29 @@ namespace Volo.Abp.AspNetCore.Components.MauiBlazor protected ICurrentTenantAccessor CurrentTenantAccessor { get; } + protected ICurrentTimezoneProvider CurrentTimezoneProvider { get; } + + protected IJSRuntime JSRuntime { get; } + + protected IClock Clock { get; } + public MauiBlazorCachedApplicationConfigurationClient( AbpApplicationConfigurationClientProxy applicationConfigurationClientProxy, ApplicationConfigurationCache cache, ICurrentTenantAccessor currentTenantAccessor, + ICurrentTimezoneProvider currentTimezoneProvider, AuthenticationStateProvider authenticationStateProvider, - AbpApplicationLocalizationClientProxy applicationLocalizationClientProxy) + AbpApplicationLocalizationClientProxy applicationLocalizationClientProxy, + IJSRuntime jsRuntime, + IClock clock) { ApplicationConfigurationClientProxy = applicationConfigurationClientProxy; Cache = cache; CurrentTenantAccessor = currentTenantAccessor; + CurrentTimezoneProvider = currentTimezoneProvider; ApplicationLocalizationClientProxy = applicationLocalizationClientProxy; + JSRuntime = jsRuntime; + Clock = clock; authenticationStateProvider.AuthenticationStateChanged += async _ => { await InitializeAsync(); }; } @@ -57,6 +72,15 @@ namespace Volo.Abp.AspNetCore.Components.MauiBlazor CurrentTenantAccessor.Current = new BasicTenantInfo( configurationDto.CurrentTenant.Id, configurationDto.CurrentTenant.Name); + + if (Clock.SupportsMultipleTimezone) + { + CurrentTimezoneProvider.TimeZone = !configurationDto.Timing.TimeZone.Iana.TimeZoneName.IsNullOrWhiteSpace() + ? configurationDto.Timing.TimeZone.Iana.TimeZoneName + : await JSRuntime.InvokeAsync("abp.clock.getBrowserTimeZone"); + + await JSRuntime.InvokeAsync("abp.clock.setBrowserTimeZoneToCookie"); + } } public virtual Task GetAsync() @@ -81,4 +105,4 @@ namespace Volo.Abp.AspNetCore.Components.MauiBlazor return configuration; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCurrentTimezoneProvider.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCurrentTimezoneProvider.cs new file mode 100644 index 0000000000..2a1a37da07 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCurrentTimezoneProvider.cs @@ -0,0 +1,10 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.Timing; + +namespace Volo.Abp.AspNetCore.Components.MauiBlazor; + +[Dependency(ReplaceServices = true)] +public class MauiBlazorCurrentTimezoneProvider : ICurrentTimezoneProvider, ISingletonDependency +{ + public string? TimeZone { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorServerComponentBundleManager.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorServerComponentBundleManager.cs index 9ba0baff2d..98ca1b54a4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorServerComponentBundleManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorServerComponentBundleManager.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Volo.Abp.AspNetCore.Bundling; using Volo.Abp.AspNetCore.Components.Web.Theming.Bundling; -using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.DependencyInjection; namespace Volo.Abp.AspNetCore.Components.Server.Theming.Bundling; diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/AbpScripts.razor b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/AbpScripts.razor index 8c9d070764..436a22fc0e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/AbpScripts.razor +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/AbpScripts.razor @@ -1,33 +1,61 @@ +@implements IDisposable @inject IComponentBundleManager BundleManager +@inject PersistentComponentState ApplicationState @if (ScriptFiles != null) { foreach (var file in ScriptFiles) { - + var src = file; + if (!AppBasePath.IsNullOrWhiteSpace()) + { + src = AppBasePath.EnsureEndsWith('/') + file.RemovePreFix("/"); + } + } } @code { + + private const string PrerenderedKey = "abp_script_prerendered"; + [Parameter] public List? WebAssemblyScriptFiles { get; set; } [Parameter] public string? BundleName { get; set; } + [Parameter] + public string? AppBasePath { get; set; } + private List? ScriptFiles { get; set; } + private PersistingComponentStateSubscription _persistingSubscription; + protected override async Task OnInitializedAsync() { - ScriptFiles = new List(); - - if (!BundleName.IsNullOrWhiteSpace()) + _persistingSubscription = ApplicationState.RegisterOnPersisting(Callback); + if (!ApplicationState.TryTakeFromJson(PrerenderedKey, out _)) { - ScriptFiles = (await BundleManager.GetScriptBundleFilesAsync(BundleName!)).ToList(); + // We are in prerendering mode + if (!BundleName.IsNullOrWhiteSpace()) + { + ScriptFiles = (await BundleManager.GetScriptBundleFilesAsync(BundleName!)).ToList(); + } } - - if (OperatingSystem.IsBrowser() && WebAssemblyScriptFiles != null) + else { - ScriptFiles.AddIfNotContains(WebAssemblyScriptFiles); + if (OperatingSystem.IsBrowser() && WebAssemblyScriptFiles != null) + { + ScriptFiles = WebAssemblyScriptFiles; + } } } + + private Task Callback() + { + ApplicationState.PersistAsJson(PrerenderedKey, PrerenderedKey); + return Task.CompletedTask; + } + + public void Dispose() => _persistingSubscription.Dispose(); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/AbpStyles.razor b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/AbpStyles.razor index bdcfd26bec..1c2ede488f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/AbpStyles.razor +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/AbpStyles.razor @@ -1,33 +1,75 @@ +@implements IDisposable @inject IComponentBundleManager BundleManager +@inject PersistentComponentState ApplicationState @if (StyleFiles != null) { foreach (var file in StyleFiles) { - + var href = file; + if (!AppBasePath.IsNullOrWhiteSpace()) + { + href = AppBasePath.EnsureEndsWith('/') + file.RemovePreFix("/"); + } + } } @code { + + private const string PrerenderedKey = "abp_style_prerendered"; + [Parameter] public List? WebAssemblyStyleFiles { get; set; } [Parameter] public string? BundleName { get; set; } + [Parameter] + public string? AppBasePath { get; set; } + private List? StyleFiles { get; set; } + private PersistingComponentStateSubscription _persistingSubscription; + protected override async Task OnInitializedAsync() { - StyleFiles = new List(); - - if (!BundleName.IsNullOrWhiteSpace()) + _persistingSubscription = ApplicationState.RegisterOnPersisting(Callback); + if (!ApplicationState.TryTakeFromJson>(PrerenderedKey, out var scriptFiles)) { - StyleFiles = (await BundleManager.GetStyleBundleFilesAsync(BundleName!)).ToList(); + // We are in prerendering mode + if (!BundleName.IsNullOrWhiteSpace()) + { + StyleFiles = (await BundleManager.GetStyleBundleFilesAsync(BundleName!)).ToList(); + } } + else + { + StyleFiles = scriptFiles; + if (OperatingSystem.IsBrowser() && StyleFiles != null && WebAssemblyStyleFiles != null) + { + StyleFiles.AddIfNotContains(WebAssemblyStyleFiles); + } + } + } - if (OperatingSystem.IsBrowser() && WebAssemblyStyleFiles != null) + private bool _hasRemoveServerStyle = false; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (!_hasRemoveServerStyle && OperatingSystem.IsBrowser() && WebAssemblyStyleFiles != null) { - StyleFiles.AddIfNotContains(WebAssemblyStyleFiles); + _hasRemoveServerStyle = true; + await Task.Delay(3000); + StyleFiles = WebAssemblyStyleFiles; + StateHasChanged(); } } + + private Task Callback() + { + ApplicationState.PersistAsJson(PrerenderedKey, StyleFiles); + return Task.CompletedTask; + } + + public void Dispose() => _persistingSubscription.Dispose(); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageHeader.razor.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageHeader.razor.cs index 30a69f8d74..74481c9bbd 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageHeader.razor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageHeader.razor.cs @@ -111,8 +111,7 @@ public partial class PageHeader : ComponentBase, IDisposable public void Dispose() { - PageLayout.ToolbarItems.Clear(); - PageLayout.BreadcrumbItems.Clear(); + PageLayout.Reset(); ToolbarItemRenders.Clear(); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageLayout.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageLayout.cs index 077ca5b32c..fc7d372b37 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageLayout.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageLayout.cs @@ -41,4 +41,12 @@ public class PageLayout : IScopedDependency, INotifyPropertyChanged { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + + public void Reset() + { + Title = string.Empty; + MenuItemName = string.Empty; + BreadcrumbItems.Clear(); + ToolbarItems.Clear(); + } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Theming/DefaultThemeManager.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Theming/DefaultThemeManager.cs index 5abd114f83..76c7bbf556 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Theming/DefaultThemeManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Theming/DefaultThemeManager.cs @@ -29,6 +29,6 @@ public class DefaultThemeManager : IThemeManager, IScopedDependency, IServicePro } _currentTheme = (ITheme)ServiceProvider.GetRequiredService(ThemeSelector.GetCurrentThemeInfo().ThemeType); - return CurrentTheme; + return _currentTheme; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpBlazorClientHttpMessageHandler.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpBlazorClientHttpMessageHandler.cs index c92c121628..5fa70baabe 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpBlazorClientHttpMessageHandler.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpBlazorClientHttpMessageHandler.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.JSInterop; using Volo.Abp.AspNetCore.Components.Progression; using Volo.Abp.DependencyInjection; +using Volo.Abp.Timing; namespace Volo.Abp.AspNetCore.Components.Web; @@ -21,6 +22,8 @@ public class AbpBlazorClientHttpMessageHandler : DelegatingHandler, ITransientDe private readonly IUiPageProgressService _uiPageProgressService; + private readonly ICurrentTimezoneProvider _currentTimezoneProvider; + private const string AntiForgeryCookieName = "XSRF-TOKEN"; private const string AntiForgeryHeaderName = "RequestVerificationToken"; @@ -29,11 +32,13 @@ public class AbpBlazorClientHttpMessageHandler : DelegatingHandler, ITransientDe IJSRuntime jsRuntime, ICookieService cookieService, NavigationManager navigationManager, - IClientScopeServiceProviderAccessor clientScopeServiceProviderAccessor) + IClientScopeServiceProviderAccessor clientScopeServiceProviderAccessor, + ICurrentTimezoneProvider currentTimezoneProvider) { _jsRuntime = jsRuntime; _cookieService = cookieService; _navigationManager = navigationManager; + _currentTimezoneProvider = currentTimezoneProvider; _uiPageProgressService = clientScopeServiceProviderAccessor.ServiceProvider.GetRequiredService(); } @@ -48,6 +53,7 @@ public class AbpBlazorClientHttpMessageHandler : DelegatingHandler, ITransientDe await SetLanguageAsync(request, cancellationToken); await SetAntiForgeryTokenAsync(request); + await SetTimeZoneAsync(request); return await base.SendAsync(request, cancellationToken); } @@ -94,4 +100,15 @@ public class AbpBlazorClientHttpMessageHandler : DelegatingHandler, ITransientDe request.Headers.Add(AntiForgeryHeaderName, token); } } + + private Task SetTimeZoneAsync(HttpRequestMessage request) + { + if (!_currentTimezoneProvider.TimeZone.IsNullOrWhiteSpace()) + { + request.Headers.Remove(TimeZoneConsts.DefaultTimeZoneKey); + request.Headers.Add(TimeZoneConsts.DefaultTimeZoneKey, _currentTimezoneProvider.TimeZone); + } + + return Task.CompletedTask; + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/UserExceptionInformer.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/UserExceptionInformer.cs index a8b0f71dea..a73ba0e77d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/UserExceptionInformer.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ExceptionHandling/UserExceptionInformer.cs @@ -8,6 +8,7 @@ using Volo.Abp.AspNetCore.Components.Messages; using Volo.Abp.AspNetCore.ExceptionHandling; using Volo.Abp.DependencyInjection; using Volo.Abp.Http; +using Volo.Abp.Http.Client; namespace Volo.Abp.AspNetCore.Components.Web.ExceptionHandling; @@ -35,6 +36,8 @@ public class UserExceptionInformer : IUserExceptionInformer, IScopedDependency { //TODO: Create sync versions of the MessageService APIs. + LogException(context); + var errorInfo = GetErrorInfo(context); if (errorInfo.Details.IsNullOrEmpty()) @@ -49,6 +52,8 @@ public class UserExceptionInformer : IUserExceptionInformer, IScopedDependency public async Task InformAsync(UserExceptionInformerContext context) { + LogException(context); + var errorInfo = GetErrorInfo(context); if (errorInfo.Details.IsNullOrEmpty()) @@ -70,4 +75,14 @@ public class UserExceptionInformer : IUserExceptionInformer, IScopedDependency options.SendExceptionDataToClientTypes = Options.SendExceptionDataToClientTypes; }); } + + protected virtual void LogException(UserExceptionInformerContext context) + { + if (context.Exception is AbpRemoteCallException && OperatingSystem.IsBrowser()) + { + return; + } + + Logger.LogException(context.Exception); + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/wwwroot/libs/abp/js/abp.js b/framework/src/Volo.Abp.AspNetCore.Components.Web/wwwroot/libs/abp/js/abp.js index 51ff0151b6..30f1915005 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/wwwroot/libs/abp/js/abp.js +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/wwwroot/libs/abp/js/abp.js @@ -245,4 +245,14 @@ var abp = abp || {}; } } } + + abp.clock = abp.clock || {}; + + abp.clock.setBrowserTimeZoneToCookie = function () { + abp.utils.setCookieValue('__timezone', Intl.DateTimeFormat().resolvedOptions().timeZone, new Date(new Date().setFullYear(new Date().getFullYear() + 1)), '/'); + } + + abp.clock.getBrowserTimeZone = function () { + return Intl.DateTimeFormat().resolvedOptions().timeZone + } })(); diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpBlazorWebAppServiceCollectionExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpBlazorWebAppServiceCollectionExtensions.cs index 1bcc1c1f45..2027ac1e69 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpBlazorWebAppServiceCollectionExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpBlazorWebAppServiceCollectionExtensions.cs @@ -1,3 +1,4 @@ +using System; using JetBrains.Annotations; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -19,10 +20,14 @@ public static class AbpBlazorWebAppServiceCollectionExtensions return services; } + [Obsolete("Use AddBlazorWebAppServices instead. See https://github.com/abpframework/abp/issues/22622")] public static IServiceCollection AddBlazorWebAppTieredServices([NotNull] this IServiceCollection services) { Check.NotNull(services, nameof(services)); + // Compatibility with old template code + services.AddTransient(); + services.AddScoped(); services.Replace(ServiceDescriptor.Singleton()); diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProvider.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProvider.cs index b35960f071..d3f05820b0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProvider.cs @@ -1,5 +1,7 @@ +using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Security.Claims; namespace Volo.Abp.AspNetCore.Components.WebAssembly.WebApp; @@ -8,21 +10,26 @@ public class RemoteAuthenticationStateProvider : AuthenticationStateProvider { protected ICurrentPrincipalAccessor CurrentPrincipalAccessor { get; } protected WebAssemblyCachedApplicationConfigurationClient WebAssemblyCachedApplicationConfigurationClient { get; } + protected IServiceProvider ServiceProvider { get; } public RemoteAuthenticationStateProvider( ICurrentPrincipalAccessor currentPrincipalAccessor, - WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient) + WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient, + IServiceProvider serviceProvider) { CurrentPrincipalAccessor = currentPrincipalAccessor; WebAssemblyCachedApplicationConfigurationClient = webAssemblyCachedApplicationConfigurationClient; + ServiceProvider = serviceProvider; } public async override Task GetAuthenticationStateAsync() { - if (CurrentPrincipalAccessor.Principal.Identity == null || - !CurrentPrincipalAccessor.Principal.Identity.IsAuthenticated) + if (ServiceProvider.GetService() != null) { - await WebAssemblyCachedApplicationConfigurationClient.InitializeAsync(); + if (CurrentPrincipalAccessor.Principal.Identity == null || !CurrentPrincipalAccessor.Principal.Identity.IsAuthenticated) + { + await WebAssemblyCachedApplicationConfigurationClient.InitializeAsync(); + } } return new AuthenticationState(CurrentPrincipalAccessor.Principal); diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProviderCompatible.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProviderCompatible.cs new file mode 100644 index 0000000000..780f96c631 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebApp/RemoteAuthenticationStateProviderCompatible.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.AspNetCore.Components.WebAssembly.WebApp; + +/// +/// This class is used to indicate that the AddBlazorWebAppTieredServices method has been called for compatibility with the old template code +/// +internal sealed class AddBlazorWebAppTieredServicesHasBeenCalled +{ +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs index bebb2a78e1..acb792d454 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Microsoft.JSInterop; using Volo.Abp.AspNetCore.Components.Web.Security; using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; @@ -6,6 +7,7 @@ using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies; using Volo.Abp.AspNetCore.Mvc.Client; using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; +using Volo.Abp.Timing; namespace Volo.Abp.AspNetCore.Components.WebAssembly; @@ -19,24 +21,32 @@ public class WebAssemblyCachedApplicationConfigurationClient : ICachedApplicatio protected ICurrentTenantAccessor CurrentTenantAccessor { get; } + protected ICurrentTimezoneProvider CurrentTimezoneProvider { get; } + protected ApplicationConfigurationChangedService ApplicationConfigurationChangedService { get; } protected IJSRuntime JSRuntime { get; } + protected IClock Clock { get; } + public WebAssemblyCachedApplicationConfigurationClient( AbpApplicationConfigurationClientProxy applicationConfigurationClientProxy, ApplicationConfigurationCache cache, ICurrentTenantAccessor currentTenantAccessor, + ICurrentTimezoneProvider currentTimezoneProvider, AbpApplicationLocalizationClientProxy applicationLocalizationClientProxy, ApplicationConfigurationChangedService applicationConfigurationChangedService, - IJSRuntime jsRuntime) + IJSRuntime jsRuntime, + IClock clock) { ApplicationConfigurationClientProxy = applicationConfigurationClientProxy; Cache = cache; CurrentTenantAccessor = currentTenantAccessor; + CurrentTimezoneProvider = currentTimezoneProvider; ApplicationLocalizationClientProxy = applicationLocalizationClientProxy; ApplicationConfigurationChangedService = applicationConfigurationChangedService; JSRuntime = jsRuntime; + Clock = clock; } public virtual async Task InitializeAsync() @@ -63,12 +73,21 @@ public class WebAssemblyCachedApplicationConfigurationClient : ICachedApplicatio await JSRuntime.InvokeVoidAsync("abp.utils.removeOidcUser"); } - ApplicationConfigurationChangedService.NotifyChanged(); - CurrentTenantAccessor.Current = new BasicTenantInfo( configurationDto.CurrentTenant.Id, configurationDto.CurrentTenant.Name ); + + if (Clock.SupportsMultipleTimezone) + { + CurrentTimezoneProvider.TimeZone = !configurationDto.Timing.TimeZone.Iana.TimeZoneName.IsNullOrWhiteSpace() + ? configurationDto.Timing.TimeZone.Iana.TimeZoneName + : await JSRuntime.InvokeAsync("abp.clock.getBrowserTimeZone"); + + await JSRuntime.InvokeAsync("abp.clock.setBrowserTimeZoneToCookie"); + } + + ApplicationConfigurationChangedService.NotifyChanged(); } public virtual Task GetAsync() diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentTimezoneProvider.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentTimezoneProvider.cs new file mode 100644 index 0000000000..c387ec30ea --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentTimezoneProvider.cs @@ -0,0 +1,10 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.Timing; + +namespace Volo.Abp.AspNetCore.Components.WebAssembly; + +[Dependency(ReplaceServices = true)] +public class WebAssemblyCurrentTimezoneProvider : ICurrentTimezoneProvider, ISingletonDependency +{ + public string? TimeZone { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs index 6f2df20de8..6b2fe0d98f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs @@ -172,8 +172,7 @@ public abstract class AbpComponentBase : OwningComponentBase { return; } - - Logger.LogException(exception); + await InvokeAsync(async () => { await UserExceptionInformer.InformAsync(new UserExceptionInformerContext(exception)); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpAspNetCoreMvcNewtonsoftModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpAspNetCoreMvcNewtonsoftModule.cs index d213c9f451..95e07a06e0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpAspNetCoreMvcNewtonsoftModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpAspNetCoreMvcNewtonsoftModule.cs @@ -16,7 +16,9 @@ public class AbpAspNetCoreMvcNewtonsoftModule : AbpModule context.Services.AddOptions() .Configure((options, rootServiceProvider) => { - options.SerializerSettings.ContractResolver = new AbpCamelCasePropertyNamesContractResolver(rootServiceProvider.GetRequiredService()); + options.SerializerSettings.ContractResolver = + new AbpCamelCasePropertyNamesContractResolver(rootServiceProvider + .GetRequiredService()); }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs index 62cae94413..af1538bbde 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs @@ -47,11 +47,13 @@ public class AbpInputTagHelper : AbpTagHelper } if (isCheckBox) { - output.Attributes.AddClass("custom-checkbox"); - output.Attributes.AddClass("custom-control"); + if (!TagHelper.UseSwitchCheckBox) + { + output.Attributes.AddClass("custom-checkbox"); + output.Attributes.AddClass("custom-control"); + } + else + { + output.Attributes.AddClass("form-switch"); + } + output.Attributes.AddClass("form-check"); } } @@ -266,7 +274,7 @@ public class AbpInputTagHelperService : AbpTagHelperService } protected virtual async Task GetLabelAsHtmlAsync(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag, bool isCheckbox) - { + { if (IsOutputHidden(inputTag) || TagHelper.SuppressLabel) { return string.Empty; @@ -395,7 +403,7 @@ public class AbpInputTagHelperService : AbpTagHelperService } innerOutput.Content.AppendHtml($" "); } - + innerOutput.Content.AppendHtml(GetRequiredSymbol(context, output)); return innerOutput.Render(_encoder); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs index bd3a4abe48..5088c08293 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs @@ -16,63 +16,28 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; using Volo.Abp.Json; +using Volo.Abp.Timing; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelperService where TTagHelper : AbpDatePickerBaseTagHelper { - protected readonly Dictionary> SupportedInputTypes = new() - { - { - typeof(string), o => - { - if(o is string s && DateTime.TryParse(s, out var dt)) - { - return dt.ToString("O"); - } - - return string.Empty; - } - }, - { - typeof(DateTime), o => - { - if(o is DateTime dt && dt != default) - { - return dt.ToString("O"); - } - - return string.Empty; - } - }, - {typeof(DateTime?), o => ((DateTime?) o)?.ToString("O")!}, - { - typeof(DateTimeOffset), o => - { - if(o is DateTimeOffset dto && dto != default) - { - return dto.ToString("O"); - } - - return string.Empty; - } - }, - {typeof(DateTimeOffset?), o => ((DateTimeOffset?) o)?.ToString("O")!} - }; + protected readonly Dictionary> SupportedInputTypes; protected readonly IJsonSerializer JsonSerializer; protected readonly IHtmlGenerator Generator; protected readonly HtmlEncoder Encoder; protected readonly IServiceProvider ServiceProvider; protected readonly IAbpTagHelperLocalizer TagHelperLocalizer; + protected readonly IClock Clock; protected virtual string TagName { get; set; } = "abp-date-picker"; protected IStringLocalizer L { get; } protected abstract TagHelperOutput TagHelperOutput { get; set; } protected AbpDatePickerBaseTagHelperService(IJsonSerializer jsonSerializer, IHtmlGenerator generator, HtmlEncoder encoder, IServiceProvider serviceProvider, IStringLocalizer l, - IAbpTagHelperLocalizer tagHelperLocalizer) + IAbpTagHelperLocalizer tagHelperLocalizer, IClock clock) { JsonSerializer = jsonSerializer; Generator = generator; @@ -80,6 +45,65 @@ public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelp ServiceProvider = serviceProvider; L = l; TagHelperLocalizer = tagHelperLocalizer; + Clock = clock; + + SupportedInputTypes = new Dictionary> + { + { + typeof(string), x => + { + if(x is string s && DateTime.TryParse(s, out var dt)) + { + return Clock.ConvertToUserTime(dt).ToString("O"); + } + + return string.Empty; + } + }, + { + typeof(DateTime), x => + { + if(x is DateTime dt && dt != default) + { + return Clock.ConvertToUserTime(dt).ToString("O"); + } + + return string.Empty; + } + }, + { + typeof(DateTime?), x => + { + if(x is DateTime dt && dt != default) + { + return Clock.ConvertToUserTime(dt).ToString("O"); + } + return string.Empty; + } + }, + { + typeof(DateTimeOffset), x => + { + if(x is DateTimeOffset dto && dto != default) + { + return Clock.ConvertToUserTime(dto).DateTime.ToString("O"); + } + + return string.Empty; + } + }, + { + typeof(DateTimeOffset?), x => + { + if(x is DateTimeOffset dto && dto != default) + { + return Clock.ConvertToUserTime(dto).DateTime.ToString("O"); + } + + return string.Empty; + } + } + }; } protected virtual T? GetAttribute() where T : Attribute @@ -233,6 +257,7 @@ public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelp } protected abstract int GetOrder(); + protected abstract void AddBaseTagAttributes(TagHelperAttributeList attributes); protected virtual string GetExtraInputHtml(TagHelperContext context, TagHelperOutput output) @@ -375,7 +400,7 @@ public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelp { attrList.Add("data-visible-date-format", options.VisibleDateFormat); } - + if(!options.InputDateFormat.IsNullOrEmpty()) { attrList.Add("data-input-date-format", options.InputDateFormat); @@ -754,7 +779,7 @@ public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelp { return Task.FromResult(string.Empty); } - + return GetValidationAsHtmlByInputAsync(context, output, @for); } @@ -766,7 +791,7 @@ public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelp new ValidationMessageTagHelper(Generator) { For = @for, ViewContext = TagHelper.ViewContext }; var attributeList = new TagHelperAttributeList { { "class", "text-danger" } }; - + if(!output.Attributes.TryGetAttribute("name", out var nameAttribute) || nameAttribute == null || nameAttribute.Value == null) { if (nameAttribute != null) @@ -776,7 +801,7 @@ public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelp nameAttribute = new TagHelperAttribute("name", "date_" + Guid.NewGuid().ToString("N")); output.Attributes.Add(nameAttribute); } - + attributeList.Add("data-valmsg-for", nameAttribute.Value); return await validationMessageTagHelper.RenderAsync(attributeList, context, Encoder, "span", diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelperService.cs index 3fc2861b59..5370a9a7de 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelperService.cs @@ -8,14 +8,22 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.Localization; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; using Volo.Abp.Json; +using Volo.Abp.Timing; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker; public class AbpDatePickerTagHelperService : AbpDatePickerBaseTagHelperService { - public AbpDatePickerTagHelperService(IJsonSerializer jsonSerializer, IHtmlGenerator generator, HtmlEncoder encoder, IServiceProvider serviceProvider, IStringLocalizer l, IAbpTagHelperLocalizer tagHelperLocalizer) : base(jsonSerializer, generator, encoder, serviceProvider, l, tagHelperLocalizer) + public AbpDatePickerTagHelperService( + IJsonSerializer jsonSerializer, + IHtmlGenerator generator, + HtmlEncoder encoder, + IServiceProvider serviceProvider, + IStringLocalizer l, + IAbpTagHelperLocalizer tagHelperLocalizer, + IClock clock) + : base(jsonSerializer, generator, encoder, serviceProvider, l, tagHelperLocalizer, clock) { - } protected override TagHelperOutput TagHelperOutput { get; set; } = default!; @@ -42,10 +50,28 @@ public class AbpDatePickerTagHelperService : AbpDatePickerBaseTagHelperService { - public AbpDateRangePickerTagHelperService(IJsonSerializer jsonSerializer, IHtmlGenerator generator, - HtmlEncoder encoder, IServiceProvider serviceProvider, IStringLocalizer l, - IAbpTagHelperLocalizer tagHelperLocalizer) : - base(jsonSerializer, generator, encoder, serviceProvider, l, - tagHelperLocalizer) + public AbpDateRangePickerTagHelperService( + IJsonSerializer jsonSerializer, IHtmlGenerator generator, + HtmlEncoder encoder, + IServiceProvider serviceProvider, + IStringLocalizer l, + IAbpTagHelperLocalizer tagHelperLocalizer, + IClock clock) : + base(jsonSerializer, generator, encoder, serviceProvider, l, tagHelperLocalizer, clock) { } @@ -34,7 +38,7 @@ public class AbpDateRangePickerTagHelperService : AbpDatePickerBaseTagHelperServ { if (TagHelper.AspForStart != null) { - var startDateAttributes = new TagHelperAttributeList { { "data-start-date", "true" }, { "type", "hidden" } }; + var startDateAttributes = new TagHelperAttributeList { { "data-hidden-datepicker", "true" }, { "data-start-date", "true" }, { "type", "hidden" } }; StartDateTagHelper = new InputTagHelper(Generator) { ViewContext = TagHelper.ViewContext, @@ -42,12 +46,29 @@ public class AbpDateRangePickerTagHelperService : AbpDatePickerBaseTagHelperServ InputTypeName = "hidden" }; + if (Clock.SupportsMultipleTimezone) + { + if (TagHelper.AspForStart.Model is DateTime dateTime) + { + StartDateTagHelper.Format = "{0:O}"; + StartDateTagHelper.Value = Clock.ConvertToUserTime(dateTime).ToString("O"); + startDateAttributes.Add("value", StartDateTagHelper.Value); + } + + if (TagHelper.AspForStart.Model is DateTimeOffset dateTimeOffset) + { + StartDateTagHelper.Format = "{0:O}"; + StartDateTagHelper.Value = Clock.ConvertToUserTime(dateTimeOffset).UtcDateTime.ToString("O"); + startDateAttributes.Add("value", StartDateTagHelper.Value); + } + } + StartDateTagHelperOutput = await StartDateTagHelper.ProcessAndGetOutputAsync(startDateAttributes, context, "input"); } if (TagHelper.AspForEnd != null) { - var endDateAttributes = new TagHelperAttributeList { { "data-end-date", "true" }, { "type", "hidden" } }; + var endDateAttributes = new TagHelperAttributeList { { "data-hidden-datepicker", "true" }, { "data-end-date", "true" }, { "type", "hidden" } }; EndDateTagHelper = new InputTagHelper(Generator) { ViewContext = TagHelper.ViewContext, @@ -55,6 +76,23 @@ public class AbpDateRangePickerTagHelperService : AbpDatePickerBaseTagHelperServ InputTypeName = "hidden" }; + if (Clock.SupportsMultipleTimezone) + { + if (TagHelper.AspForEnd.Model is DateTime dateTime) + { + EndDateTagHelper.Format = "{0:O}"; + EndDateTagHelper.Value = Clock.ConvertToUserTime(dateTime).ToString("O"); + endDateAttributes.Add("value", EndDateTagHelper.Value); + } + + if (TagHelper.AspForEnd.Model is DateTimeOffset dateTimeOffset) + { + EndDateTagHelper.Format = "{0:O}"; + EndDateTagHelper.Value = Clock.ConvertToUserTime(dateTimeOffset).UtcDateTime.ToString("O"); + endDateAttributes.Add("value", EndDateTagHelper.Value); + } + } + EndDateTagHelperOutput = await EndDateTagHelper.ProcessAndGetOutputAsync(endDateAttributes, context, "input"); } @@ -78,7 +116,7 @@ public class AbpDateRangePickerTagHelperService : AbpDatePickerBaseTagHelperServ protected override int GetOrder() { - return TagHelper.Order; + return TagHelper.AspForStart?.Metadata.Order ?? 0; } protected override void AddBaseTagAttributes(TagHelperAttributeList attributes) @@ -92,7 +130,7 @@ public class AbpDateRangePickerTagHelperService : AbpDatePickerBaseTagHelperServ attributes.Add("data-start-date", convert); } } - + if (TagHelper.AspForEnd?.Model != null && SupportedInputTypes.TryGetValue(TagHelper.AspForEnd.Metadata.ModelType, out var convertFuncEnd)) { @@ -113,4 +151,4 @@ public class AbpDateRangePickerTagHelperService : AbpDatePickerBaseTagHelperServ { return TagHelper.AspForStart ?? TagHelper.AspForEnd; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpBundleContributorOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpBundleContributorOptions.cs similarity index 93% rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpBundleContributorOptions.cs rename to framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpBundleContributorOptions.cs index 79a6272e89..dfc706607e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpBundleContributorOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpBundleContributorOptions.cs @@ -1,6 +1,6 @@ -using System; +using System; using System.Collections.Concurrent; -using JetBrains.Annotations; +using System.Diagnostics.CodeAnalysis; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; @@ -27,4 +27,4 @@ public class AbpBundleContributorOptions _ => new BundleContributorCollection() ); } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo.Abp.AspNetCore.Mvc.UI.Bundling.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo.Abp.AspNetCore.Mvc.UI.Bundling.csproj index fd3995f797..aadb26f388 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo.Abp.AspNetCore.Mvc.UI.Bundling.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo.Abp.AspNetCore.Mvc.UI.Bundling.csproj @@ -19,6 +19,7 @@ +
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs index 2292c93a53..13b157748b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.VirtualFileSystem; using Volo.Abp.Bundling.Styles; @@ -21,8 +22,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; [DependsOn( typeof(AbpAspNetCoreMvcUiBootstrapModule), - typeof(AbpMinifyModule), - typeof(AbpAspNetCoreMvcUiBundlingAbstractionsModule) + typeof(AbpAspNetCoreBundlingModule) )] public class AbpAspNetCoreMvcUiBundlingModule : AbpModule { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleManager.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleManager.cs index 9ac43585c0..c28c19a716 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleManager.cs @@ -9,6 +9,10 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.FileProviders; +using Volo.Abp.AspNetCore.Bundling; +using Volo.Abp.AspNetCore.Bundling.Scripts; +using Volo.Abp.AspNetCore.Bundling.Styles; using Volo.Abp.AspNetCore.Mvc.UI.Bundling.Scripts; using Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles; using Volo.Abp.AspNetCore.Mvc.UI.Resources; @@ -17,162 +21,47 @@ using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; -public class BundleManager : IBundleManager, ITransientDependency +public class BundleManager : BundleManagerBase, ITransientDependency { - public ILogger Logger { get; set; } - - protected readonly AbpBundlingOptions Options; - protected readonly AbpBundleContributorOptions ContributorOptions; - protected readonly IWebHostEnvironment HostingEnvironment; - protected readonly IScriptBundler ScriptBundler; - protected readonly IStyleBundler StyleBundler; - protected readonly IServiceProvider ServiceProvider; - protected readonly IDynamicFileProvider DynamicFileProvider; - protected readonly IBundleCache BundleCache; - protected readonly IWebRequestResources RequestResources; - public BundleManager( IOptions options, IOptions contributorOptions, - IScriptBundler scriptBundler, + IScriptBundler scriptBundler, IStyleBundler styleBundler, - IWebHostEnvironment hostingEnvironment, IServiceProvider serviceProvider, IDynamicFileProvider dynamicFileProvider, IBundleCache bundleCache, - IWebRequestResources requestResources) + IWebHostEnvironment hostingEnvironment, + IWebRequestResources requestResources) : base( + options, + contributorOptions, + scriptBundler, + styleBundler, + serviceProvider, + dynamicFileProvider, + bundleCache) { - Options = options.Value; - ContributorOptions = contributorOptions.Value; HostingEnvironment = hostingEnvironment; - ScriptBundler = scriptBundler; - ServiceProvider = serviceProvider; - DynamicFileProvider = dynamicFileProvider; - BundleCache = bundleCache; RequestResources = requestResources; - StyleBundler = styleBundler; - - Logger = NullLogger.Instance; - } - - public virtual async Task> GetStyleBundleFilesAsync(string bundleName) - { - return await GetBundleFilesAsync(Options.StyleBundles, bundleName, StyleBundler); - } - - public virtual async Task> GetScriptBundleFilesAsync(string bundleName) - { - return await GetBundleFilesAsync(Options.ScriptBundles, bundleName, ScriptBundler); - } - - protected virtual async Task> GetBundleFilesAsync(BundleConfigurationCollection bundles, string bundleName, IBundler bundler) - { - var files = new List(); - - var contributors = GetContributors(bundles, bundleName); - var bundleFiles = RequestResources.TryAdd(await GetBundleFilesAsync(contributors)); - var dynamicResources = RequestResources.TryAdd(await GetDynamicResourcesAsync(contributors)); - - if (!IsBundlingEnabled()) - { - return bundleFiles.Union(dynamicResources).ToImmutableList(); - } - - var localBundleFiles = new List(); - foreach (var bundleFile in bundleFiles) - { - if (!bundleFile.IsExternalFile) - { - localBundleFiles.Add(bundleFile.FileName); - } - else - { - if (localBundleFiles.Count != 0) - { - files.AddRange(AddToBundleCache(bundleName, bundler, localBundleFiles).Files); - localBundleFiles.Clear(); - } - - files.Add(bundleFile); - } - } - - if (localBundleFiles.Count != 0) - { - files.AddRange(AddToBundleCache(bundleName, bundler, localBundleFiles).Files); - } - - return files.Union(dynamicResources).ToImmutableList(); } - private BundleCacheItem AddToBundleCache(string bundleName, IBundler bundler, List bundleFiles) - { - var bundleRelativePath = - Options.BundleFolderName.EnsureEndsWith('/') + - bundleName + "." + bundleFiles.JoinAsString("|").ToMd5() + "." + bundler.FileExtension; - - return BundleCache.GetOrAdd(bundleRelativePath, () => - { - var cacheValue = new BundleCacheItem( - new List - { - new BundleFile("/" + bundleRelativePath) - } - ); - - WatchChanges(cacheValue, bundleFiles, bundleRelativePath); + protected IWebHostEnvironment HostingEnvironment { get; } - var bundleResult = bundler.Bundle( - new BundlerContext( - bundleRelativePath, - bundleFiles, - IsMinficationEnabled() - ) - ); + protected IWebRequestResources RequestResources { get; } - SaveBundleResult(bundleRelativePath, bundleResult); - return cacheValue; - }); - } - private void WatchChanges(BundleCacheItem cacheValue, List files, string bundleRelativePath) + protected async override Task> GetBundleFilesAsync(List contributors) { - lock (cacheValue.WatchDisposeHandles) - { - foreach (var file in files) - { - var watchDisposeHandle = HostingEnvironment.WebRootFileProvider.Watch(file).RegisterChangeCallback(_ => - { - lock (cacheValue.WatchDisposeHandles) - { - cacheValue.WatchDisposeHandles.ForEach(h => h.Dispose()); - cacheValue.WatchDisposeHandles.Clear(); - } - - BundleCache.Remove(bundleRelativePath); - DynamicFileProvider.Delete("/wwwroot/" + bundleRelativePath); //TODO: get rid of wwwroot! - }, null); - - cacheValue.WatchDisposeHandles.Add(watchDisposeHandle); - } - } + return RequestResources.TryAdd(await base.GetBundleFilesAsync(contributors)); } - protected virtual void SaveBundleResult(string bundleRelativePath, BundleResult bundleResult) + protected async override Task> GetDynamicResourcesAsync(List contributors) { - var fileName = bundleRelativePath.Substring(bundleRelativePath.IndexOf('/') + 1); - - DynamicFileProvider.AddOrUpdate( - new InMemoryFileInfo( - "/wwwroot/" + bundleRelativePath, //TODO: get rid of wwwroot! - Encoding.UTF8.GetBytes(bundleResult.Content), - fileName - ) - ); + return RequestResources.TryAdd(await base.GetDynamicResourcesAsync(contributors)); } - public virtual bool IsBundlingEnabled() + public override bool IsBundlingEnabled() { switch (Options.Mode) { @@ -188,7 +77,7 @@ public class BundleManager : IBundleManager, ITransientDependency } } - protected virtual bool IsMinficationEnabled() + protected override bool IsMinficationEnabled() { switch (Options.Mode) { @@ -204,78 +93,8 @@ public class BundleManager : IBundleManager, ITransientDependency } } - protected async Task> GetBundleFilesAsync(List contributors) - { - var context = CreateBundleConfigurationContext(); - - foreach (var contributor in contributors) - { - await contributor.PreConfigureBundleAsync(context); - } - - foreach (var contributor in contributors) - { - await contributor.ConfigureBundleAsync(context); - } - - foreach (var contributor in contributors) - { - await contributor.PostConfigureBundleAsync(context); - } - - return context.Files; - } - - protected virtual async Task> GetDynamicResourcesAsync(List contributors) - { - var context = CreateBundleConfigurationContext(); - - foreach (var contributor in contributors) - { - await contributor.ConfigureDynamicResourcesAsync(context); - } - - return context.Files; - } - - protected virtual BundleConfigurationContext CreateBundleConfigurationContext() - { - return new BundleConfigurationContext(ServiceProvider, HostingEnvironment.WebRootFileProvider, Options.Parameters); - } - - protected virtual List GetContributors(BundleConfigurationCollection bundles, string bundleName) - { - var contributors = new List(); - - AddContributorsWithBaseBundles(contributors, bundles, bundleName); - - for (var i = 0; i < contributors.Count; ++i) - { - var extensions = ContributorOptions.Extensions(contributors[i].GetType()).GetAll(); - if (extensions.Count > 0) - { - contributors.InsertRange(i + 1, extensions); - i += extensions.Count; - } - } - - return contributors; - } - - protected virtual void AddContributorsWithBaseBundles(List contributors, BundleConfigurationCollection bundles, string bundleName) + protected override IFileProvider GetFileProvider() { - var bundleConfiguration = bundles.Get(bundleName); - - foreach (var baseBundleName in bundleConfiguration.BaseBundles) - { - AddContributorsWithBaseBundles(contributors, bundles, baseBundleName); //Recursive call - } - - var selfContributors = bundleConfiguration.Contributors.GetAll(); - - if (selfContributors.Any()) - { - contributors.AddRange(selfContributors); - } + return HostingEnvironment.WebRootFileProvider; } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/MvcUiBundlerBase.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/MvcUiBundlerBase.cs new file mode 100644 index 0000000000..87ba769f75 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/MvcUiBundlerBase.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Bundling; +using Volo.Abp.Minify; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +public abstract class MvcUiBundlerBase : BundlerBase +{ + protected IWebHostEnvironment WebHostingEnvironment { get; } + + protected MvcUiBundlerBase( + IWebHostEnvironment webHostingEnvironment, + IMinifier minifier, + IOptions bundlingOptions) : base(minifier, bundlingOptions) + { + WebHostingEnvironment = webHostingEnvironment; + } + + protected override IFileInfo FindFileInfo(string file) + { + return WebHostingEnvironment.WebRootFileProvider.GetFileInfo(file); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Scripts/IScriptBundler.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Scripts/IScriptBundler.cs deleted file mode 100644 index a0204fdb87..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Scripts/IScriptBundler.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Scripts; - -public interface IScriptBundler : IBundler -{ - -} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Scripts/ScriptBundler.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Scripts/ScriptBundler.cs index 7e3360e4c8..8f48ad31e8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Scripts/ScriptBundler.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Scripts/ScriptBundler.cs @@ -1,20 +1,22 @@ using System; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Bundling; +using Volo.Abp.AspNetCore.Bundling.Scripts; using Volo.Abp.Minify.Scripts; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Scripts; -public class ScriptBundler : BundlerBase, IScriptBundler +public class ScriptBundler : MvcUiBundlerBase, IScriptBundler { public override string FileExtension => "js"; public ScriptBundler( - IWebHostEnvironment hostEnvironment, + IWebHostEnvironment hostingEnvironment, IJavascriptMinifier minifier, IOptions bundlingOptions) : base( - hostEnvironment, + hostingEnvironment, minifier, bundlingOptions) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/IStyleBundler.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/IStyleBundler.cs deleted file mode 100644 index 1550730d38..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/IStyleBundler.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles; - -public interface IStyleBundler : IBundler -{ - -} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/StyleBundler.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/StyleBundler.cs index b20e9af7ee..9d6277fa8e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/StyleBundler.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/StyleBundler.cs @@ -2,27 +2,28 @@ using System; using System.IO; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Options; -using Volo.Abp.AspNetCore.VirtualFileSystem; +using Volo.Abp.AspNetCore.Bundling; +using Volo.Abp.AspNetCore.Bundling.Styles; using Volo.Abp.Bundling.Styles; using Volo.Abp.Minify.Styles; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles; -public class StyleBundler : BundlerBase, IStyleBundler +public class StyleBundler : MvcUiBundlerBase, IStyleBundler { private readonly IWebHostEnvironment _hostingEnvironment; public override string FileExtension => "css"; public StyleBundler( - IWebHostEnvironment hostEnvironment, + IWebHostEnvironment hostingEnvironment, ICssMinifier minifier, IOptions bundlingOptions) : base( - hostEnvironment, + hostingEnvironment, minifier, bundlingOptions) { - _hostingEnvironment = hostEnvironment; + _hostingEnvironment = hostingEnvironment; } public string GetAbsolutePath(string relativePath) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs index 7939cb7912..b83d090f11 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Bundling; using Volo.Abp.DependencyInjection; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs index c8486ebadd..c4701aa7d5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Bundling; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs index bcfd7d95e2..1ed3a76fe1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Bundling; using Volo.Abp.AspNetCore.Security; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js index 9bba9ee9a9..e11a8f4d99 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js @@ -110,7 +110,7 @@ $.validator.defaults.ignore = ''; //TODO: Would be better if we can apply only f _onOpenCallbacks.triggerAll(_publicApi); - if ($firstVisibleInput.data("datepicker")) { + if ($firstVisibleInput.data("datepicker") || $firstVisibleInput.data("daterangepicker")) { return; //don't pop-up date pickers... } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js index 9bbf0abd28..bcb043a196 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js @@ -511,22 +511,19 @@ var abp = abp || {}; } }; - var ISOStringToDateTimeLocaleString = function (format) { - return function (data) { - var date = luxon - .DateTime - .fromISO(data, { - locale: abp.localization.currentCulture.name - }); - return format ? date.toLocaleString(format) : date.toLocaleString(); - }; + datatables.defaultRenderers['date'] = function (value) { + if (!value) { + return value; + } else { + return abp.clock.normalizeToLocaleString(value, { year: 'numeric', month: '2-digit', day: '2-digit' }); + } }; - datatables.defaultRenderers['date'] = function (value) { + datatables.defaultRenderers['time'] = function (value) { if (!value) { return value; } else { - return (ISOStringToDateTimeLocaleString())(value); + return abp.clock.normalizeToLocaleString(value, { hour: '2-digit', minute: '2-digit', second: '2-digit' }); } }; @@ -534,7 +531,7 @@ var abp = abp || {}; if (!value) { return value; } else { - return (ISOStringToDateTimeLocaleString(luxon.DateTime.DATETIME_SHORT))(value); + return abp.clock.normalizeToLocaleString(value); } }; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.css b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.css index 08bdc5d283..de679b468a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.css +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.css @@ -10,3 +10,8 @@ .dataTable tbody tr td div.dropdown ul.dropdown-menu li { cursor: pointer; } + +.abp-action-button ui.dropdown-menu[data-popper-reference-hidden]{ + visibility: hidden; + pointer-events: none; +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.min.css b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.min.css index ffc3d272a3..cd5ab5591b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.min.css +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.min.css @@ -1 +1 @@ -.dataTable{width:100% !important;border-spacing:0 !important;}.table td,.table th{padding:8px 10px;}.dataTable tbody tr td button{cursor:pointer;}.dataTable tbody tr td div.dropdown ul.dropdown-menu li{cursor:pointer;} +.dataTable{width:100%!important;border-spacing:0!important}.table td,.table th{padding:8px 10px}.dataTable tbody tr td button{cursor:pointer}.dataTable tbody tr td div.dropdown ul.dropdown-menu li{cursor:pointer}.abp-action-button ui.dropdown-menu[data-popper-reference-hidden]{visibility:hidden;pointer-events:none} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.scss b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.scss index 0ca0771faf..d2498b3cc7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.scss +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.scss @@ -27,3 +27,10 @@ } } } + +.abp-action-button { + ui.dropdown-menu[data-popper-reference-hidden] { + visibility: hidden; + pointer-events: none; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/date-range-picker/date-range-picker-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/date-range-picker/date-range-picker-extensions.js index 01fe50dec3..e10ceefbf9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/date-range-picker/date-range-picker-extensions.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/date-range-picker/date-range-picker-extensions.js @@ -752,4 +752,41 @@ $(function () { abp.dom.initializers.initializeDateRangePickers($('body')); }); -})(jQuery); \ No newline at end of file + + $.fn.handleDatepicker = function (datepickerSelector) { + var $this = $(this); + var datepickers = $this.find(datepickerSelector); + $this.find('input[class~="hidden-datepicker"]').remove(); + datepickers.each(function () { + var $this = $(this); + var datepicker = $this.data('daterangepicker'); + if (!datepicker) { + return; + } + if ($this.val() === '') { + return; + } + var name = $this.attr('name') || $this.data('name'); + $this.data('name', name).removeAttr('name'); + if (datepicker.singleDatePicker) { + var startDate = abp.clock.normalizeToString(datepicker.startDate.toDate()); + var startDateInput = $('').attr('type', 'hidden').attr('name', name).val(startDate).addClass('hidden-datepicker'); + $this.after(startDateInput); + } else { + if ($this.data('start-date')) { + var startDate = abp.clock.normalizeToString(datepicker.startDate.toDate()); + var startDateInput = $('').attr('type', 'hidden').attr('name', name).val(startDate).addClass('hidden-datepicker'); + $this.after(startDateInput); + } + if ($this.data('end-date')) { + var endDate = abp.clock.normalizeToString(datepicker.endDate.toDate()); + var endDateInput = $('').attr('type', 'hidden').attr('name', name).val(endDate).addClass('hidden-datepicker'); + $this.after(endDateInput); + } + } + }); + + return this; + }; + +})(jQuery); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Theming/AbpThemingOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Theming/AbpThemingOptions.cs index 1197ad6e53..bf999be982 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Theming/AbpThemingOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Theming/AbpThemingOptions.cs @@ -6,6 +6,12 @@ public class AbpThemingOptions public string? DefaultThemeName { get; set; } + /// + /// If set, the base element will be added to the head element of the page. + /// eg: + /// + public string? BaseUrl { get; set; } + public AbpThemingOptions() { Themes = new ThemeDictionary(); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs index 56acc2f745..548981053d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs @@ -16,6 +16,8 @@ public class AbpAspNetCoreMvcOptions public bool ExposeIntegrationServices { get; set; } = false; + public bool ExposeClientProxyServices { get; set; } = false; + public bool AutoModelValidation { get; set; } public bool EnableRazorRuntimeCompilationOnDevelopment { get; set; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs index 7f41f73165..73edea9fdf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs @@ -311,7 +311,32 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp protected virtual async Task GetTimingConfigAsync() { - var windowsTimeZoneId = await _settingProvider.GetOrNullAsync(TimingSettingNames.TimeZone); + var timeZone = await _settingProvider.GetOrNullAsync(TimingSettingNames.TimeZone); + + string? timeZoneId = null; + string? timeZoneName = null; + if (!timeZone.IsNullOrWhiteSpace()) + { + try + { + if (_timezoneProvider.GetIanaTimezones().Any(x => x.Value == timeZone)) + { + timeZoneId = _timezoneProvider.IanaToWindows(timeZone); + timeZoneName = timeZone; + } + else if (_timezoneProvider.GetWindowsTimezones().Any(x => x.Value == timeZone)) + { + timeZoneId = timeZone; + timeZoneName = _timezoneProvider.WindowsToIana(timeZone); + } + } + catch (Exception ex) + { + timeZoneId = null; + timeZoneName = null; + Logger.LogWarning(ex, $"Exception occurred while getting timezone({timeZone}) information"); + } + } return new TimingDto { @@ -319,13 +344,11 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp { Windows = new WindowsTimeZone { - TimeZoneId = windowsTimeZoneId + TimeZoneId = timeZoneId }, Iana = new IanaTimeZone { - TimeZoneName = windowsTimeZoneId.IsNullOrWhiteSpace() - ? null - : _timezoneProvider.WindowsToIana(windowsTimeZoneId!) + TimeZoneName = timeZoneName } } }; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs index 2cc051ba62..8fe801b9d5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.ModelBinding; @@ -99,6 +100,7 @@ public class AbpRemoteStreamContentModelBinder : IModelBin { var form = await request.ReadFormAsync(); + var useMemoryStream = form.Files.Count > 1; foreach (var file in form.Files) { // If there is an in the form and is left blank. @@ -109,13 +111,27 @@ public class AbpRemoteStreamContentModelBinder : IModelBin if (file.Name.Equals(modelName, StringComparison.OrdinalIgnoreCase)) { - postedFiles.Add(new RemoteStreamContent(file.OpenReadStream(), file.FileName, file.ContentType, file.Length).As()); + if (useMemoryStream) + { + var memoryStream = new MemoryStream(); + await file.OpenReadStream().CopyToAsync(memoryStream); + memoryStream.Position = 0; + postedFiles.Add(new RemoteStreamContent(memoryStream, file.FileName, file.ContentType, file.Length, disposeStream: false).As()); + bindingContext.HttpContext.Response.OnCompleted(async () => + { + await memoryStream.DisposeAsync(); + }); + } + else + { + postedFiles.Add(new RemoteStreamContent(file.OpenReadStream(), file.FileName, file.ContentType, file.Length, disposeStream: false).As()); + } } } } else if (bindingContext.IsTopLevelObject) { - postedFiles.Add(new RemoteStreamContent(request.Body, null, request.ContentType, request.ContentLength).As()); + postedFiles.Add(new RemoteStreamContent(request.Body, null, request.ContentType, request.ContentLength, disposeStream: false).As()); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs index 3d3a71958a..31ba046250 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs @@ -74,16 +74,29 @@ public class AbpServiceConvention : IAbpServiceConvention, ITransientDependency protected virtual void RemoveIntegrationControllersIfNotExposed(ApplicationModel application) { - if (Options.ExposeIntegrationServices) + if (!Options.ExposeIntegrationServices) { - return; + var integrationControllers = GetControllers(application) + .Where(c => IntegrationServiceAttribute.IsDefinedOrInherited(c.ControllerType)) + .ToArray(); + + application.Controllers.RemoveAll(integrationControllers); + } + + if (!Options.ExposeClientProxyServices) + { + var clientProxyServiceControllers = GetControllers(application) + .Where(c => IsClientProxyService(c.ControllerType)) + .ToArray(); + + application.Controllers.RemoveAll(clientProxyServiceControllers); } - - var integrationControllers = GetControllers(application) - .Where(c => IntegrationServiceAttribute.IsDefinedOrInherited(c.ControllerType)) - .ToArray(); + } - application.Controllers.RemoveAll(integrationControllers); + protected virtual bool IsClientProxyService(Type controllerType) + { + return typeof(IApplicationService).IsAssignableFrom(controllerType) && + controllerType.GetBaseClasses().Any(x => x.IsGenericType && x.GetGenericTypeDefinition().FullName!.StartsWith("Volo.Abp.Http.Client.ClientProxying.ClientProxyBase")); } protected virtual IList GetControllers(ApplicationModel application) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs index f09cde9815..1c616e7a8a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs @@ -109,9 +109,12 @@ public class AbpExceptionFilter : IAsyncExceptionFilter, IAbpFilter, ITransientD remoteServiceErrorInfoBuilder.AppendLine($"---------- {nameof(RemoteServiceErrorInfo)} ----------"); remoteServiceErrorInfoBuilder.AppendLine(context.GetRequiredService().Serialize(remoteServiceErrorInfo, indented: true)); - var logger = context.GetService>(NullLogger.Instance)!; - var logLevel = context.Exception.GetLogLevel(); - logger.LogWithLevel(logLevel, remoteServiceErrorInfoBuilder.ToString()); - logger.LogException(context.Exception, logLevel); + if(exceptionHandlingOptions.ShouldLogException(context.Exception)) + { + var logger = context.GetService>(NullLogger.Instance)!; + var logLevel = context.Exception.GetLogLevel(); + logger.LogWithLevel(logLevel, remoteServiceErrorInfoBuilder.ToString()); + logger.LogException(context.Exception, logLevel); + } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs index 0e53b01cad..d8300eef04 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs @@ -90,10 +90,12 @@ public class AbpExceptionPageFilter : IAsyncPageFilter, IAbpFilter, ITransientDe remoteServiceErrorInfoBuilder.AppendLine($"---------- {nameof(RemoteServiceErrorInfo)} ----------"); remoteServiceErrorInfoBuilder.AppendLine(context.GetRequiredService().Serialize(remoteServiceErrorInfo, indented: true)); - var logger = context.GetService>(NullLogger.Instance)!; - logger.LogWithLevel(logLevel, remoteServiceErrorInfoBuilder.ToString()); - - logger.LogException(context.Exception!, logLevel); + if (context.Exception != null && exceptionHandlingOptions.ShouldLogException(context.Exception)) + { + var logger = context.GetService>(NullLogger.Instance)!; + logger.LogWithLevel(logLevel, remoteServiceErrorInfoBuilder.ToString()); + logger.LogException(context.Exception!, logLevel); + } await context.GetRequiredService().NotifyAsync(new ExceptionNotificationContext(context.Exception!)); @@ -113,6 +115,7 @@ public class AbpExceptionPageFilter : IAsyncPageFilter, IAbpFilter, ITransientDe } else { + var logger = context.GetService>(NullLogger.Instance)!; logger.LogWarning("HTTP response has already started, cannot set headers and status code!"); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/MvcCoreBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/MvcCoreBuilderExtensions.cs index 8c6edd2eb0..f2bac81316 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/MvcCoreBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/MvcCoreBuilderExtensions.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.Json.SystemTextJson; using Volo.Abp.Json.SystemTextJson.JsonConverters; +using Volo.Abp.Json.SystemTextJson.Modifiers; namespace Volo.Abp.AspNetCore.Mvc.Json; @@ -26,6 +27,13 @@ public static class MvcCoreBuilderExtensions options.JsonSerializerOptions.TypeInfoResolver = new AbpDefaultJsonTypeInfoResolver(rootServiceProvider .GetRequiredService>()); + + var dateTimeConverter = rootServiceProvider.GetRequiredService(); + var nullableDateTimeConverter = rootServiceProvider.GetRequiredService(); + + options.JsonSerializerOptions.TypeInfoResolver.As().Modifiers.Add( + new AbpDateTimeConverterModifier(dateTimeConverter, nullableDateTimeConverter) + .CreateModifyAction()); }); return builder; diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs index b4d5d4499c..b9142b5aed 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.RequestLocalization; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.StaticAssets; +using Microsoft.AspNetCore.Timing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; @@ -114,7 +115,7 @@ public static class AbpApplicationBuilderExtensions return app.UseMiddleware(); } - [Obsolete("Replace with AbpClaimsTransformation")] + [Obsolete("Use the TransformAbpClaims extension method from IServiceCollection instead.")] public static IApplicationBuilder UseAbpClaimsMap(this IApplicationBuilder app) { return app.UseMiddleware(); @@ -269,4 +270,14 @@ public static class AbpApplicationBuilderExtensions return app; } + + /// + /// Use this middleware after middleware. + /// + /// + /// + public static IApplicationBuilder UseAbpTimeZone(this IApplicationBuilder app) + { + return app.UseMiddleware(); + } } diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Timing/AbpTimeZoneMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Timing/AbpTimeZoneMiddleware.cs new file mode 100644 index 0000000000..8ee803feb0 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Timing/AbpTimeZoneMiddleware.cs @@ -0,0 +1,82 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Middleware; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Settings; +using Volo.Abp.Timing; +using Volo.Abp.Users; + +namespace Microsoft.AspNetCore.Timing; + +public class AbpTimeZoneMiddleware : AbpMiddlewareBase, ITransientDependency +{ + public async override Task InvokeAsync(HttpContext context, RequestDelegate next) + { + if (!context.RequestServices.GetRequiredService().SupportsMultipleTimezone) + { + await next(context); + return; + } + + // Try to get the timezone from the setting system first + var settingProvider = context.RequestServices.GetRequiredService(); + var timezone = await settingProvider.GetOrNullAsync(TimingSettingNames.TimeZone); + + if (timezone.IsNullOrWhiteSpace()) + { + // Try to get the timezone from the HTTP request if the setting is not available + timezone = await GetTimezoneFromRequestAsync(context); + } + + if (timezone.IsNullOrWhiteSpace()) + { + // Try to get the timezone from the current running server if the setting and request are not available + timezone = await GetLocalTimeZoneAsync(); + } + + var currentTimezoneProvider = context.RequestServices.GetRequiredService(); + using (currentTimezoneProvider.Change(timezone)) + { + await next(context); + } + } + + protected virtual async Task GetTimezoneFromRequestAsync(HttpContext context) + { + var timeZoneSources = new Func>[] + { + ctx => Task.FromResult(ctx.Request.Headers[TimeZoneConsts.DefaultTimeZoneKey].FirstOrDefault()), + ctx => Task.FromResult(ctx.Request.Query[TimeZoneConsts.DefaultTimeZoneKey].ToString()), + async ctx => ctx.Request.HasFormContentType + ? (await ctx.Request.ReadFormAsync())[TimeZoneConsts.DefaultTimeZoneKey].ToString() + : null, + ctx => Task.FromResult(ctx.Request.Cookies[TimeZoneConsts.DefaultTimeZoneKey]?.ToString()), + }; + + foreach (var source in timeZoneSources) + { + var timezone = await source(context); + if (!string.IsNullOrEmpty(timezone)) + { + return timezone; + } + } + + return null; + } + + protected virtual Task GetLocalTimeZoneAsync() + { + if (TimeZoneInfo.Local.HasIanaId) + { + return Task.FromResult(TimeZoneInfo.Local.Id); + } + + return TimeZoneInfo.TryConvertWindowsIdToIanaId(TimeZoneInfo.Local.Id, out var ianaName) + ? Task.FromResult(ianaName) + : Task.FromResult(null); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs index c99ac00fcc..c098ba2c15 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs @@ -58,7 +58,12 @@ public class AbpExceptionHandlingMiddleware : AbpMiddlewareBase, ITransientDepen private async Task HandleAndWrapException(HttpContext httpContext, Exception exception) { - _logger.LogException(exception); + var exceptionHandlingOptions = httpContext.RequestServices.GetRequiredService>().Value; + + if (exceptionHandlingOptions.ShouldLogException(exception)) + { + _logger.LogException(exception); + } await httpContext .RequestServices @@ -77,7 +82,6 @@ public class AbpExceptionHandlingMiddleware : AbpMiddlewareBase, ITransientDepen var errorInfoConverter = httpContext.RequestServices.GetRequiredService(); var statusCodeFinder = httpContext.RequestServices.GetRequiredService(); var jsonSerializer = httpContext.RequestServices.GetRequiredService(); - var exceptionHandlingOptions = httpContext.RequestServices.GetRequiredService>().Value; httpContext.Response.Clear(); httpContext.Response.StatusCode = (int)statusCodeFinder.GetStatusCode(httpContext, exception); diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs index 2104edab0a..70fad0d19e 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs @@ -11,7 +11,7 @@ using Volo.Abp.Security.Claims; namespace Volo.Abp.AspNetCore.Security.Claims; -[Obsolete("Replace with AbpClaimsTransformation")] +[Obsolete("Use the TransformAbpClaims extension method from IServiceCollection instead.")] public class AbpClaimsMapMiddleware : AbpMiddlewareBase, ITransientDependency { public async override Task InvokeAsync(HttpContext context, RequestDelegate next) diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs index 3357d8f95c..e834b3d00d 100644 --- a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs @@ -38,7 +38,7 @@ public static class AbpRegistrationBuilderExtensions } registrationBuilder = registrationBuilder.EnablePropertyInjection(moduleContainer, implementationType); - registrationBuilder = registrationBuilder.InvokeRegistrationActions(registrationActionList, serviceType, implementationType); + registrationBuilder = registrationBuilder.InvokeRegistrationActions(registrationActionList, serviceType, implementationType, serviceDescriptor.ServiceKey); return registrationBuilder; } @@ -69,10 +69,11 @@ public static class AbpRegistrationBuilderExtensions this IRegistrationBuilder registrationBuilder, ServiceRegistrationActionList registrationActionList, Type serviceType, - Type implementationType) + Type implementationType, + object? serviceKey = null) where TActivatorData : ReflectionActivatorData { - var serviceRegistredArgs = new OnServiceRegistredContext(serviceType, implementationType); + var serviceRegistredArgs = new OnServiceRegistredContext(serviceType, implementationType, serviceKey); foreach (var registrationAction in registrationActionList) { diff --git a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpBackgroundJobsHangfireModule.cs b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpBackgroundJobsHangfireModule.cs index ee747e4611..345a69703f 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpBackgroundJobsHangfireModule.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpBackgroundJobsHangfireModule.cs @@ -25,13 +25,8 @@ public class AbpBackgroundJobsHangfireModule : AbpModule if (!options.IsJobExecutionEnabled) { var hangfireOptions = context.ServiceProvider.GetRequiredService>().Value; - hangfireOptions.BackgroundJobServerFactory = CreateOnlyEnqueueJobServer; + context.ServiceProvider.GetRequiredService(); + hangfireOptions.BackgroundJobServerFactory = _ => null; } } - - private BackgroundJobServer? CreateOnlyEnqueueJobServer(IServiceProvider serviceProvider) - { - serviceProvider.GetRequiredService(); - return null; - } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueue.cs b/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueue.cs index 1dbe4a6dcc..bdc28c7ca0 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueue.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueue.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -120,46 +121,44 @@ public class JobQueue : IJobQueue ChannelAccessor?.Dispose(); } - protected virtual Task EnsureInitializedAsync() + protected virtual async Task EnsureInitializedAsync() { if (ChannelAccessor != null && ChannelAccessor.Channel.IsOpen) { - return Task.CompletedTask; + return; } - ChannelAccessor = ChannelPool.Acquire( + ChannelAccessor = await ChannelPool.AcquireAsync( ChannelPrefix + QueueConfiguration.QueueName, QueueConfiguration.ConnectionName ); - var result = QueueConfiguration.Declare(ChannelAccessor.Channel); + var result = await QueueConfiguration.DeclareAsync(ChannelAccessor.Channel); Logger.LogDebug($"RabbitMQ Queue '{QueueConfiguration.QueueName}' has {result.MessageCount} messages and {result.ConsumerCount} consumers."); // Declare delayed queue - QueueConfiguration.DeclareDelayed(ChannelAccessor.Channel); + await QueueConfiguration.DeclareDelayedAsync(ChannelAccessor.Channel); if (AbpBackgroundJobOptions.IsJobExecutionEnabled) { if (QueueConfiguration.PrefetchCount.HasValue) { - ChannelAccessor.Channel.BasicQos(0, QueueConfiguration.PrefetchCount.Value, false); + await ChannelAccessor.Channel.BasicQosAsync(0, QueueConfiguration.PrefetchCount.Value, false); } - + Consumer = new AsyncEventingBasicConsumer(ChannelAccessor.Channel); - Consumer.Received += MessageReceived; - + Consumer.ReceivedAsync += MessageReceived; + //TODO: What BasicConsume returns? - ChannelAccessor.Channel.BasicConsume( + await ChannelAccessor.Channel.BasicConsumeAsync( queue: QueueConfiguration.QueueName, autoAck: false, consumer: Consumer ); } - - return Task.CompletedTask; } - protected virtual Task PublishAsync( + protected virtual async Task PublishAsync( TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) @@ -167,29 +166,27 @@ public class JobQueue : IJobQueue //TODO: How to handle priority var routingKey = QueueConfiguration.QueueName; - var basicProperties = CreateBasicPropertiesToPublish(); + var basicProperties = new BasicProperties + { + Persistent = true + }; if (delay.HasValue) { routingKey = QueueConfiguration.DelayedQueueName; - basicProperties.Expiration = delay.Value.TotalMilliseconds.ToString(); + basicProperties.Expiration = delay.Value.TotalMilliseconds.ToString(CultureInfo.InvariantCulture); } - ChannelAccessor!.Channel.BasicPublish( - exchange: "", - routingKey: routingKey, - basicProperties: basicProperties, - body: Serializer.Serialize(args!) - ); - - return Task.CompletedTask; - } - - protected virtual IBasicProperties CreateBasicPropertiesToPublish() - { - var properties = ChannelAccessor!.Channel.CreateBasicProperties(); - properties.Persistent = true; - return properties; + if (ChannelAccessor != null) + { + await ChannelAccessor.Channel.BasicPublishAsync( + exchange: "", + routingKey: routingKey, + mandatory: false, + basicProperties: basicProperties, + body: Serializer.Serialize(args!) + ); + } } protected virtual async Task MessageReceived(object sender, BasicDeliverEventArgs ea) @@ -205,17 +202,17 @@ public class JobQueue : IJobQueue try { await JobExecuter.ExecuteAsync(context); - ChannelAccessor!.Channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false); + await ChannelAccessor!.Channel.BasicAckAsync(deliveryTag: ea.DeliveryTag, multiple: false); } catch (BackgroundJobExecutionException) { //TODO: Reject like that? - ChannelAccessor!.Channel.BasicReject(deliveryTag: ea.DeliveryTag, requeue: true); + await ChannelAccessor!.Channel.BasicRejectAsync(deliveryTag: ea.DeliveryTag, requeue: true); } catch (Exception) { //TODO: Reject like that? - ChannelAccessor!.Channel.BasicReject(deliveryTag: ea.DeliveryTag, requeue: false); + await ChannelAccessor!.Channel.BasicRejectAsync(deliveryTag: ea.DeliveryTag, requeue: false); } } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueueConfiguration.cs b/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueueConfiguration.cs index 9425cd0604..c6418f52f7 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueueConfiguration.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueueConfiguration.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using RabbitMQ.Client; using Volo.Abp.RabbitMQ; @@ -34,15 +35,15 @@ public class JobQueueConfiguration : QueueDeclareConfiguration DelayedQueueName = delayedQueueName; } - public virtual QueueDeclareOk DeclareDelayed(IModel channel) + public virtual async Task DeclareDelayedAsync(IChannel channel) { - var delayedArguments = new Dictionary(Arguments) + var delayedArguments = new Dictionary(Arguments) { ["x-dead-letter-routing-key"] = QueueName, ["x-dead-letter-exchange"] = string.Empty }; - return channel.QueueDeclare( + return await channel.QueueDeclareAsync( queue: DelayedQueueName, durable: Durable, exclusive: Exclusive, diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs index 2577181143..433ac699d2 100644 --- a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs +++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs @@ -1,5 +1,4 @@ -using System; -using Hangfire; +using Hangfire; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.Hangfire; @@ -23,17 +22,12 @@ public class AbpBackgroundWorkersHangfireModule : AbpModule if (!options.IsEnabled) { var hangfireOptions = context.ServiceProvider.GetRequiredService>().Value; - hangfireOptions.BackgroundJobServerFactory = CreateOnlyEnqueueJobServer; + context.ServiceProvider.GetRequiredService(); + hangfireOptions.BackgroundJobServerFactory = _ => null; } context.ServiceProvider .GetRequiredService() .Initialize(); } - - private BackgroundJobServer? CreateOnlyEnqueueJobServer(IServiceProvider serviceProvider) - { - serviceProvider.GetRequiredService(); - return null; - } } diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/PageAlert.razor b/framework/src/Volo.Abp.BlazoriseUI/Components/PageAlert.razor index 1a3809c985..51b12dd9f5 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/Components/PageAlert.razor +++ b/framework/src/Volo.Abp.BlazoriseUI/Components/PageAlert.razor @@ -5,16 +5,12 @@ { @alert.AlertMessage.Title - @if (alert.AlertMessage.Dismissible) - { - - } } @alert.AlertMessage.Text @if (alert.AlertMessage.Dismissible) - { - + { + } } diff --git a/framework/src/Volo.Abp.BlazoriseUI/wwwroot/volo.abp.blazoriseui.css b/framework/src/Volo.Abp.BlazoriseUI/wwwroot/volo.abp.blazoriseui.css index 84814c2ef3..f42b6735bd 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/wwwroot/volo.abp.blazoriseui.css +++ b/framework/src/Volo.Abp.BlazoriseUI/wwwroot/volo.abp.blazoriseui.css @@ -57,4 +57,8 @@ 100% { transform: rotate(0deg); } +} + +.table-responsive .dropdown-menu-position-strategy-fixed { + position: fixed !important; } \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs index 1c04fdb755..d20c8d1214 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs @@ -86,7 +86,7 @@ public class AliyunBlobProvider : BlobProviderBase, ITransientDependency return null; } var result = ossClient.GetObject(containerName, blobName); - return await TryCopyToMemoryStreamAsync(result.Content, args.CancellationToken); + return result.ResponseStream; } protected virtual string GetContainerName(BlobProviderArgs args) diff --git a/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProvider.cs index 00a82a0f3e..7c520da471 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProvider.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProvider.cs @@ -103,7 +103,7 @@ public class AwsBlobProvider : BlobProviderBase, ITransientDependency Key = blobName }); - return await TryCopyToMemoryStreamAsync(response.ResponseStream, args.CancellationToken); + return response.ResponseStream; } } diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProvider.cs index 5d45e749ea..bea55c624d 100644 --- a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProvider.cs +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProvider.cs @@ -66,8 +66,7 @@ public class AzureBlobProvider : BlobProviderBase, ITransientDependency } var blobClient = GetBlobClient(args, blobName); - var download = await blobClient.DownloadAsync(); - return await TryCopyToMemoryStreamAsync(download.Value.Content, args.CancellationToken); + return await blobClient.OpenReadAsync(cancellationToken: args.CancellationToken); } protected virtual BlobClient GetBlobClient(BlobProviderArgs args, string blobName) diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs index a221441749..2a410005db 100644 --- a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs @@ -70,13 +70,7 @@ public class FileSystemBlobProvider : BlobProviderBase, ITransientDependency return await Policy.Handle() .WaitAndRetryAsync(2, retryCount => TimeSpan.FromSeconds(retryCount)) - .ExecuteAsync(async () => - { - using (var fileStream = File.OpenRead(filePath)) - { - return await TryCopyToMemoryStreamAsync(fileStream, args.CancellationToken); - } - }); + .ExecuteAsync(() => Task.FromResult(File.OpenRead(filePath))); } protected virtual Task ExistsAsync(string filePath) diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs index de5f60ee4e..632bfa416e 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs @@ -1,7 +1,5 @@ using Microsoft.Extensions.DependencyInjection; using System; -using Microsoft.Extensions.Caching.Hybrid; -using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Caching.Hybrid; using Volo.Abp.Json; using Volo.Abp.Modularity; diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/Hybrid/AbpHybridCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/Hybrid/AbpHybridCache.cs index 2894af9f3d..66506a0b56 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/Hybrid/AbpHybridCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/Hybrid/AbpHybridCache.cs @@ -75,7 +75,7 @@ public class AbpHybridCache : IHybridCache : IHybridCache>.Instance; KeyNormalizer = keyNormalizer; @@ -215,10 +215,15 @@ public class AbpHybridCache : IHybridCache(bytes, 0, bytes.Length));; + // Because HybridCache wraps the cache in L2(distributed cache), we can’t unwrap it directly and can only retrieve the value through its API + return await HybridCache.GetOrCreateAsync( + key: NormalizeKey(key), + factory: async cancel => await factory(), + options: optionsFactory?.Invoke(), + tags: null, + cancellationToken: token); } value = await factory(); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs index 76bc82f570..3ee7cfdea3 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlingService.cs @@ -66,7 +66,14 @@ public class BundlingService : IBundlingService, ITransientDependency var frameworkVersion = GetTargetFrameworkVersion(projectFilePath, projectType); var projectName = Path.GetFileNameWithoutExtension(projectFilePath); - var assemblyFilePath = projectType == BundlingConsts.WebAssembly? PathHelper.GetWebAssemblyFilePath(directory, frameworkVersion, projectName) : PathHelper.GetMauiBlazorAssemblyFilePath(directory, projectName); + var assemblyFilePath = projectType == BundlingConsts.WebAssembly + ? PathHelper.GetWebAssemblyFilePath(directory, frameworkVersion, projectName) + : PathHelper.GetMauiBlazorAssemblyFilePath(directory, projectName); + if (assemblyFilePath == null) + { + throw new BundlingException("No assembly file found. Please build the project first."); + } + var startupModule = GetStartupModule(assemblyFilePath); var bundleDefinitions = new List(); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/PathHelper.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/PathHelper.cs index 1b6b56adab..4d3ff3ee77 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/PathHelper.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/PathHelper.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Linq; namespace Volo.Abp.Cli.Bundling; @@ -13,12 +14,16 @@ static internal class PathHelper static internal string GetWebAssemblyFilePath(string directory, string frameworkVersion, string projectFileName) { var outputDirectory = Path.Combine(directory, "bin", "Debug", frameworkVersion); - return Path.Combine(outputDirectory, projectFileName + ".dll"); + var path = Path.Combine(outputDirectory, projectFileName + ".dll"); + return !File.Exists(path) ? null : path; } static internal string GetMauiBlazorAssemblyFilePath(string directory, string projectFileName) { - return Directory.GetFiles(directory, "*.dll", SearchOption.AllDirectories).First(f => !f.Contains("android") && f.EndsWith(projectFileName + ".dll")); + return Directory.GetFiles(Path.Combine(directory, "bin"), "*.dll", SearchOption.AllDirectories).FirstOrDefault(f => + !f.Contains("android") && + !f.Contains("windows10") && + f.EndsWith(projectFileName + ".dll", StringComparison.OrdinalIgnoreCase)); } static internal string GetWwwRootPath(string directory) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs index 8d77675c3c..d968ae6b12 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliConsts.cs @@ -2,24 +2,24 @@ public static class CliConsts { - public const string Command = "AbpCliCommand"; + public static string Command = "AbpCliCommand"; - public const string BranchPrefix = "branch@"; + public static string BranchPrefix = "branch@"; - public const string DocsLink = "https://abp.io/docs"; + public static string DocsLink = "https://abp.io/docs"; - public const string HttpClientName = "AbpHttpClient"; + public static string HttpClientName = "AbpHttpClient"; - public const string GithubHttpClientName = "GithubHttpClient"; + public static string GithubHttpClientName = "GithubHttpClient"; - public const string LogoutUrl = CliUrls.WwwAbpIo + "api/license/logout"; + public static string LogoutUrl = CliUrls.WwwAbpIo + "api/license/logout"; - public const string LicenseCodePlaceHolder = @""; + public static string LicenseCodePlaceHolder = @""; - public const string AppSettingsJsonFileName = "appsettings.json"; + public static string AppSettingsJsonFileName = "appsettings.json"; + + public static string AppSettingsSecretJsonFileName = "appsettings.secrets.json"; - public const string AppSettingsSecretJsonFileName = "appsettings.secrets.json"; - public static class MemoryKeys { public const string LatestCliVersionCheckDate = "LatestCliVersionCheckDate"; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs index bb9621b91f..28cf42c2a2 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs @@ -5,22 +5,18 @@ namespace Volo.Abp.Cli; public static class CliUrls { - public const string WwwAbpIo = WwwAbpIoProduction; - public const string AccountAbpIo = AccountAbpIoProduction; - public const string TelemetryAbpIo = TelemetryAbpIoDevelopment; - public const string NuGetRootPath = NuGetRootPathProduction; - public const string LatestVersionCheckFullPath = - "https://raw.githubusercontent.com/abpframework/abp/dev/latest-versions.json"; - - public const string WwwAbpIoProduction = "https://abp.io/"; - public const string AccountAbpIoProduction = "https://account.abp.io/"; - public const string NuGetRootPathProduction = "https://nuget.abp.io/"; - public const string TelemetryAbpIoProduction = "https://telemetry.abp.io/"; - - public const string WwwAbpIoDevelopment = "https://localhost:44328/"; - public const string AccountAbpIoDevelopment = "https://localhost:44333/"; - public const string NuGetRootPathDevelopment = "https://localhost:44373/"; - public const string TelemetryAbpIoDevelopment = "https://localhost:44393/"; + public static string WwwAbpIo = WwwAbpIoProduction; + public static string AccountAbpIo = AccountAbpIoProduction; + public static string NuGetRootPath = NuGetRootPathProduction; + public static string LatestVersionCheckFullPath = "https://raw.githubusercontent.com/abpframework/abp/dev/latest-versions.json"; + + public static string WwwAbpIoProduction = "https://abp.io/"; + public static string AccountAbpIoProduction = "https://account.abp.io/"; + public static string NuGetRootPathProduction = "https://nuget.abp.io/"; + + public static string WwwAbpIoDevelopment = "https://localhost:44328/"; + public static string AccountAbpIoDevelopment = "https://localhost:44333/"; + public static string NuGetRootPathDevelopment = "https://localhost:44373/"; public static string GetNuGetServiceIndexUrl(string apiKey) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/HelpCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/HelpCommand.cs index 9fb647806b..cc13e9d187 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/HelpCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/HelpCommand.cs @@ -68,7 +68,7 @@ public class HelpCommand : IConsoleCommand, ITransientDependency sb.AppendLine("Command List:"); sb.AppendLine(""); - foreach (var command in AbpCliOptions.Commands.ToArray().Where(NotHiddenFromCommandList)) + foreach (var command in AbpCliOptions.Commands.ToArray().Where(NotHiddenFromCommandList).OrderBy(x => x.Key)) { var method = command.Value.GetMethod("GetShortDescription", BindingFlags.Static | BindingFlags.Public); if (method == null) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs index d8a3b6d154..48f5eca5f8 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs @@ -107,6 +107,9 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency Logger.LogInformation("Removing ABP Suite..."); RemoveSuite(); break; + + default: + throw new CliUsageException("Invalid Suite command! Run \"abp help suite\" command to see available Suite commands."); } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs index ad0992af4e..8c6df3b0b9 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs @@ -38,24 +38,25 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency var directory = commandLineArgs.Options.GetOrNull(Options.SolutionPath.Short, Options.SolutionPath.Long) ?? Directory.GetCurrentDirectory(); var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long); + var leptonXVersion = commandLineArgs.Options.GetOrNull(Options.LeptonXVersion.Short, Options.LeptonXVersion.Long); if (updateNuget || !updateNpm) { - await UpdateNugetPackages(commandLineArgs, directory, version); + await UpdateNugetPackages(commandLineArgs, directory, version, leptonXVersion); } if (updateNpm || !updateNuget) { - await UpdateNpmPackages(directory, version); + await UpdateNpmPackages(directory, version, leptonXVersion); } } - private async Task UpdateNpmPackages(string directory, string version) + private async Task UpdateNpmPackages(string directory, string version, string leptonXVersion) { - await _npmPackagesUpdater.Update(directory, version: version); + await _npmPackagesUpdater.Update(directory, version: version, leptonXVersion: leptonXVersion); } - private async Task UpdateNugetPackages(CommandLineArgs commandLineArgs, string directory, string version) + private async Task UpdateNugetPackages(CommandLineArgs commandLineArgs, string directory, string version, string leptonXVersion) { var solutions = new List(); var givenSolution = commandLineArgs.Options.GetOrNull(Options.SolutionName.Short, Options.SolutionName.Long); @@ -77,7 +78,7 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency { var solutionName = Path.GetFileName(solution).RemovePostFix(".sln"); - await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, checkAll: checkAll, version: version); + await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, checkAll: checkAll, version: version, leptonXVersion: leptonXVersion); Logger.LogInformation("Volo packages are updated in {SolutionName} solution", solutionName); } @@ -90,7 +91,7 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency { var projectName = Path.GetFileName(project).RemovePostFix(".csproj"); - await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, checkAll: checkAll, version: version); + await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, checkAll: checkAll, version: version, leptonXVersion: leptonXVersion); Logger.LogInformation("Volo packages are updated in {ProjectName} project", projectName); return; @@ -120,6 +121,7 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency sb.AppendLine("-sn|--solution-name (Specify the solution name)"); sb.AppendLine("--check-all (Check the new version of each package separately)"); sb.AppendLine("-v|--version (default: latest version)"); + sb.AppendLine("-lv|--leptonx-version (default: latest LeptonX version)"); sb.AppendLine(""); sb.AppendLine("Some examples:"); sb.AppendLine(""); @@ -167,5 +169,11 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency public const string Short = "v"; public const string Long = "version"; } + + public static class LeptonXVersion + { + public const string Short = "lv"; + public const string Long = "leptonx-version"; + } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs index 5ef3657529..411be841ed 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs @@ -137,10 +137,18 @@ public class AbpIoSourceCodeStore : ISourceCodeStore, ITransientDependency ? $"The specified template version ({templateVersion}) is different than the CLI version ({currentCliVersion}). This may cause compatibility issues." : $"The latest template version ({templateVersion}) is different than the CLI version ({currentCliVersion}). This may cause compatibility issues."); Logger.LogWarning("Please upgrade/downgrade the CLI version to the template version."); - Logger.LogWarning($"> dotnet tool uninstall -g volo.abp.cli"); - Logger.LogWarning(!templateVersion.IsPrerelease - ? $"> dotnet tool install -g volo.abp.cli --version \"{templateVersion.Major}.{templateVersion.Minor}.*\"" - : $"> dotnet tool install -g volo.abp.cli --version {templateVersion}"); + + if (currentCliVersion.ToString().EndsWith("-studio")) + { + Logger.LogWarning($"> abp install-old-cli --version {templateVersion}"); + } + else + { + Logger.LogWarning($"> dotnet tool uninstall -g volo.abp.cli"); + Logger.LogWarning(!templateVersion.IsPrerelease + ? $"> dotnet tool install -g volo.abp.cli --version \"{templateVersion.Major}.{templateVersion.Minor}.*\"" + : $"> dotnet tool install -g volo.abp.cli --version {templateVersion}"); + } if (userSpecifiedVersion) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs index 2c88f1e8c5..d543b43d65 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs @@ -50,7 +50,7 @@ public class NpmPackagesUpdater : ITransientDependency public async Task Update(string rootDirectory, bool includePreviews = false, bool includeReleaseCandidates = false, - bool switchToStable = false, string version = null, bool includePreRc = false) + bool switchToStable = false, string version = null, string leptonXVersion = null, bool includePreRc = false) { var fileList = _packageJsonFileFinder.Find(rootDirectory); @@ -80,6 +80,7 @@ public class NpmPackagesUpdater : ITransientDependency var updated = await UpdatePackagesInFile(file, includePreviews, includeReleaseCandidates, switchToStable, version, + leptonXVersion, includePreRc); packagesUpdated.TryAdd(file, updated); @@ -162,6 +163,7 @@ public class NpmPackagesUpdater : ITransientDependency bool includeReleaseCandidates = false, bool switchToStable = false, string specifiedVersion = null, + string specifiedLeptonXVersion = null, bool includePreRc = false) { var packagesUpdated = false; @@ -177,7 +179,7 @@ public class NpmPackagesUpdater : ITransientDependency foreach (var abpPackage in abpPackages) { var updated = await TryUpdatingPackage(filePath, abpPackage, includePreviews, includeReleaseCandidates, - switchToStable, specifiedVersion, includePreRc); + switchToStable, specifiedVersion, specifiedLeptonXVersion, includePreRc); if (updated) { @@ -188,7 +190,7 @@ public class NpmPackagesUpdater : ITransientDependency var updatedContent = packageJson.ToString(Formatting.Indented); File.WriteAllText(filePath, updatedContent); - + return packagesUpdated; } @@ -199,6 +201,7 @@ public class NpmPackagesUpdater : ITransientDependency bool includeReleaseCandidates = false, bool switchToStable = false, string specifiedVersion = null, + string specifiedLeptonXVersion = null, bool includePreRc = false) { var currentVersion = (string)package.Value; @@ -207,18 +210,36 @@ public class NpmPackagesUpdater : ITransientDependency if (!specifiedVersion.IsNullOrWhiteSpace()) { - if (!SpecifiedVersionExists(specifiedVersion, package)) + if (package.Name.IndexOf("leptonx", StringComparison.InvariantCultureIgnoreCase) > 0 && !specifiedLeptonXVersion.IsNullOrWhiteSpace()) { - return false; - } + if (!SpecifiedVersionExists(specifiedLeptonXVersion, package)) + { + return false; + } - if (SemanticVersion.Parse(specifiedVersion) <= - SemanticVersion.Parse(currentVersion.RemovePreFix("~", "^"))) - { - return false; + if (SemanticVersion.Parse(specifiedLeptonXVersion) <= + SemanticVersion.Parse(currentVersion.RemovePreFix("~", "^"))) + { + return false; + } + + version = specifiedLeptonXVersion.EnsureStartsWith('^'); } + else + { + if (!SpecifiedVersionExists(specifiedVersion, package)) + { + return false; + } - version = specifiedVersion.EnsureStartsWith('^'); + if (SemanticVersion.Parse(specifiedVersion) <= + SemanticVersion.Parse(currentVersion.RemovePreFix("~", "^"))) + { + return false; + } + + version = specifiedVersion.EnsureStartsWith('^'); + } } else { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs index 4ece393913..924d6041b1 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs @@ -27,7 +27,14 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency Logger = NullLogger.Instance; } - public async Task UpdateSolutionAsync(string solutionPath, bool includePreviews = false, bool includeReleaseCandidates = false, bool switchToStable = false, bool checkAll = false, string version = null) + public async Task UpdateSolutionAsync( + string solutionPath, + bool includePreviews = false, + bool includeReleaseCandidates = false, + bool switchToStable = false, + bool checkAll = false, + string version = null, + string leptonXVersion = null) { var projectPaths = ProjectFinder.GetProjectFiles(solutionPath); @@ -58,6 +65,7 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency latestReleaseCandidateVersionInfo.Version, latestVersionFromMyGet, version, + leptonXVersion, latestStableVersions: latestStableVersions); fs.Seek(0, SeekOrigin.Begin); @@ -75,7 +83,14 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency } } - public async Task UpdateProjectAsync(string projectPath, bool includeNightlyPreviews = false, bool includeReleaseCandidates = false, bool switchToStable = false, bool checkAll = false, string version = null) + public async Task UpdateProjectAsync( + string projectPath, + bool includeNightlyPreviews = false, + bool includeReleaseCandidates = false, + bool switchToStable = false, + bool checkAll = false, + string version = null, + string leptonXVersion = null) { if (checkAll && version.IsNullOrWhiteSpace()) { @@ -102,6 +117,7 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency latestReleaseCandidateVersionInfo.Version, latestVersionFromMyGet, version, + leptonXVersion, latestStableVersions: latestStableVersions); fs.Seek(0, SeekOrigin.Begin); @@ -166,6 +182,7 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency SemanticVersion latestNugetReleaseCandidateVersion = null, string latestMyGetVersion = null, string specifiedVersion = null, + string specifiedLeptonXVersion = null, List latestStableVersions = null) { string packageId = null; @@ -222,21 +239,35 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency var leptonXPackageVersion = latestStableVersions? .FirstOrDefault(v => v.Version.Equals(specifiedVersion, StringComparison.InvariantCultureIgnoreCase))?.LeptonX?.Version; - if ((isLeptonXPackage && string.IsNullOrWhiteSpace(leptonXPackageVersion)) || isStudioPackage) + if ((isLeptonXPackage && string.IsNullOrWhiteSpace(leptonXPackageVersion) && specifiedLeptonXVersion.IsNullOrWhiteSpace()) || isStudioPackage) { Logger.LogWarning("Package: {PackageId} could not be updated. Please manually update the package version yourself to prevent version mismatches!", packageId); continue; } - var isLeptonXPackageWithVersion = isLeptonXPackage && !string.IsNullOrWhiteSpace(leptonXPackageVersion); - - if (isLeptonXPackageWithVersion || await SpecifiedVersionExists(specifiedVersion, packageId)) + if (isLeptonXPackage) { - TryUpdatingPackage(isLeptonXPackageWithVersion ? leptonXPackageVersion : specifiedVersion); + var isLeptonXPackageWithVersion = isLeptonXPackage && !string.IsNullOrWhiteSpace(leptonXPackageVersion); + + if (isLeptonXPackageWithVersion || await SpecifiedVersionExists(specifiedLeptonXVersion, packageId)) + { + TryUpdatingPackage(specifiedLeptonXVersion ?? leptonXPackageVersion); + } + else + { + Logger.LogWarning($"Package \"{packageId}\" specified version v{specifiedLeptonXVersion} does not exist!"); + } } else { - Logger.LogWarning("Package \"{PackageId}\" specified version v{SpecifiedVersion} does not exist!", packageId, specifiedVersion); + if (await SpecifiedVersionExists(specifiedVersion, packageId)) + { + TryUpdatingPackage(specifiedVersion); + } + else + { + Logger.LogWarning($"Package \"{packageId}\" specified version v{specifiedVersion} does not exist!"); + } } void TryUpdatingPackage(string versionToUpdate) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Version/CliVersionService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Version/CliVersionService.cs index eb5c13a561..0bb8536162 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Version/CliVersionService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Version/CliVersionService.cs @@ -34,6 +34,11 @@ public class CliVersionService : ITransientDependency break; } + if (line.StartsWith("volo.abp.studio.cli", StringComparison.InvariantCultureIgnoreCase)) + { + var assemblyVersion = string.Join(".", Assembly.GetExecutingAssembly().GetFileVersion().Split('.').Take(3)); + return SemanticVersion.Parse(assemblyVersion + "-studio"); + } } if (currentCliVersion == null) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs index bc67fac38f..3d6fa77208 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs @@ -17,7 +17,6 @@ public class RemoteStreamContent : IRemoteStreamContent public RemoteStreamContent(Stream stream, string? fileName = null, string? contentType = null, long? readOnlyLength = null, bool disposeStream = true) { _stream = stream; - FileName = fileName; if (contentType != null) { @@ -34,10 +33,16 @@ public class RemoteStreamContent : IRemoteStreamContent public virtual void Dispose() { - if (!_disposed && _disposeStream) + if (_disposed) { - _disposed = true; - _stream?.Dispose(); + return; } + + if (_disposeStream) + { + _stream.Dispose(); + } + + _disposed = true; } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IOnServiceRegistredContext.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IOnServiceRegistredContext.cs index 86dad068b6..a1c50c0c64 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IOnServiceRegistredContext.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IOnServiceRegistredContext.cs @@ -9,4 +9,6 @@ public interface IOnServiceRegistredContext ITypeList Interceptors { get; } Type ImplementationType { get; } + + object? ServiceKey { get; } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/OnServiceRegistredContext.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/OnServiceRegistredContext.cs index a843b136f6..8491c94e91 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/OnServiceRegistredContext.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/OnServiceRegistredContext.cs @@ -13,10 +13,13 @@ public class OnServiceRegistredContext : IOnServiceRegistredContext public virtual Type ImplementationType { get; } - public OnServiceRegistredContext(Type serviceType, [NotNull] Type implementationType) + public virtual object? ServiceKey { get; } + + public OnServiceRegistredContext(Type serviceType, [NotNull] Type implementationType, object? serviceKey = null) { ServiceType = Check.NotNull(serviceType, nameof(serviceType)); ImplementationType = Check.NotNull(implementationType, nameof(implementationType)); + ServiceKey = serviceKey; Interceptors = new TypeList(); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ServiceConfigurationContext.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ServiceConfigurationContext.cs index 6141777cec..f1e55b85d8 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ServiceConfigurationContext.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ServiceConfigurationContext.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using JetBrains.Annotations; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace Volo.Abp.Modularity; @@ -8,6 +9,13 @@ public class ServiceConfigurationContext { public IServiceCollection Services { get; } + public IConfiguration Configuration { + get { + return _configuration ??= Services.GetConfiguration(); + } + } + private IConfiguration? _configuration; + public IDictionary Items { get; } /// @@ -29,4 +37,4 @@ public class ServiceConfigurationContext Services = Check.NotNull(services, nameof(services)); Items = new Dictionary(); } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Domain.Shared/Volo/Abp/Domain/Entities/Events/Distributed/AbpDistributedEntityEventOptions.cs b/framework/src/Volo.Abp.Ddd.Domain.Shared/Volo/Abp/Domain/Entities/Events/Distributed/AbpDistributedEntityEventOptions.cs index 19af763c85..8109d945bc 100644 --- a/framework/src/Volo.Abp.Ddd.Domain.Shared/Volo/Abp/Domain/Entities/Events/Distributed/AbpDistributedEntityEventOptions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain.Shared/Volo/Abp/Domain/Entities/Events/Distributed/AbpDistributedEntityEventOptions.cs @@ -4,11 +4,14 @@ public class AbpDistributedEntityEventOptions { public IAutoEntityDistributedEventSelectorList AutoEventSelectors { get; } + public IAutoEntityDistributedEventSelectorList IgnoredEventSelectors { get; } + public EtoMappingDictionary EtoMappings { get; set; } public AbpDistributedEntityEventOptions() { AutoEventSelectors = new AutoEntityDistributedEventSelectorList(); + IgnoredEventSelectors = new AutoEntityDistributedEventSelectorList(); EtoMappings = new EtoMappingDictionary(); } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheBase.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheBase.cs index 62d0b5304e..7cea931c12 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheBase.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheBase.cs @@ -16,12 +16,12 @@ public abstract class EntityCacheBase : where TEntityCacheItem : class { protected IReadOnlyRepository Repository { get; } - protected IDistributedCache Cache { get; } + protected IDistributedCache, TKey> Cache { get; } protected IUnitOfWorkManager UnitOfWorkManager { get; } protected EntityCacheBase( IReadOnlyRepository repository, - IDistributedCache cache, + IDistributedCache, TKey> cache, IUnitOfWorkManager unitOfWorkManager) { Repository = repository; @@ -31,7 +31,7 @@ public abstract class EntityCacheBase : public virtual async Task FindAsync(TKey id) { - return await Cache.GetOrAddAsync( + return (await Cache.GetOrAddAsync( id, async () => { @@ -41,7 +41,7 @@ public abstract class EntityCacheBase : } return MapToCacheItem(await Repository.FindAsync(id))!; - }); + }))?.Value; } public virtual async Task GetAsync(TKey id) @@ -56,7 +56,7 @@ public abstract class EntityCacheBase : } return MapToCacheItem(await Repository.GetAsync(id))!; - }))!; + }))!.Value!; } protected virtual bool HasObjectExtensionInfo() @@ -65,15 +65,10 @@ public abstract class EntityCacheBase : ObjectExtensionManager.Instance.GetOrNull(typeof(TEntity)) != null; } - protected abstract TEntityCacheItem? MapToCacheItem(TEntity? entity); + protected abstract EntityCacheItemWrapper? MapToCacheItem(TEntity? entity); public async Task HandleEventAsync(EntityChangedEventData eventData) { - if (eventData is EntityCreatedEventData) - { - return; - } - /* Why we are using double remove: * First Cache.RemoveAsync drops the cache item in a unit of work. * Some other application / thread may read the value from database and put it to the cache again diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheItemWrapper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheItemWrapper.cs new file mode 100644 index 0000000000..e1d12692c8 --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheItemWrapper.cs @@ -0,0 +1,12 @@ +namespace Volo.Abp.Domain.Entities.Caching; + +public class EntityCacheItemWrapper + where TEntityCacheItem : class +{ + public TEntityCacheItem? Value { get; set; } + + public EntityCacheItemWrapper(TEntityCacheItem? value) + { + Value = value; + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapper.cs index a37ad49b1e..62b3162cdd 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapper.cs @@ -15,7 +15,7 @@ public class EntityCacheWithObjectMapper : public EntityCacheWithObjectMapper( IReadOnlyRepository repository, - IDistributedCache cache, + IDistributedCache, TKey> cache, IUnitOfWorkManager unitOfWorkManager, IObjectMapper objectMapper) : base(repository, cache, unitOfWorkManager) @@ -23,7 +23,7 @@ public class EntityCacheWithObjectMapper : ObjectMapper = objectMapper; } - protected override TEntityCacheItem? MapToCacheItem(TEntity? entity) + protected override EntityCacheItemWrapper? MapToCacheItem(TEntity? entity) { if (entity == null) { @@ -32,9 +32,9 @@ public class EntityCacheWithObjectMapper : if (typeof(TEntity) == typeof(TEntityCacheItem)) { - return entity.As(); + return new EntityCacheItemWrapper(entity.As()); } - return ObjectMapper.Map(entity); + return new EntityCacheItemWrapper(ObjectMapper.Map(entity)); } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapperContext.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapperContext.cs index f468598652..2471e1057c 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapperContext.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapperContext.cs @@ -12,7 +12,7 @@ public class EntityCacheWithObjectMapperContext repository, - IDistributedCache cache, + IDistributedCache, TKey> cache, IUnitOfWorkManager unitOfWorkManager, IObjectMapper objectMapper)// Intentionally injected with TContext : base(repository, cache, unitOfWorkManager, objectMapper) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithoutCacheItem.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithoutCacheItem.cs index 125273173e..4738534831 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithoutCacheItem.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithoutCacheItem.cs @@ -10,14 +10,14 @@ public class EntityCacheWithoutCacheItem : { public EntityCacheWithoutCacheItem( IReadOnlyRepository repository, - IDistributedCache cache, + IDistributedCache, TKey> cache, IUnitOfWorkManager unitOfWorkManager) : base(repository, cache, unitOfWorkManager) { } - protected override TEntity? MapToCacheItem(TEntity? entity) + protected override EntityCacheItemWrapper? MapToCacheItem(TEntity? entity) { - return entity; + return new EntityCacheItemWrapper(entity); } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs index a42b921068..2c705a01fd 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs @@ -10,8 +10,18 @@ public class AbpEntityChangeOptions public IEntitySelectorList IgnoredNavigationEntitySelectors { get; set; } + /// + /// Default: true. + /// Update the aggregate root when any navigation property changes. + /// Some properties like ConcurrencyStamp,LastModificationTime,LastModifierId etc. will be updated. + /// + public bool UpdateAggregateRootWhenNavigationChanges { get; set; } = true; + + public IEntitySelectorList IgnoredUpdateAggregateRootSelectors { get; set; } + public AbpEntityChangeOptions() { IgnoredNavigationEntitySelectors = new EntitySelectorList(); + IgnoredUpdateAggregateRootSelectors = new EntitySelectorList(); } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs index 44eb5c058a..eb16a6910c 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs @@ -68,13 +68,9 @@ public class EntityChangeEventHelper : IEntityChangeEventHelper, ITransientDepen private bool ShouldPublishDistributedEventForEntity(object entity) { - return DistributedEntityEventOptions - .AutoEventSelectors - .IsMatch( - ProxyHelper - .UnProxy(entity) - .GetType() - ); + var entityType = ProxyHelper.UnProxy(entity).GetType(); + return !DistributedEntityEventOptions.IgnoredEventSelectors.IsMatch(entityType) && + DistributedEntityEventOptions.AutoEventSelectors.IsMatch(entityType); } public virtual void PublishEntityUpdatedEvent(object entity) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 626daf0bb4..e91d08b8c9 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -274,7 +274,9 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, continue; } - if (entityEntry.State == EntityState.Unchanged) + if (EntityChangeOptions.Value.UpdateAggregateRootWhenNavigationChanges && + EntityChangeOptions.Value.IgnoredUpdateAggregateRootSelectors.All(selector => !selector.Predicate(entityEntry.Entity.GetType())) && + entityEntry.State == EntityState.Unchanged) { ApplyAbpConceptsForModifiedEntity(entityEntry, true); } @@ -446,7 +448,12 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.All(selector => !selector.Predicate(entry.Entity.GetType())) && AbpEfCoreNavigationHelper.IsNavigationEntryModified(entry)) { - ApplyAbpConceptsForModifiedEntity(entry, true); + if (EntityChangeOptions.Value.UpdateAggregateRootWhenNavigationChanges && + EntityChangeOptions.Value.IgnoredUpdateAggregateRootSelectors.All(selector => !selector.Predicate(entry.Entity.GetType()))) + { + ApplyAbpConceptsForModifiedEntity(entry, true); + } + if (entry.Entity is ISoftDelete && entry.Entity.As().IsDeleted) { EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity); @@ -478,11 +485,13 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, } } - if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges) + if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges && + EntityChangeOptions.Value.UpdateAggregateRootWhenNavigationChanges) { foreach (var entry in AbpEfCoreNavigationHelper.GetChangedEntityEntries() .Where(x => x.State == EntityState.Unchanged) - .Where(x=> EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.All(selector => !selector.Predicate(x.Entity.GetType())))) + .Where(x => EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.All(selector => !selector.Predicate(x.Entity.GetType()))) + .Where(x => EntityChangeOptions.Value.IgnoredUpdateAggregateRootSelectors.All(selector => !selector.Predicate(x.Entity.GetType())))) { UpdateConcurrencyStamp(entry); } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs index c98ef478d4..6af11ebeca 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Domain; +using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.EntityFrameworkCore.DistributedEvents; using Volo.Abp.Modularity; using Volo.Abp.Uow.EntityFrameworkCore; @@ -28,5 +29,11 @@ public class AbpEntityFrameworkCoreModule : AbpModule context.Services.TryAddTransient(typeof(IDbContextProvider<>), typeof(UnitOfWorkDbContextProvider<>)); context.Services.AddTransient(typeof(IDbContextEventOutbox<>), typeof(DbContextEventOutbox<>)); context.Services.AddTransient(typeof(IDbContextEventInbox<>), typeof(DbContextEventInbox<>)); + + Configure(options => + { + options.IgnoredEventSelectors.Add(); + options.IgnoredEventSelectors.Add(); + }); } } diff --git a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpRabbitMqEventBusOptions.cs b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpRabbitMqEventBusOptions.cs index 130f5b38e2..0b450ca26e 100644 --- a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpRabbitMqEventBusOptions.cs +++ b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpRabbitMqEventBusOptions.cs @@ -17,10 +17,10 @@ public class AbpRabbitMqEventBusOptions public ushort? PrefetchCount { get; set; } - public IDictionary QueueArguments { get; set; } = new Dictionary(); + public IDictionary QueueArguments { get; set; } = new Dictionary(); + + public IDictionary ExchangeArguments { get; set; } = new Dictionary(); - public IDictionary ExchangeArguments { get; set; } = new Dictionary(); - public string GetExchangeTypeOrDefault() { return string.IsNullOrEmpty(ExchangeType) diff --git a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs index b0f85afb56..3a647388b5 100644 --- a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs +++ b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs @@ -97,7 +97,7 @@ public class RabbitMqDistributedEventBus : DistributedEventBusBase, IRabbitMqDis SubscribeHandlers(AbpDistributedEventBusOptions.Handlers); } - private async Task ProcessEventAsync(IModel channel, BasicDeliverEventArgs ea) + private async Task ProcessEventAsync(IChannel channel, BasicDeliverEventArgs ea) { var eventName = ea.RoutingKey; var eventType = EventTypes.GetOrDefault(eventName); @@ -224,10 +224,10 @@ public class RabbitMqDistributedEventBus : DistributedEventBusBase, IRabbitMqDis IEnumerable outgoingEvents, OutboxConfig outboxConfig) { - using (var channel = ConnectionPool.Get(AbpRabbitMqEventBusOptions.ConnectionName).CreateModel()) + using (var channel = await (await ConnectionPool.GetAsync(AbpRabbitMqEventBusOptions.ConnectionName)) + .CreateChannelAsync(new CreateChannelOptions(publisherConfirmationsEnabled: true, publisherConfirmationTrackingEnabled: true, new ThrottlingRateLimiter(256)))) { var outgoingEventArray = outgoingEvents.ToArray(); - channel.ConfirmSelect(); foreach (var outgoingEvent in outgoingEventArray) { @@ -248,8 +248,6 @@ public class RabbitMqDistributedEventBus : DistributedEventBusBase, IRabbitMqDis }); } } - - channel.WaitForConfirmsOrDie(); } } @@ -293,31 +291,33 @@ public class RabbitMqDistributedEventBus : DistributedEventBusBase, IRabbitMqDis return PublishAsync(eventName, body, headersArguments, eventId, correlationId); } - protected virtual Task PublishAsync( + protected virtual async Task PublishAsync( string eventName, byte[] body, Dictionary? headersArguments = null, Guid? eventId = null, string? correlationId = null) { - using (var channel = ConnectionPool.Get(AbpRabbitMqEventBusOptions.ConnectionName).CreateModel()) + using (var channel = await (await ConnectionPool.GetAsync(AbpRabbitMqEventBusOptions.ConnectionName)).CreateChannelAsync()) { - return PublishAsync(channel, eventName, body, headersArguments, eventId, correlationId); + await PublishAsync(channel, eventName, body, headersArguments, eventId, correlationId); } } - protected virtual Task PublishAsync( - IModel channel, + protected virtual async Task PublishAsync( + IChannel channel, string eventName, byte[] body, Dictionary? headersArguments = null, Guid? eventId = null, string? correlationId = null) { - EnsureExchangeExists(channel); + await EnsureExchangeExistsAsync(channel); - var properties = channel.CreateBasicProperties(); - properties.DeliveryMode = RabbitMqConsts.DeliveryModes.Persistent; + var properties = new BasicProperties + { + DeliveryMode = DeliveryModes.Persistent + }; if (properties.MessageId.IsNullOrEmpty()) { @@ -331,18 +331,16 @@ public class RabbitMqDistributedEventBus : DistributedEventBusBase, IRabbitMqDis SetEventMessageHeaders(properties, headersArguments); - channel.BasicPublish( + await channel.BasicPublishAsync( exchange: AbpRabbitMqEventBusOptions.ExchangeName, routingKey: eventName, - mandatory: true, + mandatory: false, basicProperties: properties, body: body ); - - return Task.CompletedTask; } - private void EnsureExchangeExists(IModel channel) + protected virtual async Task EnsureExchangeExistsAsync(IChannel channel) { if (_exchangeCreated) { @@ -351,14 +349,14 @@ public class RabbitMqDistributedEventBus : DistributedEventBusBase, IRabbitMqDis try { - using (var temporaryChannel = ConnectionPool.Get(AbpRabbitMqEventBusOptions.ConnectionName).CreateModel()) + using (var temporaryChannel = await (await ConnectionPool.GetAsync(AbpRabbitMqEventBusOptions.ConnectionName)).CreateChannelAsync()) { - temporaryChannel.ExchangeDeclarePassive(AbpRabbitMqEventBusOptions.ExchangeName); + await temporaryChannel.ExchangeDeclarePassiveAsync(AbpRabbitMqEventBusOptions.ExchangeName); } } catch (Exception) { - channel.ExchangeDeclare( + await channel.ExchangeDeclareAsync( AbpRabbitMqEventBusOptions.ExchangeName, AbpRabbitMqEventBusOptions.GetExchangeTypeOrDefault(), durable: true @@ -367,14 +365,14 @@ public class RabbitMqDistributedEventBus : DistributedEventBusBase, IRabbitMqDis _exchangeCreated = true; } - private void SetEventMessageHeaders(IBasicProperties properties, Dictionary? headersArguments) + protected virtual void SetEventMessageHeaders(IBasicProperties properties, Dictionary? headersArguments) { if (headersArguments == null) { return; } - properties.Headers ??= new Dictionary(); + properties.Headers ??= new Dictionary(); foreach (var header in headersArguments) { diff --git a/framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingOptions.cs b/framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingOptions.cs index e3bde2e813..cc751fc621 100644 --- a/framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingOptions.cs +++ b/framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingOptions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace Volo.Abp.AspNetCore.ExceptionHandling; @@ -11,6 +12,12 @@ public class AbpExceptionHandlingOptions public List SendExceptionDataToClientTypes { get; set; } + /// + /// Selectors to exclude exception from logging. + /// If a selector returns true, the exception is not logged in to the logging. + /// + public List> ExcludeExceptionFromLoggerSelectors { get; } + public AbpExceptionHandlingOptions() { SendExceptionsDetailsToClients = false; @@ -19,5 +26,11 @@ public class AbpExceptionHandlingOptions [ typeof(IBusinessException) ]; + ExcludeExceptionFromLoggerSelectors = new List>(); + } + + public bool ShouldLogException(Exception exception) + { + return ExcludeExceptionFromLoggerSelectors.All(selector => !selector(exception)); } } diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyApiDescriptionFinder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyApiDescriptionFinder.cs index b8d2b763df..619cf5685f 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyApiDescriptionFinder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyApiDescriptionFinder.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.FileProviders.Physical; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Modeling; using Volo.Abp.Json; @@ -29,17 +28,17 @@ public class ClientProxyApiDescriptionFinder : IClientProxyApiDescriptionFinder, Initialize(); } - public ActionApiDescriptionModel? FindAction(string methodName) + public virtual ActionApiDescriptionModel? FindAction(string methodName) { - return ActionApiDescriptionModels.ContainsKey(methodName) ? ActionApiDescriptionModels[methodName] : null; + return ActionApiDescriptionModels.TryGetValue(methodName, out var model) ? model : null; } - public ApplicationApiDescriptionModel GetApiDescription() + public virtual ApplicationApiDescriptionModel GetApiDescription() { return ApplicationApiDescriptionModel; } - private void Initialize() + protected virtual void Initialize() { ApplicationApiDescriptionModel = GetApplicationApiDescriptionModel(); var controllers = ApplicationApiDescriptionModel.Modules.Select(x => x.Value).SelectMany(x => x.Controllers.Values).ToList(); @@ -51,7 +50,6 @@ public class ClientProxyApiDescriptionFinder : IClientProxyApiDescriptionFinder, foreach (var actionItem in controller.Actions.Values) { var actionKey = $"{appServiceType}.{actionItem.Name}.{string.Join("-", actionItem.ParametersOnMethod.Select(x => x.Type))}"; - if (!ActionApiDescriptionModels.ContainsKey(actionKey)) { ActionApiDescriptionModels.Add(actionKey, actionItem); @@ -60,7 +58,7 @@ public class ClientProxyApiDescriptionFinder : IClientProxyApiDescriptionFinder, } } - private ApplicationApiDescriptionModel GetApplicationApiDescriptionModel() + protected virtual ApplicationApiDescriptionModel GetApplicationApiDescriptionModel() { var applicationApiDescription = ApplicationApiDescriptionModel.Create(); var fileInfoList = new List(); @@ -87,41 +85,21 @@ public class ClientProxyApiDescriptionFinder : IClientProxyApiDescriptionFinder, return applicationApiDescription; } - private void GetGenerateProxyFileInfos(List fileInfoList, string path = "") + protected virtual void GetGenerateProxyFileInfos(List fileInfoList, string path = "") { - foreach (var directoryContent in VirtualFileProvider.GetDirectoryContents(path)) + foreach (var fileInfo in VirtualFileProvider.GetDirectoryContents(path)) { - if (directoryContent.IsDirectory) + if (fileInfo.IsDirectory) { - GetGenerateProxyFileInfos(fileInfoList, GetDirectoryContentPath(path, directoryContent)); + GetGenerateProxyFileInfos(fileInfoList, path + fileInfo.Name.EnsureStartsWith('/')); } else { - if (directoryContent.Name.EndsWith("generate-proxy.json")) + if (fileInfo.Name.EndsWith("generate-proxy.json", StringComparison.OrdinalIgnoreCase)) { - fileInfoList.Add(VirtualFileProvider.GetFileInfo(GetProxyFileInfoPath(path, directoryContent))); + fileInfoList.Add(fileInfo); } } } } - - private string GetDirectoryContentPath(string rootPath, IFileInfo fileInfo) - { - if (fileInfo is PhysicalDirectoryInfo physicalDirectoryInfo) - { - return rootPath + physicalDirectoryInfo.Name.EnsureStartsWith('/'); - } - - return fileInfo.PhysicalPath!; - } - - private string GetProxyFileInfoPath(string rootPath, IFileInfo fileInfo) - { - if (fileInfo is PhysicalFileInfo physicalFileInfo) - { - return rootPath + physicalFileInfo.Name.EnsureStartsWith('/'); - } - - return fileInfo.GetVirtualOrPhysicalPathOrNull()!; - } } diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs index 01c1e2a720..d09f97b24c 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs @@ -20,6 +20,7 @@ using Volo.Abp.Json; using Volo.Abp.MultiTenancy; using Volo.Abp.Reflection; using Volo.Abp.Threading; +using Volo.Abp.Timing; using Volo.Abp.Tracing; namespace Volo.Abp.Http.Client.ClientProxying; @@ -33,6 +34,7 @@ public class ClientProxyBase : ITransientDependency protected ICorrelationIdProvider CorrelationIdProvider => LazyServiceProvider.LazyGetRequiredService(); protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService(); protected IOptions AbpCorrelationIdOptions => LazyServiceProvider.LazyGetRequiredService>(); + protected ICurrentTimezoneProvider CurrentTimezoneProvider => LazyServiceProvider.LazyGetRequiredService(); protected IProxyHttpClientFactory HttpClientFactory => LazyServiceProvider.LazyGetRequiredService(); protected IRemoteServiceConfigurationProvider RemoteServiceConfigurationProvider => LazyServiceProvider.LazyGetRequiredService(); protected IOptions ClientOptions => LazyServiceProvider.LazyGetRequiredService>(); @@ -337,6 +339,12 @@ public class ClientProxyBase : ITransientDependency //X-Requested-With requestMessage.Headers.Add("X-Requested-With", "XMLHttpRequest"); + + //Timezone + if (!CurrentTimezoneProvider.TimeZone.IsNullOrWhiteSpace()) + { + requestMessage.Headers.Add(TimeZoneConsts.DefaultTimeZoneKey, CurrentTimezoneProvider.TimeZone); + } } protected virtual StringSegment RemoveQuotes(StringSegment input) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs index c24404918e..91fc6f33bf 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs @@ -15,6 +15,7 @@ using Volo.Abp.Http.Client.Proxying; using Volo.Abp.Http.Modeling; using Volo.Abp.Http.ProxyScripting.Generators; using Volo.Abp.Json; +using Volo.Abp.Timing; namespace Volo.Abp.Http.Client.ClientProxying; @@ -33,9 +34,15 @@ public class ClientProxyRequestPayloadBuilder : ITransientDependency protected AbpHttpClientProxyingOptions HttpClientProxyingOptions { get; } - public ClientProxyRequestPayloadBuilder(IServiceScopeFactory serviceScopeFactory, IOptions httpClientProxyingOptions) + protected IClock Clock { get; } + + public ClientProxyRequestPayloadBuilder( + IServiceScopeFactory serviceScopeFactory, + IOptions httpClientProxyingOptions, + IClock clock) { ServiceScopeFactory = serviceScopeFactory; + Clock = clock; HttpClientProxyingOptions = httpClientProxyingOptions.Value; } @@ -156,12 +163,12 @@ public class ClientProxyRequestPayloadBuilder : ITransientDependency { foreach (var item in (IEnumerable) value) { - formData.Add(new StringContent(item.ToString()!, Encoding.UTF8), parameter.Name); + formData.Add(new StringContent(await ConvertValueToStringAsync(item), Encoding.UTF8), parameter.Name); } } else { - formData.Add(new StringContent(value.ToString()!, Encoding.UTF8), parameter.Name); + formData.Add(new StringContent(await ConvertValueToStringAsync(value), Encoding.UTF8), parameter.Name); } } @@ -172,4 +179,19 @@ public class ClientProxyRequestPayloadBuilder : ITransientDependency { return await converter.ConvertAsync(actionApiDescription, parameterApiDescription, value); } + + protected virtual Task ConvertValueToStringAsync(object value) + { + if (value is DateTime dateTimeValue) + { + if (Clock.SupportsMultipleTimezone || dateTimeValue.Kind == DateTimeKind.Utc) + { + return Task.FromResult(dateTimeValue.ToUniversalTime().ToString("O")); + } + + return Task.FromResult(dateTimeValue.ToString("yyyy-MM-ddTHH:mm:ss.fffffff").TrimEnd('0').TrimEnd('.')); + } + + return Task.FromResult(value.ToString()!); + } } diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs index 65703dc56b..5be598af20 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs @@ -14,6 +14,7 @@ using Volo.Abp.Http.Client.Proxying; using Volo.Abp.Http.Modeling; using Volo.Abp.Http.ProxyScripting.Generators; using Volo.Abp.Localization; +using Volo.Abp.Timing; namespace Volo.Abp.Http.Client.ClientProxying; @@ -36,11 +37,16 @@ public class ClientProxyUrlBuilder : ITransientDependency protected IServiceScopeFactory ServiceScopeFactory { get; } protected AbpHttpClientProxyingOptions HttpClientProxyingOptions { get; } + protected IClock Clock { get; } - public ClientProxyUrlBuilder(IServiceScopeFactory serviceScopeFactory, IOptions httpClientProxyingOptions) + public ClientProxyUrlBuilder( + IServiceScopeFactory serviceScopeFactory, + IOptions httpClientProxyingOptions, + IClock clock) { ServiceScopeFactory = serviceScopeFactory; HttpClientProxyingOptions = httpClientProxyingOptions.Value; + Clock = clock; } public async Task GenerateUrlWithParametersAsync(ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) @@ -218,13 +224,18 @@ public class ClientProxyUrlBuilder : ITransientDependency return true; } - protected virtual Task ConvertValueToStringAsync(object? value) + protected virtual Task ConvertValueToStringAsync(object value) { if (value is DateTime dateTimeValue) { - return Task.FromResult(dateTimeValue.ToUniversalTime().ToString("O"))!; + if (Clock.SupportsMultipleTimezone || dateTimeValue.Kind == DateTimeKind.Utc) + { + return Task.FromResult(dateTimeValue.ToUniversalTime().ToString("O")); + } + + return Task.FromResult(dateTimeValue.ToString("yyyy-MM-ddTHH:mm:ss.fffffff").TrimEnd('0').TrimEnd('.')); } - return Task.FromResult(value?.ToString()); + return Task.FromResult(value.ToString()!); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Imaging.ImageSharp/Volo/Abp/Imaging/ImageSharpImageCompressorContributor.cs b/framework/src/Volo.Abp.Imaging.ImageSharp/Volo/Abp/Imaging/ImageSharpImageCompressorContributor.cs index 9a2f53b988..da6b75b4b8 100644 --- a/framework/src/Volo.Abp.Imaging.ImageSharp/Volo/Abp/Imaging/ImageSharpImageCompressorContributor.cs +++ b/framework/src/Volo.Abp.Imaging.ImageSharp/Volo/Abp/Imaging/ImageSharpImageCompressorContributor.cs @@ -5,9 +5,6 @@ using System.Threading.Tasks; using Microsoft.Extensions.Options; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.Formats.Webp; using Volo.Abp.DependencyInjection; using Volo.Abp.Http; @@ -32,7 +29,7 @@ public class ImageSharpImageCompressorContributor : IImageCompressorContributor, return new ImageCompressResult(stream, ImageProcessState.Unsupported); } - var image = await Image.LoadAsync(stream, cancellationToken); + using var image = await Image.LoadAsync(stream, cancellationToken); if (!CanCompress(image.Metadata.DecodedImageFormat!.DefaultMimeType)) { @@ -46,7 +43,7 @@ public class ImageSharpImageCompressorContributor : IImageCompressorContributor, return new ImageCompressResult(memoryStream, ImageProcessState.Done); } - memoryStream.Dispose(); + await memoryStream.DisposeAsync(); return new ImageCompressResult(stream, ImageProcessState.Canceled); } @@ -69,7 +66,7 @@ public class ImageSharpImageCompressorContributor : IImageCompressorContributor, } var newBytes = await result.Result.GetAllBytesAsync(cancellationToken); - result.Result.Dispose(); + await result.Result.DisposeAsync(); return new ImageCompressResult(newBytes, result.State); } @@ -100,7 +97,7 @@ public class ImageSharpImageCompressorContributor : IImageCompressorContributor, } catch { - memoryStream.Dispose(); + await memoryStream.DisposeAsync(); throw; } } @@ -110,11 +107,11 @@ public class ImageSharpImageCompressorContributor : IImageCompressorContributor, switch (format.DefaultMimeType) { case MimeTypes.Image.Jpeg: - return Options.JpegEncoder ?? new JpegEncoder(); + return Options.JpegEncoder; case MimeTypes.Image.Png: - return Options.PngEncoder ?? new PngEncoder(); + return Options.PngEncoder; case MimeTypes.Image.Webp: - return Options.WebpEncoder ?? new WebpEncoder(); + return Options.WebpEncoder; default: throw new NotSupportedException($"No encoder available for the given format: {format.Name}"); } diff --git a/framework/src/Volo.Abp.Imaging.ImageSharp/Volo/Abp/Imaging/ImageSharpImageResizerContributor.cs b/framework/src/Volo.Abp.Imaging.ImageSharp/Volo/Abp/Imaging/ImageSharpImageResizerContributor.cs index 6e17ddc9a8..e52155eaeb 100644 --- a/framework/src/Volo.Abp.Imaging.ImageSharp/Volo/Abp/Imaging/ImageSharpImageResizerContributor.cs +++ b/framework/src/Volo.Abp.Imaging.ImageSharp/Volo/Abp/Imaging/ImageSharpImageResizerContributor.cs @@ -23,7 +23,7 @@ public class ImageSharpImageResizerContributor : IImageResizerContributor, ITran return new ImageResizeResult(stream, ImageProcessState.Unsupported); } - var image = await Image.LoadAsync(stream, cancellationToken); + using var image = await Image.LoadAsync(stream, cancellationToken); if (!CanResize(image.Metadata.DecodedImageFormat!.DefaultMimeType)) { @@ -49,7 +49,7 @@ public class ImageSharpImageResizerContributor : IImageResizerContributor, ITran } catch { - memoryStream.Dispose(); + await memoryStream.DisposeAsync(); throw; } } @@ -76,7 +76,7 @@ public class ImageSharpImageResizerContributor : IImageResizerContributor, ITran var newBytes = await result.Result.GetAllBytesAsync(cancellationToken); - result.Result.Dispose(); + await result.Result.DisposeAsync(); return new ImageResizeResult(newBytes, result.State); } diff --git a/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpImageResizerContributor.cs b/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpImageResizerContributor.cs index 760acd0916..0ba2c7c833 100644 --- a/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpImageResizerContributor.cs +++ b/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpImageResizerContributor.cs @@ -25,21 +25,19 @@ public class SkiaSharpImageResizerContributor : IImageResizerContributor, ITrans return new ImageResizeResult(bytes, ImageProcessState.Unsupported); } - using (var memoryStream = new MemoryStream(bytes)) - { - var result = await TryResizeAsync(memoryStream, resizeArgs, mimeType, cancellationToken); + using var memoryStream = new MemoryStream(bytes); + var result = await TryResizeAsync(memoryStream, resizeArgs, mimeType, cancellationToken); - if (result.State != ImageProcessState.Done) - { - return new ImageResizeResult(bytes, result.State); - } + if (result.State != ImageProcessState.Done) + { + return new ImageResizeResult(bytes, result.State); + } - var newBytes = await result.Result.GetAllBytesAsync(cancellationToken); + var newBytes = await result.Result.GetAllBytesAsync(cancellationToken); - result.Result.Dispose(); + result.Result.Dispose(); - return new ImageResizeResult(newBytes, result.State); - } + return new ImageResizeResult(newBytes, result.State); } public virtual async Task> TryResizeAsync(Stream stream, ImageResizeArgs resizeArgs, string? mimeType = null, CancellationToken cancellationToken = default) @@ -51,21 +49,14 @@ public class SkiaSharpImageResizerContributor : IImageResizerContributor, ITrans var (memoryBitmapStream, memorySkCodecStream) = await CreateMemoryStream(stream); - using (var original = SKBitmap.Decode(memoryBitmapStream)) - { - using (var resized = original.Resize(new SKImageInfo(resizeArgs.Width, resizeArgs.Height), Options.SKFilterQuality)) - { - using (var image = SKImage.FromBitmap(resized)) - { - using (var codec = SKCodec.Create(memorySkCodecStream)) - { - var memoryStream = new MemoryStream(); - image.Encode(codec.EncodedFormat, Options.Quality).SaveTo(memoryStream); - return new ImageResizeResult(memoryStream, ImageProcessState.Done); - } - } - } - } + using var original = SKBitmap.Decode(memoryBitmapStream); + using var resized = original.Resize(new SKImageInfo(resizeArgs.Width, resizeArgs.Height), Options.SKFilterQuality); + using var image = SKImage.FromBitmap(resized); + using var codec = SKCodec.Create(memorySkCodecStream); + var memoryStream = new MemoryStream(); + using var skData = image.Encode(codec.EncodedFormat, Options.Quality); + skData.SaveTo(memoryStream); + return new ImageResizeResult(memoryStream, ImageProcessState.Done); } protected virtual async Task<(MemoryStream, MemoryStream)> CreateMemoryStream(Stream stream) diff --git a/framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpDateTimeConverter.cs b/framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpDateTimeConverter.cs index effda40057..c95660e484 100644 --- a/framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpDateTimeConverter.cs +++ b/framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpDateTimeConverter.cs @@ -19,6 +19,7 @@ public class AbpDateTimeConverter : DateTimeConverterBase, ITransientDependency private readonly CultureInfo _culture = CultureInfo.InvariantCulture; private readonly IClock _clock; private readonly AbpJsonOptions _options; + private bool _skipDateTimeNormalization; public AbpDateTimeConverter(IClock clock, IOptions options) { @@ -26,6 +27,12 @@ public class AbpDateTimeConverter : DateTimeConverterBase, ITransientDependency _options = options.Value; } + public virtual AbpDateTimeConverter SkipDateTimeNormalization() + { + _skipDateTimeNormalization = true; + return this; + } + public override bool CanConvert(Type objectType) { return objectType == typeof(DateTime) || objectType == typeof(DateTime?); @@ -46,7 +53,7 @@ public class AbpDateTimeConverter : DateTimeConverterBase, ITransientDependency if (reader.TokenType == JsonToken.Date) { - return _clock.Normalize(reader.Value!.To()); + return Normalize(reader.Value!.To()); } if (reader.TokenType != JsonToken.String) @@ -67,20 +74,20 @@ public class AbpDateTimeConverter : DateTimeConverterBase, ITransientDependency { if (DateTime.TryParseExact(dateText, format, _culture, _dateTimeStyles, out var d1)) { - return _clock.Normalize(d1); + return Normalize(d1); } } } var date = DateTime.Parse(dateText!, _culture, _dateTimeStyles); - return _clock.Normalize(date); + return Normalize(date); } public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if (value != null) { - value = _clock.Normalize(value.To()); + value = Normalize(value.To()); } if (value is DateTime dateTime) @@ -111,4 +118,11 @@ public class AbpDateTimeConverter : DateTimeConverterBase, ITransientDependency return ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(member) == null; } + + protected virtual DateTime Normalize(DateTime dateTime) + { + return _skipDateTimeNormalization + ? dateTime + : _clock.Normalize(dateTime); + } } diff --git a/framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpJsonNewtonsoftModule.cs b/framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpJsonNewtonsoftModule.cs index 6f93a6c54e..ea35831d86 100644 --- a/framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpJsonNewtonsoftModule.cs +++ b/framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpJsonNewtonsoftModule.cs @@ -13,7 +13,8 @@ public class AbpJsonNewtonsoftModule : AbpModule context.Services.AddOptions() .Configure((options, rootServiceProvider) => { - options.JsonSerializerSettings.ContractResolver = new AbpCamelCasePropertyNamesContractResolver(rootServiceProvider.GetRequiredService()); + options.JsonSerializerSettings.ContractResolver = new AbpCamelCasePropertyNamesContractResolver( + rootServiceProvider.GetRequiredService().SkipDateTimeNormalization()); }); } } diff --git a/framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpNewtonsoftJsonSerializer.cs b/framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpNewtonsoftJsonSerializer.cs index 0450064f2f..8184ee6d45 100644 --- a/framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpNewtonsoftJsonSerializer.cs +++ b/framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpNewtonsoftJsonSerializer.cs @@ -86,7 +86,8 @@ public class AbpNewtonsoftJsonSerializer : IJsonSerializer, ITransientDependency if (!camelCase) { //Default contract resolver is AbpCamelCasePropertyNamesContractResolver} - settings.ContractResolver = new AbpDefaultContractResolver(RootServiceProvider.GetRequiredService()); + settings.ContractResolver = new AbpDefaultContractResolver(RootServiceProvider + .GetRequiredService().SkipDateTimeNormalization()); } if (indented) diff --git a/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpJsonSystemTextJsonModule.cs b/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpJsonSystemTextJsonModule.cs index 498dedd8cd..0a5066cec1 100644 --- a/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpJsonSystemTextJsonModule.cs +++ b/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpJsonSystemTextJsonModule.cs @@ -29,14 +29,13 @@ public class AbpJsonSystemTextJsonModule : AbpModule options.JsonSerializerOptions.TypeInfoResolver = new AbpDefaultJsonTypeInfoResolver(rootServiceProvider .GetRequiredService>()); - }); - context.Services.AddOptions() - .Configure((options, rootServiceProvider) => - { - options.Modifiers.Add(new AbpDateTimeConverterModifier( - rootServiceProvider.GetRequiredService(), - rootServiceProvider.GetRequiredService()).CreateModifyAction()); + var dateTimeConverter = rootServiceProvider.GetRequiredService().SkipDateTimeNormalization(); + var nullableDateTimeConverter = rootServiceProvider.GetRequiredService().SkipDateTimeNormalization(); + + options.JsonSerializerOptions.TypeInfoResolver.As().Modifiers.Add( + new AbpDateTimeConverterModifier(dateTimeConverter, nullableDateTimeConverter) + .CreateModifyAction()); }); } } diff --git a/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializer.cs b/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializer.cs index 6f433ce918..7f72f0de71 100644 --- a/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializer.cs +++ b/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializer.cs @@ -40,21 +40,10 @@ public class AbpSystemTextJsonSerializer : IJsonSerializer, ITransientDependency camelCase, indented, Options.JsonSerializerOptions - }, _ => + }, _ => new JsonSerializerOptions(Options.JsonSerializerOptions) { - var settings = new JsonSerializerOptions(Options.JsonSerializerOptions); - - if (camelCase) - { - settings.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; - } - - if (indented) - { - settings.WriteIndented = true; - } - - return settings; + PropertyNamingPolicy = camelCase ? JsonNamingPolicy.CamelCase : null, + WriteIndented = indented }); } } diff --git a/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs b/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs index a8653bb54a..3feea8d299 100644 --- a/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs +++ b/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs @@ -13,6 +13,7 @@ public class AbpDateTimeConverter : JsonConverter, ITransientDependenc { private readonly IClock _clock; private readonly AbpJsonOptions _options; + private bool _skipDateTimeNormalization; public AbpDateTimeConverter(IClock clock, IOptions abpJsonOptions) { @@ -20,6 +21,12 @@ public class AbpDateTimeConverter : JsonConverter, ITransientDependenc _options = abpJsonOptions.Value; } + public virtual AbpDateTimeConverter SkipDateTimeNormalization() + { + _skipDateTimeNormalization = true; + return this; + } + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (_options.InputDateTimeFormats.Any()) @@ -31,7 +38,7 @@ public class AbpDateTimeConverter : JsonConverter, ITransientDependenc var s = reader.GetString(); if (DateTime.TryParseExact(s, format, CultureInfo.CurrentUICulture, DateTimeStyles.None, out var d1)) { - return _clock.Normalize(d1); + return Normalize(d1); } } } @@ -43,7 +50,7 @@ public class AbpDateTimeConverter : JsonConverter, ITransientDependenc if (reader.TryGetDateTime(out var d3)) { - return _clock.Normalize(d3); + return Normalize(d3); } var dateText = reader.GetString(); @@ -51,7 +58,7 @@ public class AbpDateTimeConverter : JsonConverter, ITransientDependenc { if (DateTime.TryParse(dateText, CultureInfo.CurrentUICulture, DateTimeStyles.None, out var d4)) { - return _clock.Normalize(d4); + return Normalize(d4); } } @@ -62,11 +69,16 @@ public class AbpDateTimeConverter : JsonConverter, ITransientDependenc { if (_options.OutputDateTimeFormat.IsNullOrWhiteSpace()) { - writer.WriteStringValue(_clock.Normalize(value)); + writer.WriteStringValue(Normalize(value)); } else { - writer.WriteStringValue(_clock.Normalize(value).ToString(_options.OutputDateTimeFormat, CultureInfo.CurrentUICulture)); + writer.WriteStringValue(Normalize(value).ToString(_options.OutputDateTimeFormat, CultureInfo.CurrentUICulture)); } } + + protected virtual DateTime Normalize(DateTime dateTime) + { + return _skipDateTimeNormalization ? dateTime : _clock.Normalize(dateTime); + } } diff --git a/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs b/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs index e8a94892da..e73f39d097 100644 --- a/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs +++ b/framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs @@ -13,6 +13,7 @@ public class AbpNullableDateTimeConverter : JsonConverter, ITransient { private readonly IClock _clock; private readonly AbpJsonOptions _options; + private bool _skipDateTimeNormalization; public AbpNullableDateTimeConverter(IClock clock, IOptions abpJsonOptions) { @@ -20,6 +21,12 @@ public class AbpNullableDateTimeConverter : JsonConverter, ITransient _options = abpJsonOptions.Value; } + public virtual AbpNullableDateTimeConverter SkipDateTimeNormalization() + { + _skipDateTimeNormalization = true; + return this; + } + public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (_options.InputDateTimeFormats.Any()) @@ -31,7 +38,7 @@ public class AbpNullableDateTimeConverter : JsonConverter, ITransient var s = reader.GetString(); if (DateTime.TryParseExact(s, format, CultureInfo.CurrentUICulture, DateTimeStyles.None, out var d1)) { - return _clock.Normalize(d1); + return Normalize(d1); } } } @@ -43,7 +50,7 @@ public class AbpNullableDateTimeConverter : JsonConverter, ITransient if (reader.TryGetDateTime(out var d2)) { - return _clock.Normalize(d2); + return Normalize(d2); } var dateText = reader.GetString(); @@ -51,7 +58,7 @@ public class AbpNullableDateTimeConverter : JsonConverter, ITransient { if (DateTime.TryParse(dateText, CultureInfo.CurrentUICulture, DateTimeStyles.None, out var d3)) { - return _clock.Normalize(d3); + return Normalize(d3); } } @@ -68,12 +75,17 @@ public class AbpNullableDateTimeConverter : JsonConverter, ITransient { if (_options.OutputDateTimeFormat.IsNullOrWhiteSpace()) { - writer.WriteStringValue(_clock.Normalize(value.Value)); + writer.WriteStringValue(Normalize(value.Value)); } else { - writer.WriteStringValue(_clock.Normalize(value.Value).ToString(_options.OutputDateTimeFormat, CultureInfo.CurrentUICulture)); + writer.WriteStringValue(Normalize(value.Value).ToString(_options.OutputDateTimeFormat, CultureInfo.CurrentUICulture)); } } } + + protected virtual DateTime Normalize(DateTime dateTime) + { + return _skipDateTimeNormalization ? dateTime : _clock.Normalize(dateTime); + } } diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/VirtualFiles/VirtualFileLocalizationResourceContributorBase.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/VirtualFiles/VirtualFileLocalizationResourceContributorBase.cs index b25bd9ec6e..d151dc78df 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/VirtualFiles/VirtualFileLocalizationResourceContributorBase.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/VirtualFiles/VirtualFileLocalizationResourceContributorBase.cs @@ -103,7 +103,7 @@ public abstract class VirtualFileLocalizationResourceContributorBase : ILocaliza { continue; } - + if (dictionaries.ContainsKey(dictionary.CultureName)) { throw new AbpException($"{file.GetVirtualOrPhysicalPathOrNull()} dictionary has a culture name '{dictionary.CultureName}' which is already defined! Localization resource: {_resource.ResourceName}"); @@ -121,7 +121,14 @@ public abstract class VirtualFileLocalizationResourceContributorBase : ILocaliza { using (var stream = file.CreateReadStream()) { - return CreateDictionaryFromFileContent(Utf8Helper.ReadStringFromStream(stream)); + try + { + return CreateDictionaryFromFileContent(Utf8Helper.ReadStringFromStream(stream)); + } + catch (Exception e) + { + throw new AbpException("Invalid localization file format: " + (file.GetVirtualOrPhysicalPathOrNull() ?? file.Name), e); + } } } diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpGuidCustomBsonTypeMapper.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpGuidCustomBsonTypeMapper.cs new file mode 100644 index 0000000000..4cfacc39fd --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpGuidCustomBsonTypeMapper.cs @@ -0,0 +1,13 @@ +using System; +using MongoDB.Bson; + +namespace Volo.Abp.MongoDB; + +public class AbpGuidCustomBsonTypeMapper : ICustomBsonTypeMapper +{ + public bool TryMapToBsonValue(object value, out BsonValue bsonValue) + { + bsonValue = new BsonBinaryData((Guid)value, GuidRepresentation.Standard); + return true; + } +} diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs index 91d2808010..195491d989 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs @@ -1,9 +1,11 @@ -using Microsoft.Extensions.DependencyInjection; +using System; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using Volo.Abp.Domain; +using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.Domain.Repositories.MongoDB; using Volo.Abp.Modularity; using Volo.Abp.MongoDB.DependencyInjection; @@ -15,6 +17,12 @@ namespace Volo.Abp.MongoDB; [DependsOn(typeof(AbpDddDomainModule))] public class AbpMongoDbModule : AbpModule { + static AbpMongoDbModule() + { + BsonSerializer.TryRegisterSerializer(new GuidSerializer(GuidRepresentation.Standard)); + BsonTypeMapper.RegisterCustomTypeMapper(typeof(Guid), new AbpGuidCustomBsonTypeMapper()); + } + public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.AddConventionalRegistrar(new AbpMongoDbConventionalRegistrar()); @@ -22,8 +30,6 @@ public class AbpMongoDbModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { - BsonSerializer.TryRegisterSerializer(new GuidSerializer(GuidRepresentation.Standard)); - context.Services.TryAddTransient( typeof(IMongoDbContextProvider<>), typeof(UnitOfWorkMongoDbContextProvider<>) @@ -48,5 +54,11 @@ public class AbpMongoDbModule : AbpModule typeof(IMongoDbContextEventInbox<>), typeof(MongoDbContextEventInbox<>) ); + + Configure(options => + { + options.IgnoredEventSelectors.Add(); + options.IgnoredEventSelectors.Add(); + }); } } diff --git a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/AbpRabbitMqModule.cs b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/AbpRabbitMqModule.cs index da010b83c9..74b41dea23 100644 --- a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/AbpRabbitMqModule.cs +++ b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/AbpRabbitMqModule.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Json; using Volo.Abp.Modularity; using Volo.Abp.Threading; @@ -19,7 +20,6 @@ public class AbpRabbitMqModule : AbpModule { foreach (var connectionFactory in options.Connections.Values) { - connectionFactory.DispatchConsumersAsync = true; connectionFactory.AutomaticRecoveryEnabled = false; } }); @@ -27,12 +27,17 @@ public class AbpRabbitMqModule : AbpModule public override void OnApplicationShutdown(ApplicationShutdownContext context) { - context.ServiceProvider + AsyncHelper.RunSync(() => OnApplicationShutdownAsync(context)); + } + + public async override Task OnApplicationShutdownAsync(ApplicationShutdownContext context) + { + await context.ServiceProvider .GetRequiredService() - .Dispose(); + .DisposeAsync(); - context.ServiceProvider + await context.ServiceProvider .GetRequiredService() - .Dispose(); + .DisposeAsync(); } } diff --git a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ChannelPool.cs b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ChannelPool.cs index b7ed4c80c8..03d6029b69 100644 --- a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ChannelPool.cs +++ b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ChannelPool.cs @@ -3,10 +3,12 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Linq; using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using RabbitMQ.Client; using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; namespace Volo.Abp.RabbitMQ; @@ -16,6 +18,8 @@ public class ChannelPool : IChannelPool, ISingletonDependency protected ConcurrentDictionary Channels { get; } + protected SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1); + protected bool IsDisposed { get; private set; } protected TimeSpan TotalDisposeWaitDuration { get; set; } = TimeSpan.FromSeconds(10); @@ -29,28 +33,54 @@ public class ChannelPool : IChannelPool, ISingletonDependency Logger = NullLogger.Instance; } - public virtual IChannelAccessor Acquire(string? channelName = null, string? connectionName = null) + public virtual async Task AcquireAsync(string? channelName = null, string? connectionName = null) { CheckDisposed(); channelName = channelName ?? ""; - var poolItem = Channels.GetOrAdd( - channelName, - _ => new ChannelPoolItem(CreateChannel(channelName, connectionName)) - ); + ChannelPoolItem poolItem; + + if (Channels.TryGetValue(channelName, out var existingChannelPoolItem)) + { + poolItem = existingChannelPoolItem; + } + else + { + using (await Semaphore.LockAsync()) + { + if (Channels.TryGetValue(channelName, out var existingChannelPoolItem2)) + { + poolItem = existingChannelPoolItem2; + } + else + { + poolItem = new ChannelPoolItem(await CreateChannelAsync(channelName, connectionName)); + Channels.TryAdd(channelName, poolItem); + } + } + } poolItem.Acquire(); if (poolItem.Channel.IsClosed) { - poolItem.Dispose(); + await poolItem.DisposeAsync(); Channels.TryRemove(channelName, out _); - poolItem = Channels.GetOrAdd( - channelName, - _ => new ChannelPoolItem(CreateChannel(channelName, connectionName)) - ); - + + using (await Semaphore.LockAsync()) + { + if (Channels.TryGetValue(channelName, out var existingChannelPoolItem3)) + { + poolItem = existingChannelPoolItem3; + } + else + { + poolItem = new ChannelPoolItem(await CreateChannelAsync(channelName, connectionName)); + Channels.TryAdd(channelName, poolItem); + } + } + poolItem.Acquire(); } @@ -61,14 +91,14 @@ public class ChannelPool : IChannelPool, ISingletonDependency ); } - protected virtual IModel CreateChannel(string channelName, string? connectionName) + protected virtual async Task CreateChannelAsync(string channelName, string? connectionName) { - return ConnectionPool - .Get(connectionName) - .CreateModel(); + return await (await ConnectionPool + .GetAsync(connectionName)) + .CreateChannelAsync(); } - protected void CheckDisposed() + protected virtual void CheckDisposed() { if (IsDisposed) { @@ -76,7 +106,7 @@ public class ChannelPool : IChannelPool, ISingletonDependency } } - public void Dispose() + public async ValueTask DisposeAsync() { if (IsDisposed) { @@ -104,10 +134,12 @@ public class ChannelPool : IChannelPool, ISingletonDependency try { poolItem.WaitIfInUse(remainingWaitDuration); - poolItem.Dispose(); + await poolItem.DisposeAsync(); } catch - { } + { + // ignored + } poolItemDisposeStopwatch.Stop(); @@ -128,9 +160,9 @@ public class ChannelPool : IChannelPool, ISingletonDependency Channels.Clear(); } - protected class ChannelPoolItem : IDisposable + protected class ChannelPoolItem : IAsyncDisposable { - public IModel Channel { get; } + public IChannel Channel { get; } public bool IsInUse { get => _isInUse; @@ -138,7 +170,7 @@ public class ChannelPool : IChannelPool, ISingletonDependency } private volatile bool _isInUse; - public ChannelPoolItem(IModel channel) + public ChannelPoolItem(IChannel channel) { Channel = channel; } @@ -178,21 +210,21 @@ public class ChannelPool : IChannelPool, ISingletonDependency } } - public void Dispose() + public async ValueTask DisposeAsync() { - Channel.Dispose(); + await Channel.DisposeAsync(); } } protected class ChannelAccessor : IChannelAccessor { - public IModel Channel { get; } + public IChannel Channel { get; } public string Name { get; } private readonly Action _disposeAction; - public ChannelAccessor(IModel channel, string name, Action disposeAction) + public ChannelAccessor(IChannel channel, string name, Action disposeAction) { _disposeAction = disposeAction; Name = name; diff --git a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs index 5f4556b96a..888de64613 100644 --- a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs +++ b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Options; using RabbitMQ.Client; using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; namespace Volo.Abp.RabbitMQ; @@ -11,55 +13,51 @@ public class ConnectionPool : IConnectionPool, ISingletonDependency { protected AbpRabbitMqOptions Options { get; } - protected ConcurrentDictionary> Connections { get; } + protected ConcurrentDictionary Connections { get; } + + protected SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1); private bool _isDisposed; public ConnectionPool(IOptions options) { Options = options.Value; - Connections = new ConcurrentDictionary>(); + Connections = new ConcurrentDictionary(); } - public virtual IConnection Get(string? connectionName = null) + public virtual async Task GetAsync(string? connectionName = null) { - connectionName ??= RabbitMqConnections.DefaultConnectionName; - var connectionFactory = Options.Connections.GetOrDefault(connectionName); - try + using (await Semaphore.LockAsync()) { - var connection = GetConnection(connectionName, connectionFactory); - - if (connection.IsOpen) + connectionName ??= RabbitMqConnections.DefaultConnectionName; + + if (Connections.TryGetValue(connectionName, out var existingConnection) && existingConnection.IsOpen) { - return connection; + return existingConnection; } - - connection.Dispose(); - Connections.TryRemove(connectionName, out _); - return GetConnection(connectionName, connectionFactory); - } - catch (Exception) - { - Connections.TryRemove(connectionName, out _); - throw; + + if(existingConnection != null) + { + await existingConnection.DisposeAsync(); + } + + var connectionFactory = Options.Connections.GetOrDefault(connectionName); + var connection = await GetConnectionAsync(connectionName, connectionFactory); + Connections[connectionName] = connection; + return connection; } } - protected virtual IConnection GetConnection(string connectionName, ConnectionFactory connectionFactory) + protected virtual async Task GetConnectionAsync(string connectionName, ConnectionFactory connectionFactory) { - return Connections.GetOrAdd( - connectionName, () => new Lazy(() => - { - var hostnames = connectionFactory.HostName.TrimEnd(';').Split(';'); - // Handle Rabbit MQ Cluster. - return hostnames.Length == 1 - ? connectionFactory.CreateConnection() - : connectionFactory.CreateConnection(hostnames); - }) - ).Value; + var hostnames = connectionFactory.HostName.TrimEnd(';').Split(';'); + // Handle Rabbit MQ Cluster. + return hostnames.Length == 1 + ? await connectionFactory.CreateConnectionAsync() + : await connectionFactory.CreateConnectionAsync(hostnames); } - public void Dispose() + public async ValueTask DisposeAsync() { if (_isDisposed) { @@ -72,11 +70,11 @@ public class ConnectionPool : IConnectionPool, ISingletonDependency { try { - connection.Value.Dispose(); + await connection.DisposeAsync(); } catch { - + // ignored } } diff --git a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ExchangeDeclareConfiguration.cs b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ExchangeDeclareConfiguration.cs index 5c239893c7..4a5c0f01d0 100644 --- a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ExchangeDeclareConfiguration.cs +++ b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ExchangeDeclareConfiguration.cs @@ -12,19 +12,19 @@ public class ExchangeDeclareConfiguration public bool AutoDelete { get; set; } - public IDictionary Arguments { get; } + public IDictionary Arguments { get; } public ExchangeDeclareConfiguration( string exchangeName, string type, bool durable = false, bool autoDelete = false, - IDictionary? arguments = null) + IDictionary? arguments = null) { ExchangeName = exchangeName; Type = type; Durable = durable; AutoDelete = autoDelete; - Arguments = arguments?? new Dictionary(); + Arguments = arguments?? new Dictionary(); } } diff --git a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IChannelAccessor.cs b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IChannelAccessor.cs index ad8829f8ee..d4eb954a67 100644 --- a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IChannelAccessor.cs +++ b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IChannelAccessor.cs @@ -10,7 +10,7 @@ public interface IChannelAccessor : IDisposable /// Never dispose the object. /// Instead, dispose the after usage. /// - IModel Channel { get; } + IChannel Channel { get; } /// /// Name of the channel. diff --git a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IChannelPool.cs b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IChannelPool.cs index 2ba6259fec..5a9611ad86 100644 --- a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IChannelPool.cs +++ b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IChannelPool.cs @@ -1,8 +1,9 @@ using System; +using System.Threading.Tasks; namespace Volo.Abp.RabbitMQ; -public interface IChannelPool : IDisposable +public interface IChannelPool : IAsyncDisposable { - IChannelAccessor Acquire(string? channelName = null, string? connectionName = null); + Task AcquireAsync(string? channelName = null, string? connectionName = null); } diff --git a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IConnectionPool.cs b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IConnectionPool.cs index dc97476c84..02fdf286d9 100644 --- a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IConnectionPool.cs +++ b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IConnectionPool.cs @@ -1,9 +1,10 @@ using System; +using System.Threading.Tasks; using RabbitMQ.Client; namespace Volo.Abp.RabbitMQ; -public interface IConnectionPool : IDisposable +public interface IConnectionPool : IAsyncDisposable { - IConnection Get(string? connectionName = null); + Task GetAsync(string? connectionName = null); } diff --git a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IRabbitMqMessageConsumer.cs b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IRabbitMqMessageConsumer.cs index a24ad69a4a..8501b8958f 100644 --- a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IRabbitMqMessageConsumer.cs +++ b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/IRabbitMqMessageConsumer.cs @@ -11,5 +11,5 @@ public interface IRabbitMqMessageConsumer Task UnbindAsync(string routingKey); - void OnMessageReceived(Func callback); + void OnMessageReceived(Func callback); } diff --git a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/QueueDeclareConfiguration.cs b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/QueueDeclareConfiguration.cs index eb59ec3058..ebb2c3e1cc 100644 --- a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/QueueDeclareConfiguration.cs +++ b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/QueueDeclareConfiguration.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; using JetBrains.Annotations; using RabbitMQ.Client; @@ -13,10 +14,10 @@ public class QueueDeclareConfiguration public bool Exclusive { get; set; } public bool AutoDelete { get; set; } - - public ushort? PrefetchCount { get; set; } - public IDictionary Arguments { get; } + public ushort? PrefetchCount { get; set; } + + public IDictionary Arguments { get; } public QueueDeclareConfiguration( [NotNull] string queueName, @@ -24,19 +25,19 @@ public class QueueDeclareConfiguration bool exclusive = false, bool autoDelete = false, ushort? prefetchCount = null, - IDictionary? arguments = null) + IDictionary? arguments = null) { QueueName = queueName; Durable = durable; Exclusive = exclusive; AutoDelete = autoDelete; - Arguments = arguments?? new Dictionary(); + Arguments = arguments?? new Dictionary(); PrefetchCount = prefetchCount; } - public virtual QueueDeclareOk Declare(IModel channel) + public virtual async Task DeclareAsync(IChannel channel) { - return channel.QueueDeclare( + return await channel.QueueDeclareAsync( queue: QueueName, durable: Durable, exclusive: Exclusive, diff --git a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqMessageConsumer.cs b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqMessageConsumer.cs index 6f361f7670..74dac68a61 100644 --- a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqMessageConsumer.cs +++ b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqMessageConsumer.cs @@ -5,6 +5,7 @@ using RabbitMQ.Client; using RabbitMQ.Client.Events; using System; using System.Collections.Concurrent; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.ExceptionHandling; @@ -28,13 +29,13 @@ public class RabbitMqMessageConsumer : IRabbitMqMessageConsumer, ITransientDepen protected string? ConnectionName { get; private set; } - protected ConcurrentBag> Callbacks { get; } + protected ConcurrentBag> Callbacks { get; } - protected IModel? Channel { get; private set; } + protected IChannel? Channel { get; private set; } protected ConcurrentQueue QueueBindCommands { get; } - protected object ChannelSendSyncLock { get; } = new object(); + protected SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1); public RabbitMqMessageConsumer( IConnectionPool connectionPool, @@ -47,7 +48,7 @@ public class RabbitMqMessageConsumer : IRabbitMqMessageConsumer, ITransientDepen Logger = NullLogger.Instance; QueueBindCommands = new ConcurrentQueue(); - Callbacks = new ConcurrentBag>(); + Callbacks = new ConcurrentBag>(); Timer.Period = 5000; //5 sec. Timer.Elapsed = Timer_Elapsed; @@ -88,21 +89,21 @@ public class RabbitMqMessageConsumer : IRabbitMqMessageConsumer, ITransientDepen return; } - lock (ChannelSendSyncLock) + using (await Semaphore.LockAsync()) { if (QueueBindCommands.TryPeek(out var command)) { switch (command.Type) { case QueueBindType.Bind: - Channel.QueueBind( + await Channel.QueueBindAsync( queue: Queue.QueueName, exchange: Exchange.ExchangeName, routingKey: command.RoutingKey ); break; case QueueBindType.Unbind: - Channel.QueueUnbind( + await Channel.QueueUnbindAsync( queue: Queue.QueueName, exchange: Exchange.ExchangeName, routingKey: command.RoutingKey @@ -124,7 +125,7 @@ public class RabbitMqMessageConsumer : IRabbitMqMessageConsumer, ITransientDepen } } - public virtual void OnMessageReceived(Func callback) + public virtual void OnMessageReceived(Func callback) { Callbacks.Add(callback); } @@ -144,11 +145,11 @@ public class RabbitMqMessageConsumer : IRabbitMqMessageConsumer, ITransientDepen try { - Channel = ConnectionPool - .Get(ConnectionName) - .CreateModel(); + Channel = await (await ConnectionPool + .GetAsync(ConnectionName)) + .CreateChannelAsync(); - Channel.ExchangeDeclare( + await Channel.ExchangeDeclareAsync( exchange: Exchange.ExchangeName, type: Exchange.Type, durable: Exchange.Durable, @@ -156,7 +157,7 @@ public class RabbitMqMessageConsumer : IRabbitMqMessageConsumer, ITransientDepen arguments: Exchange.Arguments ); - Channel.QueueDeclare( + await Channel.QueueDeclareAsync( queue: Queue.QueueName, durable: Queue.Durable, exclusive: Queue.Exclusive, @@ -166,13 +167,13 @@ public class RabbitMqMessageConsumer : IRabbitMqMessageConsumer, ITransientDepen if (Queue.PrefetchCount.HasValue) { - Channel.BasicQos(0, Queue.PrefetchCount.Value, false); + await Channel.BasicQosAsync(0, Queue.PrefetchCount.Value, false); } - + var consumer = new AsyncEventingBasicConsumer(Channel); - consumer.Received += HandleIncomingMessageAsync; - - Channel.BasicConsume( + consumer.ReceivedAsync += HandleIncomingMessageAsync; + + await Channel.BasicConsumeAsync( queue: Queue.QueueName, autoAck: false, consumer: consumer @@ -194,17 +195,23 @@ public class RabbitMqMessageConsumer : IRabbitMqMessageConsumer, ITransientDepen await callback(Channel!, basicDeliverEventArgs); } - Channel?.BasicAck(basicDeliverEventArgs.DeliveryTag, multiple: false); + if (Channel != null) + { + await Channel.BasicAckAsync(basicDeliverEventArgs.DeliveryTag, multiple: false); + } } catch (Exception ex) { try { - Channel?.BasicNack( - basicDeliverEventArgs.DeliveryTag, - multiple: false, - requeue: true - ); + if (Channel != null) + { + await Channel.BasicNackAsync( + basicDeliverEventArgs.DeliveryTag, + multiple: false, + requeue: true + ); + } } // ReSharper disable once EmptyGeneralCatchClause catch { } diff --git a/framework/src/Volo.Abp.Swashbuckle/Volo.Abp.Swashbuckle.csproj b/framework/src/Volo.Abp.Swashbuckle/Volo.Abp.Swashbuckle.csproj index 58c2dc3693..d62ba9c65d 100644 --- a/framework/src/Volo.Abp.Swashbuckle/Volo.Abp.Swashbuckle.csproj +++ b/framework/src/Volo.Abp.Swashbuckle/Volo.Abp.Swashbuckle.csproj @@ -30,6 +30,8 @@ + + diff --git a/framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/oauth2-redirect.html b/framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/oauth2-redirect.html new file mode 100644 index 0000000000..ef47009856 --- /dev/null +++ b/framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/oauth2-redirect.html @@ -0,0 +1,39 @@ + + + + Swagger UI: OAuth2 Redirect + + + + + diff --git a/framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/abp.js b/framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/abp.js index 0e13317cbf..11cbe56803 100644 --- a/framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/abp.js +++ b/framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/abp.js @@ -4,7 +4,9 @@ var abp = abp || {}; /* Application paths *****************************************/ //Current application root path (including virtual directory if exists). - abp.appPath = abp.appPath || '/'; + var baseElement = document.querySelector('base'); + var baseHref = baseElement ? baseElement.getAttribute('href') : null; + abp.appPath = baseHref || abp.appPath || '/'; /* UTILS ***************************************************/ diff --git a/framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/abp.swagger.js b/framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/abp.swagger.js index b8abb0df25..db054056d1 100644 --- a/framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/abp.swagger.js +++ b/framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/abp.swagger.js @@ -1,9 +1,9 @@ var abp = abp || {}; (function () { - + var oldSwaggerUIBundle = SwaggerUIBundle; - + SwaggerUIBundle = function (configObject) { var excludeUrl = ["swagger.json", "connect/token"] var firstRequest = true; @@ -112,7 +112,57 @@ var abp = abp || {}; return oldSwaggerUIBundle(configObject); } - + SwaggerUIBundle = Object.assign(SwaggerUIBundle, oldSwaggerUIBundle); - + + window.addEventListener("storage", function (event) { + if (event.key !== "abp_swagger_oauth2" || !event.newValue) { + return; + } + + var qp = JSON.parse(event.newValue || "{}"); + localStorage.removeItem("abp_swagger_oauth2"); + var oauth2 = window.swaggerUIRedirectOauth2; + var sentState = oauth2.state; + var redirectUrl = oauth2.redirectUrl; + var isValid = qp.state === sentState; + + if (( + oauth2.auth.schema.get("flow") === "accessCode" || + oauth2.auth.schema.get("flow") === "authorizationCode" || + oauth2.auth.schema.get("flow") === "authorization_code" + ) && !oauth2.auth.code) { + if (!isValid) { + oauth2.errCb({ + authId: oauth2.auth.name, + source: "auth", + level: "warning", + message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server." + }); + } + + if (qp.code) { + delete oauth2.state; + oauth2.auth.code = qp.code; + oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl}); + } else { + let oauthErrorMsg; + if (qp.error) { + oauthErrorMsg = "["+qp.error+"]: " + + (qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") + + (qp.error_uri ? "More info: "+qp.error_uri : ""); + } + + oauth2.errCb({ + authId: oauth2.auth.name, + source: "auth", + level: "error", + message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server." + }); + } + } else { + oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl}); + } + }); + })(); diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Clock.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Clock.cs index 8788a2b5db..36bee9e8fd 100644 --- a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Clock.cs +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Clock.cs @@ -7,9 +7,16 @@ namespace Volo.Abp.Timing; public class Clock : IClock, ITransientDependency { protected AbpClockOptions Options { get; } + protected ICurrentTimezoneProvider CurrentTimezoneProvider { get; } + protected ITimezoneProvider TimezoneProvider { get; } - public Clock(IOptions options) + public Clock( + IOptions options, + ICurrentTimezoneProvider currentTimezoneProvider, + ITimezoneProvider timezoneProvider) { + CurrentTimezoneProvider = currentTimezoneProvider; + TimezoneProvider = timezoneProvider; Options = options.Value; } @@ -19,6 +26,11 @@ public class Clock : IClock, ITransientDependency public virtual bool SupportsMultipleTimezone => Options.Kind == DateTimeKind.Utc; + /// + /// Normalizes given . + /// + /// DateTime to be normalized. + /// Normalized DateTime public virtual DateTime Normalize(DateTime dateTime) { if (Kind == DateTimeKind.Unspecified || Kind == dateTime.Kind) @@ -38,4 +50,58 @@ public class Clock : IClock, ITransientDependency return DateTime.SpecifyKind(dateTime, Kind); } + + /// + /// Converts given UTC to user's time zone. + /// + /// DateTime to be normalized. + /// Converted DateTime + public virtual DateTime ConvertToUserTime(DateTime utcDateTime) + { + if (!SupportsMultipleTimezone || + utcDateTime.Kind != DateTimeKind.Utc || + CurrentTimezoneProvider.TimeZone.IsNullOrWhiteSpace()) + { + return utcDateTime; + } + + var timezoneInfo = TimezoneProvider.GetTimeZoneInfo(CurrentTimezoneProvider.TimeZone); + return TimeZoneInfo.ConvertTime(utcDateTime, timezoneInfo); + } + + /// + /// Converts given to user's time zone. + /// + /// DateTimeOffset to be normalized. + /// Converted DateTimeOffset + public virtual DateTimeOffset ConvertToUserTime(DateTimeOffset dateTimeOffset) + { + if (!SupportsMultipleTimezone || + CurrentTimezoneProvider.TimeZone.IsNullOrWhiteSpace()) + { + return dateTimeOffset; + } + + var timezoneInfo = TimezoneProvider.GetTimeZoneInfo(CurrentTimezoneProvider.TimeZone); + return TimeZoneInfo.ConvertTime(dateTimeOffset, timezoneInfo); + } + + /// + /// Converts given to UTC if the given time is not UTC and the current clock provider supports multiple time zones. + /// + /// DateTime to be normalized. + /// Converted DateTime + public DateTime ConvertToUtc(DateTime dateTime) + { + if (!SupportsMultipleTimezone || + dateTime.Kind == DateTimeKind.Utc || + CurrentTimezoneProvider.TimeZone.IsNullOrWhiteSpace()) + { + return dateTime; + } + + var timezoneInfo = TimezoneProvider.GetTimeZoneInfo(CurrentTimezoneProvider.TimeZone); + dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified); + return TimeZoneInfo.ConvertTimeToUtc(dateTime, timezoneInfo); + } } diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/CurrentTimezoneProvider.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/CurrentTimezoneProvider.cs new file mode 100644 index 0000000000..12781d4321 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/CurrentTimezoneProvider.cs @@ -0,0 +1,20 @@ +using System.Threading; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Timing; + +public class CurrentTimezoneProvider : ICurrentTimezoneProvider, ISingletonDependency +{ + public string? TimeZone + { + get => _currentScope.Value; + set => _currentScope.Value = value; + } + + private readonly AsyncLocal _currentScope; + + public CurrentTimezoneProvider() + { + _currentScope = new AsyncLocal(); + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/CurrentTimezoneProviderExtensions.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/CurrentTimezoneProviderExtensions.cs new file mode 100644 index 0000000000..2fc79289ad --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/CurrentTimezoneProviderExtensions.cs @@ -0,0 +1,18 @@ +using System; + +namespace Volo.Abp.Timing; + +public static class CurrentTimezoneProviderExtensions +{ + public static IDisposable Change(this ICurrentTimezoneProvider currentTimezoneProvider, string? timeZone) + { + var parentScope = currentTimezoneProvider.TimeZone; + currentTimezoneProvider.TimeZone = timeZone; + + return new DisposeAction>(static (state) => + { + var (currentTimezoneProvider, parentScope) = state; + currentTimezoneProvider.TimeZone = parentScope; + }, (currentTimezoneProvider, parentScope)); + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClientTimezoneProvider.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClientTimezoneProvider.cs new file mode 100644 index 0000000000..69c2835adc --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClientTimezoneProvider.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.Timing; + +public interface ICurrentTimezoneProvider +{ + string? TimeZone { get; set; } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClock.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClock.cs index 33db4b45a8..7bf83dc7e0 100644 --- a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClock.cs +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClock.cs @@ -25,4 +25,25 @@ public interface IClock /// DateTime to be normalized. /// Normalized DateTime DateTime Normalize(DateTime dateTime); + + /// + /// Converts given UTC to user's time zone. + /// + /// DateTime to be normalized. + /// Converted DateTime + DateTime ConvertToUserTime(DateTime utcDateTime); + + /// + /// Converts given to user's time zone. + /// + /// DateTimeOffset to be normalized. + /// Converted DateTimeOffset + DateTimeOffset ConvertToUserTime(DateTimeOffset dateTimeOffset); + + /// + /// Converts given to UTC if the given time is not UTC and the current clock provider supports multiple time zones. + /// + /// DateTime to be normalized. + /// Converted DateTime + DateTime ConvertToUtc(DateTime dateTime); } diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TZConvertTimezoneProvider.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TZConvertTimezoneProvider.cs index 70d19090e2..565c04c6fa 100644 --- a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TZConvertTimezoneProvider.cs +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TZConvertTimezoneProvider.cs @@ -15,7 +15,7 @@ public class TZConvertTimezoneProvider : ITimezoneProvider, ITransientDependency public virtual List GetIanaTimezones() { - return TZConvert.KnownIanaTimeZoneNames.OrderBy(x => x).Select(x => new NameValue(x, x)).ToList(); + return TZConvert.KnownIanaTimeZoneNames.OrderBy(x => x).Where(x => x.Contains("/") && !x.Contains("Etc") || x == "UTC").Select(x => new NameValue(x, x)).ToList(); } public virtual string WindowsToIana(string windowsTimeZoneId) diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimeZoneConsts.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimeZoneConsts.cs new file mode 100644 index 0000000000..211247f5aa --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimeZoneConsts.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.Timing; + +public class TimeZoneConsts +{ + public const string DefaultTimeZoneKey = "__timezone"; +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingProvider.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingProvider.cs index 066747df94..343e20feb5 100644 --- a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingProvider.cs +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingProvider.cs @@ -10,7 +10,7 @@ public class TimingSettingProvider : SettingDefinitionProvider { context.Add( new SettingDefinition(TimingSettingNames.TimeZone, - "UTC", + "", L("DisplayName:Abp.Timing.Timezone"), L("Description:Abp.Timing.Timezone"), isVisibleToClients: true) diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs index 0e13746d66..1c705d8df3 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs @@ -159,6 +159,7 @@ public class AbpAspNetCoreMvcTestModule : AbpModule app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); + app.UseAbpTimeZone(); app.UseAuditing(); app.UseUnitOfWork(); app.UseConfiguredEndpoints(); diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Timing/AbpTimeZoneMiddleware_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Timing/AbpTimeZoneMiddleware_Tests.cs new file mode 100644 index 0000000000..7f12e5a07d --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Timing/AbpTimeZoneMiddleware_Tests.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; +using Shouldly; +using Volo.Abp.Settings; +using Volo.Abp.Timing; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Timing; + +public class AbpTimeZoneMiddleware_Tests : AspNetCoreMvcTestBase +{ + private readonly ICurrentTimezoneProvider _currentTimezoneProvider; + private readonly ITimezoneProvider _timezoneProvider; + public AbpTimeZoneMiddleware_Tests() + { + _currentTimezoneProvider = GetRequiredService(); + _timezoneProvider = GetRequiredService(); + } + + protected override void ConfigureServices(IServiceCollection services) + { + services.Configure(options => + { + options.Kind = DateTimeKind.Utc; + }); + } + + [Fact] + public async Task Should_Override_TimeZone_Setting_By_Request() + { + using (_currentTimezoneProvider.Change("UTC")) + { + var result = await Client.GetStringAsync("api/timing-test"); + result.ShouldBe(GetLocalTimeZone()); + } + + // Query string + using (_currentTimezoneProvider.Change("UTC")) + { + var result = await Client.GetStringAsync("api/timing-test?__timezone=Europe/Istanbul"); + result.ShouldBe("Europe/Istanbul"); + } + + // Header + using (_currentTimezoneProvider.Change("UTC")) + { + Client.DefaultRequestHeaders.Add("__timezone", "Asia/Shanghai"); + var result = await Client.GetStringAsync("api/timing-test"); + result.ShouldBe("Asia/Shanghai"); + } + + // Form + using (_currentTimezoneProvider.Change("UTC")) + { + Client.DefaultRequestHeaders.Remove("__timezone"); + var result = await Client.PostAsync("api/timing-test", new FormUrlEncodedContent(new[] {new KeyValuePair("__timezone", "Europe/Germany")})); + (await result.Content.ReadAsStringAsync()).ShouldBe("Europe/Germany"); + } + + // Cookie + using (_currentTimezoneProvider.Change("UTC")) + { + Client.DefaultRequestHeaders.Remove("__timezone"); + Client.DefaultRequestHeaders.Add("Cookie", "__timezone=Europe/Istanbul"); + var result = await Client.GetStringAsync("api/timing-test"); + result.ShouldBe("Europe/Istanbul"); + } + } + + private string GetLocalTimeZone() + { + if (TimeZoneInfo.Local.HasIanaId) + { + return TimeZoneInfo.Local.Id; + } + + return TimeZoneInfo.TryConvertWindowsIdToIanaId(TimeZoneInfo.Local.Id, out var ianaName) + ? ianaName + : null; + } +} + + +public class AbpTimeZoneMiddleware_With_SettingValue_Tests : AspNetCoreMvcTestBase +{ + private readonly ICurrentTimezoneProvider _currentTimezoneProvider; + public AbpTimeZoneMiddleware_With_SettingValue_Tests() + { + _currentTimezoneProvider = GetRequiredService(); + } + + protected override void ConfigureServices(IServiceCollection services) + { + var settingStore = Substitute.For(); + settingStore.GetOrNullAsync(TimingSettingNames.TimeZone).Returns("Asia/Shanghai"); + services.AddSingleton(settingStore); + + services.Configure(options => + { + options.Kind = DateTimeKind.Utc; + }); + } + + [Fact] + public async Task Should_Not_Override_TimeZone_Setting_By_Request() + { + using (_currentTimezoneProvider.Change("UTC")) + { + var result = await Client.GetStringAsync("api/timing-test"); + result.ShouldBe("Asia/Shanghai"); + } + + // Query string + using (_currentTimezoneProvider.Change("UTC")) + { + var result = await Client.GetStringAsync("api/timing-test?__timezone=Europe/Istanbul"); + result.ShouldBe("Asia/Shanghai"); + } + + // Header + using (_currentTimezoneProvider.Change("UTC")) + { + Client.DefaultRequestHeaders.Add("__timezone", "Europe/Istanbul"); + var result = await Client.GetStringAsync("api/timing-test"); + result.ShouldBe("Asia/Shanghai"); + } + + // Form + using (_currentTimezoneProvider.Change("UTC")) + { + Client.DefaultRequestHeaders.Remove("__timezone"); + var result = await Client.PostAsync("api/timing-test", new FormUrlEncodedContent(new[] {new KeyValuePair("__timezone", "Europe/Istanbul")})); + (await result.Content.ReadAsStringAsync()).ShouldBe("Asia/Shanghai"); + } + + // Cookie + using (_currentTimezoneProvider.Change("UTC")) + { + Client.DefaultRequestHeaders.Remove("__timezone"); + Client.DefaultRequestHeaders.Add("Cookie", "__timezone=Europe/Istanbul"); + var result = await Client.GetStringAsync("api/timing-test"); + result.ShouldBe("Asia/Shanghai"); + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Timing/TimingTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Timing/TimingTestController.cs new file mode 100644 index 0000000000..a509e62ec5 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Timing/TimingTestController.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Timing; + +namespace Volo.Abp.AspNetCore.Mvc.Timing; + +[Route("api/timing-test")] +public class UnitOfWorkTestController : AbpController +{ + private readonly ICurrentTimezoneProvider _currentTimezoneProvider; + + public UnitOfWorkTestController(ICurrentTimezoneProvider currentTimezoneProvider) + { + _currentTimezoneProvider = currentTimezoneProvider; + } + + [HttpGet] + public ActionResult GetAsync() + { + return Content(_currentTimezoneProvider.TimeZone ?? "null"); + } + + [HttpPost] + public ActionResult PostAsync() + { + return Content(_currentTimezoneProvider.TimeZone ?? "null"); + } +} diff --git a/framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/AutoFac_OnActivated_Tests.cs b/framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/AutoFac_OnActivated_Tests.cs index b2af6d85da..46c9ea2e8c 100644 --- a/framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/AutoFac_OnActivated_Tests.cs +++ b/framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/AutoFac_OnActivated_Tests.cs @@ -31,9 +31,9 @@ public class AutoFac_OnActivated_Tests : Autofac_Interception_Test var server = ServiceProvider.GetRequiredService(); server.Name.ShouldBe("MyServer12"); } -} -class MyServer -{ - public string Name { get; set; } = "MyServer"; + class MyServer + { + public string Name { get; set; } = "MyServer"; + } } diff --git a/framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/AutoFac_OnRegistred_Tests.cs b/framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/AutoFac_OnRegistred_Tests.cs new file mode 100644 index 0000000000..34b739cb7f --- /dev/null +++ b/framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/AutoFac_OnRegistred_Tests.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Autofac.Interception; +using Xunit; + +namespace Volo.Abp.Autofac; + +public class AutoFac_OnRegistred_Tests : Autofac_Interception_Test +{ + protected override Task AfterAddApplicationAsync(IServiceCollection services) + { + services.Add(ServiceDescriptor.KeyedTransient("key")); + services.OnRegistered(onServiceRegistredContext => + { + if (onServiceRegistredContext.ImplementationType == typeof(MyServer)) + { + onServiceRegistredContext.ServiceKey.ShouldBe("key"); + } + }); + + return base.AfterAddApplicationAsync(services); + } + + class MyServer + { + public string Name { get; set; } = "MyServer"; + } +} diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs index d82fb195ab..6249d588f5 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs @@ -396,3 +396,134 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase entityUpdatedEventTriggered.ShouldBeTrue(); } } + + +public abstract class AbpEfCoreDomainEvents_Disable_UpdateAggregateRoot_Tests : EntityFrameworkCoreTestBase +{ + protected readonly IRepository AppEntityWithNavigationsRepository; + protected readonly IRepository AppEntityWithNavigationForeignRepository; + protected readonly ILocalEventBus LocalEventBus; + + protected AbpEfCoreDomainEvents_Disable_UpdateAggregateRoot_Tests() + { + AppEntityWithNavigationsRepository = GetRequiredService>(); + AppEntityWithNavigationForeignRepository = GetRequiredService>(); + LocalEventBus = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + services.Configure(options => + { + options.PublishEntityUpdatedEventWhenNavigationChanges = true; + options.UpdateAggregateRootWhenNavigationChanges = false; + }); + + base.AfterAddApplication(services); + } + + [Fact] + public async Task Should_Trigger_Domain_Events_But_Do_Not_Change_Aggregate_Root_When_Navigation_Changes_Tests() + { + var entityId = Guid.NewGuid(); + + var newEntity = await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity")); + + var latestConcurrencyStamp = newEntity.ConcurrencyStamp; + var lastModificationTime = newEntity.LastModificationTime; + + var entityUpdatedEventTriggered = false; + + LocalEventBus.Subscribe>(data => + { + entityUpdatedEventTriggered = true; + + // The Aggregate will not be updated + data.Entity.ConcurrencyStamp.ShouldBe(latestConcurrencyStamp); + data.Entity.LastModificationTime.ShouldBe(lastModificationTime); + return Task.CompletedTask; + }); + + // Test with value object + entityUpdatedEventTriggered = false; + await WithUnitOfWorkAsync(async () => + { + var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); + entity.AppEntityWithValueObjectAddress = new AppEntityWithValueObjectAddress("Turkey"); + await AppEntityWithNavigationsRepository.UpdateAsync(entity); + }); + entityUpdatedEventTriggered.ShouldBeTrue(); + + // Test with one to one + entityUpdatedEventTriggered = false; + await WithUnitOfWorkAsync(async () => + { + var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); + entity.OneToOne = new AppEntityWithNavigationChildOneToOne + { + ChildName = "ChildName" + }; + await AppEntityWithNavigationsRepository.UpdateAsync(entity); + }); + entityUpdatedEventTriggered.ShouldBeTrue(); + + // Test with one to many + entityUpdatedEventTriggered = false; + await WithUnitOfWorkAsync(async () => + { + var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); + entity.OneToMany = new List() + { + new AppEntityWithNavigationChildOneToMany + { + AppEntityWithNavigationId = entity.Id, + ChildName = "ChildName1" + } + }; + await AppEntityWithNavigationsRepository.UpdateAsync(entity); + }); + entityUpdatedEventTriggered.ShouldBeTrue(); + + // Test with many to many + entityUpdatedEventTriggered = false; + await WithUnitOfWorkAsync(async () => + { + var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); + entity.ManyToMany = new List() + { + new AppEntityWithNavigationChildManyToMany + { + ChildName = "ChildName1" + } + }; + await AppEntityWithNavigationsRepository.UpdateAsync(entity); + }); + entityUpdatedEventTriggered.ShouldBeTrue(); + } +} + +public class AbpEfCoreDomainEvents_UpdateAggregateRootWhenNavigationChanges_Tests : AbpEfCoreDomainEvents_Disable_UpdateAggregateRoot_Tests +{ + protected override void AfterAddApplication(IServiceCollection services) + { + services.Configure(options => + { + options.UpdateAggregateRootWhenNavigationChanges = false; + }); + + base.AfterAddApplication(services); + } +} + +public class AbpEfCoreDomainEvents_IgnoredUpdateAggregateRootSelectors_Test : AbpEfCoreDomainEvents_Disable_UpdateAggregateRoot_Tests +{ + protected override void AfterAddApplication(IServiceCollection services) + { + services.Configure(options => + { + options.IgnoredUpdateAggregateRootSelectors.Add("AppEntityWithValueObjectAddress", x => x == typeof(AppEntityWithNavigations)); + }); + + base.AfterAddApplication(services); + } +} diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonNewtonsoftTestModule.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonNewtonsoftTestModule.cs deleted file mode 100644 index 42559397da..0000000000 --- a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonNewtonsoftTestModule.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Volo.Abp.Autofac; -using Volo.Abp.Json.Newtonsoft; -using Volo.Abp.Json.SystemTextJson; -using Volo.Abp.Modularity; - -namespace Volo.Abp.Json; - -[DependsOn( - typeof(AbpAutofacModule), - typeof(AbpJsonSystemTextJsonModule), - typeof(AbpTestBaseModule) -)] -public class AbpJsonSystemTextJsonTestModule : AbpModule -{ - -} - -[DependsOn( - typeof(AbpAutofacModule), - typeof(AbpJsonNewtonsoftModule), - typeof(AbpTestBaseModule) -)] -public class AbpJsonNewtonsoftTestModule : AbpModule -{ - -} diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonSystemTextJsonTestBase.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonSystemTextJsonTestBase.cs deleted file mode 100644 index ebae706aca..0000000000 --- a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonSystemTextJsonTestBase.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Volo.Abp.Testing; - -namespace Volo.Abp.Json; - -public abstract class AbpJsonSystemTextJsonTestBase : AbpIntegratedTest -{ - protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) - { - options.UseAutofac(); - } -} - -public abstract class AbpJsonNewtonsoftJsonTestBase : AbpIntegratedTest -{ - protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) - { - options.UseAutofac(); - } -} diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonTestBase.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonTestBase.cs new file mode 100644 index 0000000000..e442ce48ad --- /dev/null +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonTestBase.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Concurrent; +using System.Reflection; +using Newtonsoft.Json; +using Volo.Abp.Json.Newtonsoft; +using Volo.Abp.Testing; + +namespace Volo.Abp.Json; + +public abstract class AbpJsonSystemTextJsonTestBase : AbpIntegratedTest +{ + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } +} + +public abstract class AbpJsonNewtonsoftJsonTestBase : AbpIntegratedTest +{ + protected AbpJsonNewtonsoftJsonTestBase() + { + var cache = typeof(AbpNewtonsoftJsonSerializer).GetField("JsonSerializerOptionsCache", BindingFlags.NonPublic | BindingFlags.Static); + if (cache != null) + { + var cacheValue = cache.GetValue(null)?.As>(); + cacheValue?.Clear(); + } + } + + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } +} diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonTestModule.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonTestModule.cs new file mode 100644 index 0000000000..121ec62a85 --- /dev/null +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonTestModule.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Autofac; +using Volo.Abp.Json.Newtonsoft; +using Volo.Abp.Json.SystemTextJson; +using Volo.Abp.Json.SystemTextJson.JsonConverters; +using Volo.Abp.Json.SystemTextJson.Modifiers; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Json; + +[DependsOn( + typeof(AbpAutofacModule), + typeof(AbpJsonSystemTextJsonModule), + typeof(AbpTestBaseModule) +)] +public class AbpJsonSystemTextJsonTestModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddOptions() + .Configure((options, rootServiceProvider) => + { + if (options.JsonSerializerOptions.TypeInfoResolver != null) + { + var modifiers = options.JsonSerializerOptions.TypeInfoResolver.As().Modifiers; + modifiers.RemoveAll(x => x.Target?.GetType() == typeof(AbpDateTimeConverterModifier)); + modifiers.Add(new AbpDateTimeConverterModifier( + rootServiceProvider.GetRequiredService(), + rootServiceProvider.GetRequiredService()).CreateModifyAction()); + } + }); + } +} + +[DependsOn( + typeof(AbpAutofacModule), + typeof(AbpJsonNewtonsoftModule), + typeof(AbpTestBaseModule) +)] +public class AbpJsonNewtonsoftTestModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddOptions() + .Configure((options, rootServiceProvider) => + { + options.JsonSerializerSettings.ContractResolver = new AbpCamelCasePropertyNamesContractResolver( + rootServiceProvider.GetRequiredService()); + }); + } +} diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpNewtonsoftSerializerProviderTests.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpNewtonsoftSerializerProviderTests.cs new file mode 100644 index 0000000000..9dc0c0afd3 --- /dev/null +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpNewtonsoftSerializerProviderTests.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using Shouldly; +using Xunit; + +namespace Volo.Abp.Json; + +[Collection("AbpJsonNewtonsoftJsonTest")] +public class AbpNewtonsoftSerializerProviderTests : AbpJsonNewtonsoftJsonTestBase +{ + protected IJsonSerializer JsonSerializer; + + public AbpNewtonsoftSerializerProviderTests() + { + JsonSerializer = GetRequiredService(); + } + + public class File + { + public string FileName { get; set; } + + public Dictionary ExtraProperties { get; set; } + } + + [Fact] + public void Serialize_Deserialize_Test() + { + var defaultIndent = " "; // Default indent is 2 spaces + var newLine = Environment.NewLine; + var file = new File() + { + FileName = "abp", + ExtraProperties = new Dictionary() + { + { "One", 1 }, + { "Two", 2 } + } + }; + + var json = JsonSerializer.Serialize(file, camelCase: true); + json.ShouldBe("{\"fileName\":\"abp\",\"extraProperties\":{\"One\":1,\"Two\":2}}"); + + json = JsonSerializer.Serialize(file, camelCase: true, indented: true); + json.ShouldBe($"{{{newLine}{defaultIndent}\"fileName\": \"abp\",{newLine}{defaultIndent}\"extraProperties\": {{{newLine}{defaultIndent}{defaultIndent}\"One\": 1,{newLine}{defaultIndent}{defaultIndent}\"Two\": 2{newLine}{defaultIndent}}}{newLine}}}"); + + json = JsonSerializer.Serialize(file, camelCase: false); + json.ShouldBe("{\"FileName\":\"abp\",\"ExtraProperties\":{\"One\":1,\"Two\":2}}"); + + json = JsonSerializer.Serialize(file, camelCase: false, indented: true); + json.ShouldBe($"{{{newLine}{defaultIndent}\"FileName\": \"abp\",{newLine}{defaultIndent}\"ExtraProperties\": {{{newLine}{defaultIndent}{defaultIndent}\"One\": 1,{newLine}{defaultIndent}{defaultIndent}\"Two\": 2{newLine}{defaultIndent}}}{newLine}}}"); + } +} diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs index e32df85d7f..49b321d46d 100644 --- a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Text.Json; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Data; @@ -11,11 +13,11 @@ namespace Volo.Abp.Json; public abstract class AbpSystemTextJsonSerializerProviderTestBase : AbpJsonSystemTextJsonTestBase { - protected AbpSystemTextJsonSerializer JsonSerializer; + protected IJsonSerializer JsonSerializer; public AbpSystemTextJsonSerializerProviderTestBase() { - JsonSerializer = GetRequiredService(); + JsonSerializer = GetRequiredService(); } public class TestExtensibleObjectClass : ExtensibleObject @@ -23,6 +25,13 @@ public abstract class AbpSystemTextJsonSerializerProviderTestBase : AbpJsonSyste public string Name { get; set; } } + public class File + { + public string FileName { get; set; } + + public Dictionary ExtraProperties { get; set; } + } + public class FileWithBoolean { public string Name { get; set; } @@ -74,6 +83,34 @@ public abstract class AbpSystemTextJsonSerializerProviderTestBase : AbpJsonSyste public class AbpSystemTextJsonSerializerProviderTests : AbpSystemTextJsonSerializerProviderTestBase { + [Fact] + public void Serialize_Deserialize_Test() + { + var defaultIndent = " "; // Default indent is 2 spaces + var newLine = Environment.NewLine; + var file = new File() + { + FileName = "abp", + ExtraProperties = new Dictionary() + { + { "One", 1 }, + { "Two", 2 } + } + }; + + var json = JsonSerializer.Serialize(file, camelCase: true); + json.ShouldBe("{\"fileName\":\"abp\",\"extraProperties\":{\"One\":1,\"Two\":2}}"); + + json = JsonSerializer.Serialize(file, camelCase: true, indented: true); + json.ShouldBe($"{{{newLine}{defaultIndent}\"fileName\": \"abp\",{newLine}{defaultIndent}\"extraProperties\": {{{newLine}{defaultIndent}{defaultIndent}\"One\": 1,{newLine}{defaultIndent}{defaultIndent}\"Two\": 2{newLine}{defaultIndent}}}{newLine}}}"); + + json = JsonSerializer.Serialize(file, camelCase: false); + json.ShouldBe("{\"FileName\":\"abp\",\"ExtraProperties\":{\"One\":1,\"Two\":2}}"); + + json = JsonSerializer.Serialize(file, camelCase: false, indented: true); + json.ShouldBe($"{{{newLine}{defaultIndent}\"FileName\": \"abp\",{newLine}{defaultIndent}\"ExtraProperties\": {{{newLine}{defaultIndent}{defaultIndent}\"One\": 1,{newLine}{defaultIndent}{defaultIndent}\"Two\": 2{newLine}{defaultIndent}}}{newLine}}}"); + } + [Fact] public void Serialize_Deserialize_With_Boolean() { @@ -223,6 +260,8 @@ public class AbpSystemTextJsonSerializerProviderDateTimeFormatTests : AbpSystemT options.InputDateTimeFormats.Add("yyyy*MM*dd"); options.OutputDateTimeFormat = "yyyy*MM*dd HH*mm*ss"; }); + + base.AfterAddApplication(services); } [Fact] @@ -289,6 +328,8 @@ public class AbpSystemTextJsonSerializerProviderDatetimeKindUtcTests : AbpSystem { Kind = DateTimeKind.Utc; services.Configure(x => x.Kind = Kind); + + base.AfterAddApplication(services); } } @@ -298,6 +339,8 @@ public class AbpSystemTextJsonSerializerProviderDatetimeKindLocalTests : AbpSyst { Kind = DateTimeKind.Local; services.Configure(x => x.Kind = Kind); + + base.AfterAddApplication(services); } } diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/InputAndOutputDateTimeFormat_Tests.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/InputAndOutputDateTimeFormat_Tests.cs index cf6356477d..189a82cae0 100644 --- a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/InputAndOutputDateTimeFormat_Tests.cs +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/InputAndOutputDateTimeFormat_Tests.cs @@ -31,6 +31,8 @@ public class InputAndOutputDateTimeFormatSystemTextJsonTests : AbpJsonSystemText { options.Kind = DateTimeKind.Utc; }); + + base.AfterAddApplication(services); } [Fact] @@ -59,6 +61,7 @@ public class InputAndOutputDateTimeFormatSystemTextJsonTests : AbpJsonSystemText } } +[Collection("AbpJsonNewtonsoftJsonTest")] public class InputAndOutputDateTimeFormatNewtonsoftTests : AbpJsonNewtonsoftJsonTestBase { private readonly IJsonSerializer _jsonSerializer; @@ -83,6 +86,8 @@ public class InputAndOutputDateTimeFormatNewtonsoftTests : AbpJsonNewtonsoftJson { options.Kind = DateTimeKind.Utc; }); + + base.AfterAddApplication(services); } [Fact] diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj b/framework/test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj index e2328fdca3..524533f890 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj @@ -16,9 +16,9 @@ - - - + + + diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs index e2ad9088cb..3df8699f6d 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using MongoDB.Driver.Linq; using Shouldly; +using Volo.Abp.Data; using Volo.Abp.Domain.Repositories; using Volo.Abp.TestApp; using Volo.Abp.TestApp.Domain; @@ -52,10 +53,12 @@ public class Repository_Basic_Tests : Repository_Basic_Tests p.PersonId == person.Id && p.Number == "1234567890").ShouldBeTrue(); + person.GetProperty("test-guid-property").ShouldBe(person.Id); + person.GetProperty("test-nullable-guid-property").ShouldBe(person.Id); } - + [Fact] public async Task Filter_Case_Insensitive() { (await CityRepository.GetQueryableAsync()).FirstOrDefault(c => c.Name == "ISTANBUL").ShouldBeNull(); (await CityRepository.GetQueryableAsync()).FirstOrDefault(c => c.Name == "istanbul").ShouldBeNull(); (await CityRepository.GetQueryableAsync()).FirstOrDefault(c => c.Name == "Istanbul").ShouldNotBeNull(); - + (await PersonRepository.GetQueryableAsync()).FirstOrDefault(p => p.Name == "douglas").ShouldNotBeNull(); (await PersonRepository.GetQueryableAsync()).FirstOrDefault(p => p.Name == "DOUGLAS").ShouldNotBeNull(); (await PersonRepository.GetQueryableAsync()).FirstOrDefault(p => p.Name == "Douglas").ShouldNotBeNull(); diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityCache_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityCache_Tests.cs index f1bd602102..679590ac31 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityCache_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityCache_Tests.cs @@ -70,6 +70,29 @@ public abstract class EntityCache_Tests : TestAppTestBase : TestAppTestBase where TStartupModule : IAbpModule { protected IRepository PersonRepository { get; } + protected ICityRepository CityRepository { get; } protected ILocalEventBus LocalEventBus { get; } protected IDistributedEventBus DistributedEventBus { get; } protected EntityChangeEvents_Tests() { PersonRepository = GetRequiredService>(); + CityRepository = GetRequiredService(); LocalEventBus = GetRequiredService(); DistributedEventBus = GetRequiredService(); } + protected override void AfterAddApplication(IServiceCollection services) + { + services.Configure(options => + { + options.IgnoredEventSelectors.Add(); + }); + } + [Fact] public async Task Complex_Event_Test() { @@ -62,7 +73,7 @@ public abstract class EntityChangeEvents_Tests : TestAppTestBase await uow.CompleteAsync(); } - + createdEventTriggered.ShouldBeTrue(); createdEtoTriggered.ShouldBeTrue(); } @@ -113,4 +124,33 @@ public abstract class EntityChangeEvents_Tests : TestAppTestBase updateEventCount.ShouldBe(1); updatedAge.ShouldBe(45); } + + [Fact] + public async Task Should_Not_Trigger_Event_If_Ignore_Event_Selector_Is_Configured() + { + var personCreatedEtoTriggered = false; + var cityCreatedEtoTriggered = false; + using (var uow = GetRequiredService().Begin()) + { + DistributedEventBus.Subscribe>(eto => + { + cityCreatedEtoTriggered = true; + return Task.CompletedTask; + }); + + DistributedEventBus.Subscribe>(eto => + { + personCreatedEtoTriggered = true; + return Task.CompletedTask; + }); + + await CityRepository.InsertAsync(new City(Guid.NewGuid(), "Istanbul")); + await PersonRepository.InsertAsync(new Person(Guid.NewGuid(), "John", 15)); + + await uow.CompleteAsync(); + } + + personCreatedEtoTriggered.ShouldBeTrue(); + cityCreatedEtoTriggered.ShouldBeFalse(); + } } diff --git a/framework/test/Volo.Abp.Timing.Tests/Volo.Abp.Timing.Tests.csproj b/framework/test/Volo.Abp.Timing.Tests/Volo.Abp.Timing.Tests.csproj new file mode 100644 index 0000000000..58bc261247 --- /dev/null +++ b/framework/test/Volo.Abp.Timing.Tests/Volo.Abp.Timing.Tests.csproj @@ -0,0 +1,16 @@ + + + + + + net9.0 + + + + + + + + + + diff --git a/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/AbpTimingTestModule.cs b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/AbpTimingTestModule.cs new file mode 100644 index 0000000000..c416477f53 --- /dev/null +++ b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/AbpTimingTestModule.cs @@ -0,0 +1,14 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.Timing; + +[DependsOn( + typeof(AbpTimingModule), + typeof(AbpTestBaseModule) +)] +public class AbpTimingTestModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + } +} diff --git a/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Default_Tests.cs b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Default_Tests.cs new file mode 100644 index 0000000000..1904518180 --- /dev/null +++ b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Default_Tests.cs @@ -0,0 +1,110 @@ +using System; +using Shouldly; +using Volo.Abp.Testing; +using Xunit; + +namespace Volo.Abp.Timing; + +public class Clock_Default_Tests : AbpIntegratedTest +{ + private readonly IClock _clock; + private readonly ICurrentTimezoneProvider _currentTimezoneProvider; + + public Clock_Default_Tests() + { + _clock = GetRequiredService(); + _currentTimezoneProvider = GetRequiredService(); + } + + [Fact] + public void ConvertTo_Test() + { + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc); + var convertedDateTime = _clock.ConvertToUserTime(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Utc); + convertedDateTime.ToString("O").ShouldBe("2025-03-01T05:30:00.0000000Z"); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Local); + var convertedDateTime = _clock.ConvertToUserTime(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Local); + convertedDateTime.ToString("O").ShouldBe(dateTime.ToString("O")); + } + + using(_currentTimezoneProvider.Change(null)) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Local); + var convertedDateTime = _clock.ConvertToUserTime(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Local); + convertedDateTime.ToString("O").ShouldBe(dateTime.ToString("O")); + } + } + + [Fact] + public void ConvertTo_DateTimeOffset_Test() + { + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc), TimeSpan.Zero); + var convertedDateTimeOffset = _clock.ConvertToUserTime(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.Zero); + convertedDateTimeOffset.ShouldBe(dateTimeOffset); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.Zero); + var convertedDateTimeOffset = _clock.ConvertToUserTime(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.Zero); + convertedDateTimeOffset.ShouldBe(dateTimeOffset); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.FromHours(3)); + var convertedDateTimeOffset = _clock.ConvertToUserTime(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(3)); + convertedDateTimeOffset.ShouldBe(dateTimeOffset); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.FromHours(8)); + var convertedDateTimeOffset = _clock.ConvertToUserTime(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(8)); + convertedDateTimeOffset.ShouldBe(dateTimeOffset); + } + } + + [Fact] + public void ConvertFrom_Test() + { + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified); + var convertedDateTime = _clock.ConvertToUtc(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Unspecified); + convertedDateTime.ShouldBe(dateTime); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Local); + var convertedDateTime = _clock.ConvertToUtc(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Local); + convertedDateTime.ShouldBe(dateTime); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc); + var convertedDateTime = _clock.ConvertToUtc(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Utc); + convertedDateTime.ShouldBe(dateTime); + } + } +} diff --git a/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Utc_Tests.cs b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Utc_Tests.cs new file mode 100644 index 0000000000..4ecbd0d4c6 --- /dev/null +++ b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Utc_Tests.cs @@ -0,0 +1,132 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Testing; +using Xunit; + +namespace Volo.Abp.Timing; + +public class Clock_Utc_Tests : AbpIntegratedTest +{ + private readonly IClock _clock; + private readonly ICurrentTimezoneProvider _currentTimezoneProvider; + + public Clock_Utc_Tests() + { + _clock = GetRequiredService(); + _currentTimezoneProvider = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + services.Configure(options => + { + options.Kind = DateTimeKind.Utc; + }); + base.AfterAddApplication(services); + } + + [Fact] + public void ConvertTo_Test() + { + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc); + var convertedDateTime = _clock.ConvertToUserTime(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Unspecified); + convertedDateTime.ToString("O").ShouldBe("2025-03-01T08:30:00.0000000"); //Without Z + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + // ConvertTo will not convert the DateTimeKind.Local + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Local); + var convertedDateTime = _clock.ConvertToUserTime(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Local); + convertedDateTime.ToString("O").ShouldBe(dateTime.ToString("O")); + } + + using(_currentTimezoneProvider.Change(null)) + { + // ConvertTo will not convert if the timezone is not set + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Local); + var convertedDateTime = _clock.ConvertToUserTime(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Local); + convertedDateTime.ToString("O").ShouldBe(dateTime.ToString("O")); + } + } + + [Fact] + public void ConvertTo_DateTimeOffset_Test() + { + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc), TimeSpan.Zero); + var convertedDateTimeOffset = _clock.ConvertToUserTime(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(3)); + convertedDateTimeOffset.ToString("O").ShouldBe("2025-03-01T08:30:00.0000000+03:00"); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.Zero); + var convertedDateTimeOffset = _clock.ConvertToUserTime(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(3)); + convertedDateTimeOffset.ToString("O").ShouldBe("2025-03-01T08:30:00.0000000+03:00"); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.FromHours(3)); + var convertedDateTimeOffset = _clock.ConvertToUserTime(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(3)); + convertedDateTimeOffset.ShouldBe(dateTimeOffset); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.FromHours(8)); + var convertedDateTimeOffset = _clock.ConvertToUserTime(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(3)); + convertedDateTimeOffset.DateTime.ShouldBe(new DateTime(2025, 3, 1, 0, 30, 0, DateTimeKind.Unspecified)); + } + + using(_currentTimezoneProvider.Change(null)) + { + // ConvertTo will not convert if the timezone is not set + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.FromHours(8)); + var convertedDateTimeOffset = _clock.ConvertToUserTime(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(8)); + convertedDateTimeOffset.ShouldBe(dateTimeOffset); + } + } + + [Fact] + public void ConvertFrom_Test() + { + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified); + var convertedDateTime = _clock.ConvertToUtc(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Utc); + convertedDateTime.ToString("O").ShouldBe("2025-03-01T02:30:00.0000000Z"); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Local); + var convertedDateTime = _clock.ConvertToUtc(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Utc); + convertedDateTime.ToString("O").ShouldBe("2025-03-01T02:30:00.0000000Z"); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc); + var convertedDateTime = _clock.ConvertToUtc(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Utc); + convertedDateTime.ToString("O").ShouldBe("2025-03-01T05:30:00.0000000Z"); + } + } + +} diff --git a/latest-versions.json b/latest-versions.json index 0ced5df485..340ed62653 100644 --- a/latest-versions.json +++ b/latest-versions.json @@ -1,5 +1,14 @@ [ { + "version": "9.1.1", + "releaseDate": "", + "type": "stable", + "message": "", + "leptonx": { + "version": "4.1.1" + } + }, + { "version": "9.1.0", "releaseDate": "", "type": "stable", @@ -8,6 +17,24 @@ "version": "4.1.0" } }, + { + "version": "9.0.8", + "releaseDate": "", + "type": "stable", + "message": "", + "leptonx": { + "version": "4.0.9" + } + }, + { + "version": "9.0.7", + "releaseDate": "", + "type": "stable", + "message": "", + "leptonx": { + "version": "4.0.8" + } + }, { "version": "9.0.6", "releaseDate": "", diff --git a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs index 9f670ffc82..1b41deaff7 100644 --- a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs +++ b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs @@ -57,11 +57,6 @@ public class AbpAccountWebModule : AbpModule options.MenuContributors.Add(new AbpAccountUserMenuContributor()); }); - Configure(options => - { - options.Contributors.Add(new AccountModuleToolbarContributor()); - }); - ConfigureProfileManagementPage(); context.Services.AddAutoMapperObjectMapper(); diff --git a/modules/account/src/Volo.Abp.Account.Web/AccountModuleToolbarContributor.cs b/modules/account/src/Volo.Abp.Account.Web/AccountModuleToolbarContributor.cs deleted file mode 100644 index 12ea1f87c0..0000000000 --- a/modules/account/src/Volo.Abp.Account.Web/AccountModuleToolbarContributor.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Account.Web.Modules.Account.Components.Toolbar.UserLoginLink; -using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars; -using Volo.Abp.Users; - -namespace Volo.Abp.Account.Web; - -public class AccountModuleToolbarContributor : IToolbarContributor -{ - public virtual Task ConfigureToolbarAsync(IToolbarConfigurationContext context) - { - if (context.Toolbar.Name != StandardToolbars.Main) - { - return Task.CompletedTask; - } - - if (!context.ServiceProvider.GetRequiredService().IsAuthenticated) - { - context.Toolbar.Items.Add(new ToolbarItem(typeof(UserLoginLinkViewComponent))); - } - - return Task.CompletedTask; - } -} diff --git a/modules/account/src/Volo.Abp.Account.Web/Modules/Account/Components/Toolbar/UserLoginLink/Default.cshtml b/modules/account/src/Volo.Abp.Account.Web/Modules/Account/Components/Toolbar/UserLoginLink/Default.cshtml deleted file mode 100644 index dbe3362c87..0000000000 --- a/modules/account/src/Volo.Abp.Account.Web/Modules/Account/Components/Toolbar/UserLoginLink/Default.cshtml +++ /dev/null @@ -1,4 +0,0 @@ -@using Localization.Resources.AbpUi -@using Microsoft.AspNetCore.Mvc.Localization -@inject IHtmlLocalizer L -@L["Login"] diff --git a/modules/account/src/Volo.Abp.Account.Web/Modules/Account/Components/Toolbar/UserLoginLink/UserLoginLinkViewComponent.cs b/modules/account/src/Volo.Abp.Account.Web/Modules/Account/Components/Toolbar/UserLoginLink/UserLoginLinkViewComponent.cs deleted file mode 100644 index 1180b6e454..0000000000 --- a/modules/account/src/Volo.Abp.Account.Web/Modules/Account/Components/Toolbar/UserLoginLink/UserLoginLinkViewComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Volo.Abp.AspNetCore.Mvc; - -namespace Volo.Abp.Account.Web.Modules.Account.Components.Toolbar.UserLoginLink; - -public class UserLoginLinkViewComponent : AbpViewComponent -{ - public virtual IViewComponentResult Invoke() - { - return View("~/Modules/Account/Components/Toolbar/UserLoginLink/Default.cshtml"); - } -} diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo.Abp.AuditLogging.MongoDB.Tests.csproj b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo.Abp.AuditLogging.MongoDB.Tests.csproj index a33c1c74f8..1b9925261b 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo.Abp.AuditLogging.MongoDB.Tests.csproj +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo.Abp.AuditLogging.MongoDB.Tests.csproj @@ -15,9 +15,9 @@ - - - + + + diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo.Abp.BackgroundJobs.Domain.abppkg.analyze.json b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo.Abp.BackgroundJobs.Domain.abppkg.analyze.json index f792712158..dda4d3a500 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo.Abp.BackgroundJobs.Domain.abppkg.analyze.json +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo.Abp.BackgroundJobs.Domain.abppkg.analyze.json @@ -132,6 +132,11 @@ "namespace": "Volo.Abp.BackgroundJobs", "primaryKeyType": "Guid", "properties": [ + { + "type": "System.String", + "name": "ApplicationName", + "summary": "Application name that scheduled this job." + }, { "type": "System.String", "name": "JobName", @@ -230,6 +235,11 @@ "isPrivate": false, "isStatic": false, "parameters": [ + { + "type": "String", + "name": "applicationName", + "isOptional": false + }, { "type": "Int32", "name": "maxResultCount", diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo.Abp.BackgroundJobs.MongoDB.Tests.csproj b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo.Abp.BackgroundJobs.MongoDB.Tests.csproj index a3f84bb18a..a1a1a47d2c 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo.Abp.BackgroundJobs.MongoDB.Tests.csproj +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo.Abp.BackgroundJobs.MongoDB.Tests.csproj @@ -15,9 +15,9 @@ - - - + + + diff --git a/modules/basic-theme/Volo.Abp.BasicTheme.sln b/modules/basic-theme/Volo.Abp.BasicTheme.sln index ac0ab4ed3b..66f19dad9a 100644 --- a/modules/basic-theme/Volo.Abp.BasicTheme.sln +++ b/modules/basic-theme/Volo.Abp.BasicTheme.sln @@ -1,5 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.35906.104 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED6D078F-B0A2-48E8-A09D-3B7CDF6CE3D1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0BC55E3B-4964-48E3-A390-2ADD37980149}" @@ -65,6 +68,9 @@ Global {D02053D9-10EF-4717-A792-A53F83347816}.Release|Any CPU.ActiveCfg = Release|Any CPU {D02053D9-10EF-4717-A792-A53F83347816}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection GlobalSection(NestedProjects) = preSolution {C8068E7F-4A04-4755-8976-C2A4C0ADC708} = {ED6D078F-B0A2-48E8-A09D-3B7CDF6CE3D1} {655C0CF7-7BFA-45E4-A157-E868A97FB45B} = {ED6D078F-B0A2-48E8-A09D-3B7CDF6CE3D1} diff --git a/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/WebAssemblyRedirectToLogin.razor b/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/WebAssemblyRedirectToLogin.razor index 97e0eb2995..f4f3e152ce 100644 --- a/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/WebAssemblyRedirectToLogin.razor +++ b/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/WebAssemblyRedirectToLogin.razor @@ -15,7 +15,7 @@ { if (AbpAspNetCoreComponentsWebOptions.Value.IsBlazorWebApp) { - Navigation.NavigateTo(AuthenticationOptions.Value.LogoutUrl, forceLoad: true); + Navigation.NavigateTo(AuthenticationOptions.Value.LoginUrl, forceLoad: true); } else { diff --git a/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml b/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml index 9a62892b5e..1e5f1b1fe7 100644 --- a/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml +++ b/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml @@ -18,6 +18,7 @@ @inject ICurrentTenant CurrentTenant @inject IStringLocalizer MultiTenancyStringLocalizer @inject ITenantResolveResultAccessor TenantResolveResultAccessor +@inject IOptions ThemingOptions @{ Layout = null; @@ -34,6 +35,11 @@ + + @if (!ThemingOptions.Value.BaseUrl.IsNullOrWhiteSpace()) + { + + } @(ViewBag.Title == null ? BrandingProvider.AppName : ViewBag.Title) diff --git a/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml b/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml index c694c68424..6823d2b938 100644 --- a/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml +++ b/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml @@ -1,4 +1,6 @@ -@using Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook +@using Microsoft.Extensions.Options +@using Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers +@using Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook @using Volo.Abp.AspNetCore.Mvc.UI.Layout @using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling @using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.ContentTitle @@ -12,6 +14,8 @@ @using Volo.Abp.Ui.LayoutHooks @inject IBrandingProvider BrandingProvider @inject IPageLayout PageLayout +@inject IOptions ThemingOptions + @{ Layout = null; var containerClass = ViewBag.FluidLayout == true ? "container-fluid" : "container"; //TODO: Better and type-safe options @@ -40,6 +44,11 @@ + + @if (!ThemingOptions.Value.BaseUrl.IsNullOrWhiteSpace()) + { + + } @pageTitle diff --git a/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml b/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml index aaef7898c3..c2b2ae447c 100644 --- a/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml +++ b/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml @@ -1,4 +1,6 @@ -@using Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook +@using Microsoft.Extensions.Options +@using Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers +@using Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook @using Volo.Abp.AspNetCore.Mvc.UI.Layout @using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling @using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.PageAlerts @@ -10,6 +12,8 @@ @using Volo.Abp.Ui.LayoutHooks @inject IBrandingProvider BrandingProvider @inject IPageLayout PageLayout +@inject IOptions ThemingOptions + @{ Layout = null; var containerClass = ViewBag.FluidLayout == true ? "container-fluid" : "container"; //TODO: Better and type-safe options @@ -38,6 +42,11 @@ + + @if (!ThemingOptions.Value.BaseUrl.IsNullOrWhiteSpace()) + { + + } @pageTitle @if (ViewBag.Description != null) diff --git a/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/wwwroot/themes/basic/favicon.svg b/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/wwwroot/themes/basic/favicon.svg new file mode 100644 index 0000000000..7610fe9a37 --- /dev/null +++ b/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/wwwroot/themes/basic/favicon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Components/TagHelperProperties/Default.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Components/TagHelperProperties/Default.cshtml new file mode 100644 index 0000000000..bd5ab49ba8 --- /dev/null +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Components/TagHelperProperties/Default.cshtml @@ -0,0 +1,32 @@ +@using System.Reflection + +@model List + +

Properties

+ +
    + @foreach (var property in Model) + { +
  • + @property.Name + @{ + var typeName = property.PropertyType.Name; + if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + typeName = property.PropertyType.GetGenericArguments()[0].Name; + } + } + (@typeName) + @if (property.PropertyType.IsEnum) + { +
      +
    • + + [ @string.Join(", ", Enum.GetNames(property.PropertyType)) ] + +
    • +
    + } +
  • + } +
diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Components/TagHelperProperties/Default.cshtml.cs b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Components/TagHelperProperties/Default.cshtml.cs new file mode 100644 index 0000000000..7103a10833 --- /dev/null +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Components/TagHelperProperties/Default.cshtml.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Components.TagHelperProperties; + +public class TagHelperPropertiesViewComponent : AbpViewComponent +{ + public List Properties { get; } = new(); + public IViewComponentResult Invoke(Type type) + { + foreach (var property in type.GetRuntimeProperties()) + { + if (typeof(AbpTagHelper).IsAssignableFrom(property.DeclaringType) && + property.GetCustomAttribute() == null && + !property.PropertyType.IsAbstract && + property.GetMethod?.IsPublic == true) + { + Properties.Add(property); + } + } + + return View("/Components/TagHelperProperties/Default.cshtml", Properties); + } +} diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Alerts.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Alerts.cshtml index fd4591073e..aed809d33b 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Alerts.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Alerts.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.AlertsModel @{ ViewData["Title"] = "Alerts"; @@ -258,4 +259,8 @@ - \ No newline at end of file + + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpAlertTagHelper)) \ No newline at end of file diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Badges.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Badges.cshtml index 602c752dfd..b097ae1a64 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Badges.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Badges.cshtml @@ -209,4 +209,8 @@ - \ No newline at end of file + + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpBadgeTagHelper)) \ No newline at end of file diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Blockquotes.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Blockquotes.cshtml index 34ab545163..63bfbd519c 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Blockquotes.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Blockquotes.cshtml @@ -30,3 +30,6 @@ +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpBlockquoteTagHelper)) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Borders.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Borders.cshtml index 7473c84bb8..4f7a7a93b6 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Borders.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Borders.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Border @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.BordersModel @{ ViewData["Title"] = "Borders"; @@ -196,3 +197,7 @@ + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpBorderTagHelper)) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Breadcrumbs.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Breadcrumbs.cshtml index 4c4164a823..f7e056ed3b 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Breadcrumbs.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Breadcrumbs.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Breadcrumb @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.BreadcrumbsModel @{ ViewData["Title"] = "Breadcrumbs"; @@ -77,3 +78,7 @@ + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpBreadcrumbTagHelper)) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/ButtonGroups.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/ButtonGroups.cshtml index 94e0d7ee70..36f3f41be3 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/ButtonGroups.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/ButtonGroups.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.ButtonGroupsModel @{ ViewData["Title"] = "ButtonGroups"; @@ -268,4 +269,8 @@ - \ No newline at end of file + + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpButtonGroupTagHelper)) \ No newline at end of file diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Buttons.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Buttons.cshtml index 08d1e0093a..ad6324614c 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Buttons.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Buttons.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.ButtonsModel @{ ViewData["Title"] = "Buttons"; @@ -258,4 +259,8 @@ - \ No newline at end of file + + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpButtonTagHelper)) \ No newline at end of file diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Cards.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Cards.cshtml index 2880f8b179..bfdf80e784 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Cards.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Cards.cshtml @@ -1,5 +1,7 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card +@using Volo.Abp.DependencyInjection @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.CardsModel @{ ViewData["Title"] = "Cards"; @@ -41,7 +43,7 @@

-<div class="card" style="width: 18rem;">
+<div class="card mb-3" style="width: 18rem;">
   <img class="card-img-top" src=".../100px180/" alt="Card image cap">
   <div class="card-body">
     <h5 class="card-title">Card title</h5>
@@ -88,7 +90,7 @@
             
             
                 

-<div class="card" style="width: 18rem;">
+<div class="card mb-3" style="width: 18rem;">
   <div class="card-body">
     <h5 class="card-title">Card title</h5>
     <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
@@ -132,7 +134,7 @@
             
             
                 

-<div class="card" style="width: 18rem;">
+<div class="card mb-3" style="width: 18rem;">
   <ul class="list-group list-group-flush">
     <li class="list-group-item">Cras justo odio</li>
     <li class="list-group-item">Dapibus ac facilisis in</li>
@@ -174,7 +176,7 @@
             
             
                 

-<div class="card" style="width: 18rem;">
+<div class="card mb-3" style="width: 18rem;">
   <div class="card-header">
     Featured
   </div>
@@ -237,7 +239,7 @@
             
             
                 

-<div class="card" style="width: 18rem;">
+<div class="card mb-3" style="width: 18rem;">
   <img class="card-img-top" src=".../100px180/?text=Image cap" alt="Card image cap">
   <div class="card-body">
     <h5 class="card-title">Card title</h5>
@@ -290,7 +292,7 @@
             
             
                 

-<div class="card">
+<div class="card mb-3">
   <div class="card-header">
     Featured
   </div>
@@ -337,7 +339,7 @@
             
             
                 

-<div class="card">
+<div class="card mb-3">
   <div class="card-header">
     Quote
   </div>
@@ -586,4 +588,8 @@
             
         
     
-
\ No newline at end of file
+
+
+
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpCardTagHelper)) \ No newline at end of file diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Cards.cshtml.cs b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Cards.cshtml.cs index 4a5d541668..01323da4d0 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Cards.cshtml.cs +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Cards.cshtml.cs @@ -1,4 +1,10 @@ -using Microsoft.AspNetCore.Mvc.RazorPages; +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Card; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components; @@ -6,6 +12,6 @@ public class CardsModel : PageModel { public void OnGet() { - + } } diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Carousel.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Carousel.cshtml index d1b6cbe0b1..9d44f19453 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Carousel.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Carousel.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.CarouselsModel @{ ViewData["Title"] = "Carousels"; @@ -266,3 +267,6 @@ +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpCarouselTagHelper)) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Collapse.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Collapse.cshtml index 728afc0a1e..b8b01ec60e 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Collapse.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Collapse.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.CollapseModel @{ ViewData["Title"] = "Collapse"; @@ -214,3 +215,28 @@ + +
+ + + + + +

abp-accordion

+
+
+ + @await Component.InvokeAsync("TagHelperProperties", typeof(AbpAccordionTagHelper)) + +
+ + + + +

abp-accordion-item

+
+
+ + @await Component.InvokeAsync("TagHelperProperties", typeof(AbpAccordionItemTagHelper)) + +
diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/DatePicker.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/DatePicker.cshtml index b65eaf15be..800fc4bf36 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/DatePicker.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/DatePicker.cshtml @@ -1,4 +1,4 @@ -@page +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.DatePickerModel

Date Picker & Date Range Picker

@@ -775,5 +775,7 @@ public class DatePickerModel : PageModel +
+@await Component.InvokeAsync("TagHelperProperties", typeof(AbpDatePickerTagHelper)) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Dropdowns.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Dropdowns.cshtml index 3f5717033d..c2d84593cf 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Dropdowns.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Dropdowns.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Dropdown @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.DropdownsModel @{ ViewData["Title"] = "Dropdowns"; @@ -735,4 +736,8 @@ - \ No newline at end of file + + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpDropdownTagHelper)) \ No newline at end of file diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/DynamicForms.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/DynamicForms.cshtml index cbec1ee01c..ab44e878c7 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/DynamicForms.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/DynamicForms.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form @model DynamicFormsModel @{ @@ -428,4 +429,8 @@ public class DynamicFormsModel : PageModel - \ No newline at end of file + + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpDynamicFormTagHelper)) \ No newline at end of file diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/FormElements.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/FormElements.cshtml index e637aebfad..3ed17afd37 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/FormElements.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/FormElements.cshtml @@ -1,6 +1,7 @@ @page -@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components -@model FormElementsModel +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +@model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.FormElementsModel @{ ViewData["Title"] = "Form Elements"; @@ -65,7 +66,7 @@ <input type="password" data-val="true" data-val-required="The Password field is required." id="MyModel_Password" name="MyModel.Password" class="form-control "> <span class="text-danger field-validation-valid" data-valmsg-for="MyModel.Password" data-valmsg-replace="true"></span> </div> -<div class="form-check"> +<div class="mb-2 form-check"> <input type="checkbox" data-val="true" data-val-required="The CheckMeOut field is required." id="MyModel_CheckMeOut" name="MyModel.CheckMeOut" value="true" class="form-check-input "><input name="MyModel.CheckMeOut" type="hidden" value="false"> <label class="form-check-label" for="MyModel_CheckMeOut">Check Me Out</label> </div> @@ -495,4 +496,30 @@ - \ No newline at end of file + + +
+ + + + +

abp-input

+
+
+ + @await Component.InvokeAsync("TagHelperProperties", typeof(AbpInputTagHelper)) + +
+ + + + +

abp-select

+
+
+ + @await Component.InvokeAsync("TagHelperProperties", typeof(AbpSelectTagHelper)) + +
+ + diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Grids.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Grids.cshtml index 0e33d39c59..37a9ad4428 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Grids.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Grids.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.GridsModel @{ ViewData["Title"] = "Grids"; @@ -830,4 +831,38 @@
+ + + + +

abp-container

+
+
+ + @await Component.InvokeAsync("TagHelperProperties", typeof(AbpContainerTagHelper)) + +
+ + + + +

abp-row

+
+
+ + @await Component.InvokeAsync("TagHelperProperties", typeof(AbpRowTagHelper)) + +
+ + + + +

abp-column

+
+
+ + @await Component.InvokeAsync("TagHelperProperties", typeof(AbpColumnTagHelper)) + +
+ < back \ No newline at end of file diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/ListGroup.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/ListGroup.cshtml index ce66b8d5aa..71668d3496 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/ListGroup.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/ListGroup.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.ListGroup @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.ListGroupsModel @{ ViewData["Title"] = "List Groups"; @@ -347,3 +348,7 @@ + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpListGroupTagHelper)) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Modals.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Modals.cshtml index 9e51ef25a3..fa208cb0a8 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Modals.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Modals.cshtml @@ -73,3 +73,7 @@ + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpModalTagHelper)) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Navs.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Navs.cshtml index ca9828d348..28d46eb59e 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Navs.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Navs.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Nav @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.NavsModel @{ ViewData["Title"] = "Navs"; @@ -201,3 +202,31 @@ + +
+ + + + +

abp-nav-bar

+
+
+ + @await Component.InvokeAsync("TagHelperProperties", typeof(AbpNavBarTagHelper)) + +
+ + + + +

abp-nav-item

+
+
+ + @await Component.InvokeAsync("TagHelperProperties", typeof(AbpNavItemTagHelper)) + +
+ + + + diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml index 00666c091a..77fe1530a3 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml @@ -1,4 +1,6 @@ @page +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.PaginatorModel @{ ViewData["Title"] = "Paginator"; @@ -89,3 +91,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpPaginationTagHelper)) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Popovers.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Popovers.cshtml index ddc87ba66d..10fb759853 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Popovers.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Popovers.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Popover @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.PopoversModel @{ ViewData["Title"] = "Popovers"; @@ -69,3 +70,7 @@ + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpPopoverTagHelper)) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/ProgressBars.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/ProgressBars.cshtml index 0390090e9a..3566c194db 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/ProgressBars.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/ProgressBars.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.ProgressBar @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.ProgressBarsModel @{ ViewData["Title"] = "Progress Bars"; @@ -77,3 +78,7 @@ + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpProgressBarTagHelper)) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Tables.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Tables.cshtml index e39fe8cae3..f73a6d2ca5 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Tables.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Tables.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Table @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.TablesModel @{ ViewData["Title"] = "Tables"; @@ -449,3 +450,7 @@ + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpTableTagHelper)) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Tabs.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Tabs.cshtml index e9631ed179..a0efeb0c0a 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Tabs.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Tabs.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Tab @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.TabsModel @{ ViewData["Title"] = "Tabs"; @@ -293,4 +294,39 @@ - \ No newline at end of file + + +
+ + + + +

abp-tab

+
+
+ + @await Component.InvokeAsync("TagHelperProperties", typeof(AbpTabTagHelper)) + +
+ + + + +

abp-tab-link

+
+
+ + @await Component.InvokeAsync("TagHelperProperties", typeof(AbpTabLinkTagHelper)) + +
+ + + + +

abp-tab-dropdown

+
+
+ + @await Component.InvokeAsync("TagHelperProperties", typeof(AbpTabDropdownTagHelper)) + +
diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Tooltips.cshtml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Tooltips.cshtml index f19161d515..7bbf1cd2da 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Tooltips.cshtml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Tooltips.cshtml @@ -1,5 +1,6 @@ @page @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Tooltip @model Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components.TooltipsModel @{ ViewData["Title"] = "Tooltips"; @@ -84,3 +85,7 @@ + +
+ +@await Component.InvokeAsync("TagHelperProperties", typeof(AbpTooltipTagHelper)) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Menus/BootstrapDemoMenuContributor.cs b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Menus/BootstrapDemoMenuContributor.cs index e36f329306..625fb60a80 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Menus/BootstrapDemoMenuContributor.cs +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Menus/BootstrapDemoMenuContributor.cs @@ -25,6 +25,7 @@ public class BootstrapDemoMenuContributor : IMenuContributor { new ApplicationMenuItem(BootstrapDemoMenus.Components.Alerts, "Alerts", url: "/Components/Alerts"), new ApplicationMenuItem(BootstrapDemoMenus.Components.Badges, "Badges", url: "/Components/Badges"), + new ApplicationMenuItem(BootstrapDemoMenus.Components.Blockquotes, "Blockquotes", url: "/Components/Blockquotes"), new ApplicationMenuItem(BootstrapDemoMenus.Components.Borders, "Borders", url: "/Components/Borders"), new ApplicationMenuItem(BootstrapDemoMenus.Components.Breadcrumbs, "Breadcrumbs", url: "/Components/Breadcrumbs"), new ApplicationMenuItem(BootstrapDemoMenus.Components.Buttons, "Buttons", url: "/Components/Buttons"), diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Menus/BootstrapDemoMenus.cs b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Menus/BootstrapDemoMenus.cs index 2f381221ed..7a758523af 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Menus/BootstrapDemoMenus.cs +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Menus/BootstrapDemoMenus.cs @@ -11,6 +11,7 @@ public class BootstrapDemoMenus public const string Alerts = Root + ".Alerts"; public const string Badges = Root + ".Badges"; public const string Borders = Root + ".Borders"; + public const string Blockquotes = Root + ".Blockquotes"; public const string Breadcrumbs = Root + ".Breadcrumbs"; public const string ButtonGroups = Root + ".ButtonGroups"; public const string Buttons = Root + ".Buttons"; diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Program.cs b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Program.cs index ad3a39c746..ac9601cda3 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Program.cs +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Program.cs @@ -1,31 +1,42 @@ using System; -using System.IO; -using Microsoft.AspNetCore.Hosting; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Serilog; -using Serilog.Events; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo; public class Program { - public static int Main(string[] args) + public async static Task Main(string[] args) { Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() //TODO: Should be configurable! - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .MinimumLevel.Debug() .Enrich.FromLogContext() - .WriteTo.File("Logs/logs.txt") + .WriteTo.Async(c => c.Console()) .CreateLogger(); try { Log.Information("Starting web host."); - CreateHostBuilder(args).Build().Run(); + var builder = WebApplication.CreateBuilder(args); + builder.Host.AddAppSettingsSecretsJson() + .UseAutofac() + .UseSerilog(); + await builder.AddApplicationAsync(); + var app = builder.Build(); + await app.InitializeApplicationAsync(); + await app.RunAsync(); return 0; } catch (Exception ex) { + if (ex is HostAbortedException) + { + throw; + } + Log.Fatal(ex, "Host terminated unexpectedly!"); return 1; } @@ -34,14 +45,4 @@ public class Program Log.CloseAndFlush(); } } - - - internal static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }) - .UseAutofac() - .UseSerilog(); } diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Properties/launchSettings.json b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Properties/launchSettings.json index 07a310a660..7ad9d24119 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Properties/launchSettings.json +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Properties/launchSettings.json @@ -1,27 +1,12 @@ { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:51339", - "sslPort": 0 - } - }, "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.AbpIo.Commercial.Web": { + "Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo": { "commandName": "Project", "launchBrowser": true, - "applicationUrl": "http://localhost:5000", + "applicationUrl": "https://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } -} \ No newline at end of file +} diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Startup.cs b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Startup.cs deleted file mode 100644 index 838cf43402..0000000000 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Startup.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo; - -public class Startup -{ - public void ConfigureServices(IServiceCollection services) - { - services.AddApplication(); - } - - public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) - { - app.InitializeApplication(); - } -} diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj index f8202b97ec..9aa8142fda 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj @@ -10,7 +10,7 @@ - + @@ -23,5 +23,5 @@ Always - + diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/azure-pipelines.yml b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/azure-pipelines.yml index 235ef306de..f121045d83 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/azure-pipelines.yml +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/azure-pipelines.yml @@ -13,7 +13,7 @@ resources: variables: # Container registry service connection established during pipeline creation - dockerRegistryServiceConnection: 'volosoft-reg' + dockerRegistryServiceConnection: 'volosoft-reg1' workDir: '$(Build.SourcesDirectory)' bootstrapTaghelpersDir: '$(workDir)/abp/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo' # tag: $[replace(variables['Build.SourceBranch'], 'refs/tags/', '')] diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json index ec32f363dc..aec1b7a084 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json @@ -3,8 +3,8 @@ "name": "asp.net", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.shared": "~9.1.0", - "@abp/prismjs": "~9.1.0", - "@abp/highlight.js": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.shared": "~9.2.0-rc.2", + "@abp/prismjs": "~9.2.0-rc.2", + "@abp/highlight.js": "~9.2.0-rc.2" } } diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock index 52db1821a9..0f4ff2d889 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock @@ -2,212 +2,203 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.shared@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.1.0.tgz#35a73580b9b67a33708f2cf897243b50c9ad7100" - integrity sha512-rggTYL9poOFICsOseDX22JNhD0KWH8Oxs0YH91TZjvm/F67cPemW99eBlJc/YdJ7bN6icRiebN8m81y9MSanyg== - dependencies: - "@abp/aspnetcore.mvc.ui" "~9.1.0" - "@abp/bootstrap" "~9.1.0" - "@abp/bootstrap-datepicker" "~9.1.0" - "@abp/bootstrap-daterangepicker" "~9.1.0" - "@abp/datatables.net-bs5" "~9.1.0" - "@abp/font-awesome" "~9.1.0" - "@abp/jquery-form" "~9.1.0" - "@abp/jquery-validation-unobtrusive" "~9.1.0" - "@abp/lodash" "~9.1.0" - "@abp/luxon" "~9.1.0" - "@abp/malihu-custom-scrollbar-plugin" "~9.1.0" - "@abp/moment" "~9.1.0" - "@abp/select2" "~9.1.0" - "@abp/sweetalert2" "~9.1.0" - "@abp/timeago" "~9.1.0" - "@abp/toastr" "~9.1.0" - -"@abp/aspnetcore.mvc.ui@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.1.0.tgz#38d058ab148a36ad4d63870e8a501ca339e4a70e" - integrity sha512-lUw1ij87KJp+Rj4cz8oSQkdRuoMq76l/wIAGwtXH+EOKusVpSJTOTioAV/tPKeKZ15ubE4bO5jWcQGxZkXgH3w== +"@abp/aspnetcore.mvc.ui.theme.shared@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.2.0-rc.2.tgz#77562013b604d0193e361efa6801235179dc7b4f" + integrity sha512-5KcMGg6o6W5uwsujrjUARlZFgWmZeYBNCf1vkf3btakkwTzmPDbypUx6VrgBJbt4AUnYlMQbSHvbfK0d3I1e6w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~9.2.0-rc.2" + "@abp/bootstrap" "~9.2.0-rc.2" + "@abp/bootstrap-datepicker" "~9.2.0-rc.2" + "@abp/bootstrap-daterangepicker" "~9.2.0-rc.2" + "@abp/datatables.net-bs5" "~9.2.0-rc.2" + "@abp/font-awesome" "~9.2.0-rc.2" + "@abp/jquery-form" "~9.2.0-rc.2" + "@abp/jquery-validation-unobtrusive" "~9.2.0-rc.2" + "@abp/lodash" "~9.2.0-rc.2" + "@abp/luxon" "~9.2.0-rc.2" + "@abp/malihu-custom-scrollbar-plugin" "~9.2.0-rc.2" + "@abp/moment" "~9.2.0-rc.2" + "@abp/select2" "~9.2.0-rc.2" + "@abp/sweetalert2" "~9.2.0-rc.2" + "@abp/timeago" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.2.0-rc.2.tgz#e1c0e4b0280d5fe37659da8dc696150f0d461336" + integrity sha512-WG4WWGS1mSb2QWAVOA2nrvoi9/3oueEaKEG18xbPgWiLz5GCq8gsopKqUgW/fmBaw+1Jh9tsuCTH7eED/2tUxA== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.1.0.tgz#96a1875a2b92f03b8f90f7842ebf8849cf8a4d04" - integrity sha512-3FQm1SK8qmzoP7LOvXkZpq1AzrdPHKRdQJjoqK9N+lQxQjNfOxEFMxeDSg6WJKO619Cnve++jAyMidojXlCiwQ== +"@abp/bootstrap-datepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.2.0-rc.2.tgz#429c2d44a22f9190013aea0efa420854b7e83a4f" + integrity sha512-bxMpaxTgnF5/VELbKvtZVw0tI0bowzUQFVSBQJqO4o7wJmxyHNzJuaom8Is+tCsoMrpo8t9EGMGfpWHV1IsrjQ== dependencies: bootstrap-datepicker "^1.10.0" -"@abp/bootstrap-daterangepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.1.0.tgz#1993c539837eb31de174cba9f0bccd441a98f209" - integrity sha512-jS1jTqP6KCXQYX9xZpDAI07C94nwzXrNJD3H0pJXsXoOva3TTBQDD8khf6RDb7tiZrkrNJqmMyUiCfVTDbO7zg== +"@abp/bootstrap-daterangepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.2.0-rc.2.tgz#85bb869d5d43eb6c97f50943ea2c390cb4d37d54" + integrity sha512-rLLsY6GCY16IOwRkmx7vZ25sVHOM9dPdTlcZiuUuEi5+QrmgGiqZmIwqYiu4WFSPPTJtufsyMaHzXxqiZRpXVg== dependencies: bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.1.0.tgz#a06c1cd4c11554c29973db70c6a383efebb10b4e" - integrity sha512-r6onkBnpqoWrdYLDVZTiqgQBvH5UZerYRrPB3Ky+RkXJhgwn7AM3LAVrB42sIvFZRNbwOlvuAL+a0+5yDOH67Q== +"@abp/bootstrap@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.2.0-rc.2.tgz#ba2ab98d930501e7f004f99e7d7e7d4e6d3c0998" + integrity sha512-u2mtHHbmXNgngExl4hM5uo/E3k453n0im2Ci02+4dnhZoFicLZlBdtJusZAw5QR1XTLPybW6RXofWBvkZcgiwg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" bootstrap "^5.3.3" -"@abp/clipboard@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.1.0.tgz#5d5678ef7e8c355715c5a74e4aabd0a21d5e6cbe" - integrity sha512-1TxZbB/q5DPeLhYzrc6y0OVekxnjM+CRH/Gu8nAXQMX46XjdTboXvBtvgKHV1aCznGNsWyjd5A2O5G/XGg9NNw== +"@abp/clipboard@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.2.0-rc.2.tgz#608bdb3d4a3ff1a7b38ad5850385746f7cfe6c0c" + integrity sha512-w/JiTmidNc7SZuvpkQumyNj6FTBq6hLnhXeBBh4/xega1Ob2q+tbrOSpemgpK6YOXUQbmH8a4vEPvwt7uzNfvQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" clipboard "^2.0.11" -"@abp/core@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.1.0.tgz#af78fc096932d4ec45216b5e910c87a72e479b1f" - integrity sha512-6P0KGh5+IqlhQnUOyzaGaKrKzOAm1G0gg8Ay3DXyW2ksY6Zr+lyi5cxNsSi4ix1dicqfydIKvLfV+DF3mQf/dw== +"@abp/core@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.2.0-rc.2.tgz#0eab20842f8c0801d1dca036d3d66a67e155e019" + integrity sha512-8bC1MAV4iSFLJvRvM5mvrI9+N5FPAjaG79hr3p0xJtNixFOFpNELDf4p0zwy97zY8juL+AIEyJAkg+KIIK90DQ== dependencies: - "@abp/utils" "~9.1.0" + "@abp/utils" "~9.2.0-rc.2" -"@abp/datatables.net-bs5@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.1.0.tgz#3b2e7d68e947db3d3432ab5e8bff4da89a85d05d" - integrity sha512-zBrzOrfvTPJcc6mvHZDPVdXbWesKU0lKlrb7vuBZxWjrFNwVC0d/KNJPcYVvyQ3zf9rrQB5Xg0MDgu0k5Tt7tQ== +"@abp/datatables.net-bs5@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.2.0-rc.2.tgz#8c07544cafd2d2257f45150d3265c25eafc0ec0f" + integrity sha512-l3VOtc0fleUsx+ZUMr49+nPBWRBUVw4Z26ogT7O7uRR8doeerNU1GfA4yFJCUp02WIwt8cmZ+apJHADykn9Vng== dependencies: - "@abp/datatables.net" "~9.1.0" + "@abp/datatables.net" "~9.2.0-rc.2" datatables.net-bs5 "^2.1.8" -"@abp/datatables.net@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.1.0.tgz#12d6e1412aac4a2e0a61437dcdfbfbe1add7fed7" - integrity sha512-lYRvEZUkntaI+dS9Xkz23Z54eDeZ1R4Wzhqcg6VIsz3iO5kYAa5GiQjcOiBssNGJSLuaet6R5Pqh1ThEUjfvvA== +"@abp/datatables.net@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.2.0-rc.2.tgz#2880ccb61750faa1689aec15f9e49d92ab1e94a1" + integrity sha512-nljq0p8mxoBvMZngtViInyQJqX/MI7EyQeCIYNICF2LO42RRZfpKaRy5zX+4Uc3z/wz209CfK8QfRNH/d1HtBQ== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" datatables.net "^2.1.8" -"@abp/font-awesome@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.1.0.tgz#af21ff2d7c332deb1df626404b53a78d790ba842" - integrity sha512-HcJzDCACGejOIvhxsorCdaesyDHnupR/sl5YFCsGikSqATyOSKQy4pw8wKsb1z+FCT5JYCTR7ROJ3lB1lTEgZw== +"@abp/font-awesome@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.2.0-rc.2.tgz#51eb750f5697668db538ad68cc6b9279517da537" + integrity sha512-X9JfF4n4MDZLszwZHIHuxSdGGxImEzV91ROKqNeclErCx3bOXyq/l9C+haZl/3TLzTAOSDMLgHGrT3MUCVPF5w== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" "@fortawesome/fontawesome-free" "^6.6.0" -"@abp/highlight.js@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-9.1.0.tgz#baed72c6ca38fd888b49174e632788c681306dce" - integrity sha512-kV6BFYESIIOJOJ1I9I3xUdLftrIkK8vZlIaBupJobxzNeOXh4/VRIVJE75xRXyMSmJRSMOZm8pjNoQsuKDgT3w== +"@abp/highlight.js@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-9.2.0-rc.2.tgz#9865cae50e39cca9ddd0c0390da794778ed36779" + integrity sha512-43NEl2omc8HWJjprEJpcMtb88QCD9bTCXrW7e2CO8ReyFuEoQh+79c0q2E5xJ5aitZpFhq4j4OOxxzUvAKORKw== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" "@highlightjs/cdn-assets" "~11.10.0" -"@abp/jquery-form@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.1.0.tgz#bdb96436c54b2a09ac4259db97e1e577410e1fc3" - integrity sha512-xjHXWstVY6B3MPB4/8g0EJeHlVvlV010OMZKFmfCGLFi6aHAEKqqWh1rMItAiVuSmCCzL0m/208ZkpoCI++jGQ== +"@abp/jquery-form@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.2.0-rc.2.tgz#58ce31a32bdfb355e5d4e3f97cb4761f594aa8e1" + integrity sha512-xN2xjK1RWKygwaqzTaWmpMlU0kGWGa2nKaudM6g9k/B6b1Pfdr8ykI4DaSuPb4BkRmurGkgy4r9o7IUoqv0T8Q== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-form "^4.3.0" -"@abp/jquery-validation-unobtrusive@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.1.0.tgz#798551e7c14ba286a819a48804107a53e974c1ee" - integrity sha512-TggKckfbLtB7Ig2kNt2kH//GaJUns8OZbpdKdh7vCWIAGedw2FLNCABCpoGnyNp0s/dVOtdyxRl9eFRN3+a4tw== +"@abp/jquery-validation-unobtrusive@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.2.0-rc.2.tgz#4e624e22cbf149d1c78da46aabaf6fc18d3b65ff" + integrity sha512-adKJxnWnhCrIYWoVdo41zgO5p4SjyypCxoHeQ7mkohry2WN6WuQG5nmHqhpdevw966DENvmVzFfz7vnO72vtrg== dependencies: - "@abp/jquery-validation" "~9.1.0" + "@abp/jquery-validation" "~9.2.0-rc.2" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.1.0.tgz#b771652377fbfb3c92ce03b7b7c069a3bb2ae70b" - integrity sha512-bmPSOHcxjitB8NkiLv0SxxuSS3FlCq9SiTUGvsT8nOStDsm+KpyYSzZQq8LSCyqWWj54tlOARTfcD4tMK7/K3Q== +"@abp/jquery-validation@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.2.0-rc.2.tgz#b6a8124d5056ddad515733bffc78703f6bd422be" + integrity sha512-4j1fg1qImif2vFZQFJ7z1olEUnjc3BByKS+7joNQ+96/NqOQdOQjRjB2n490od7ExCinKnvYx3JFttpYjHU3Xw== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-validation "^1.21.0" -"@abp/jquery@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.1.0.tgz#952b61ea3e4a99e6551f667297dbb637d83f3c50" - integrity sha512-Ulw8ClAbqmxceNYCb76bR6dq7e9b7Hs6mjlEy05zdbsSNuZW3UoycsJ+BquXi+dUNBo6OppqEmhVeiIjAKTaFg== +"@abp/jquery@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.2.0-rc.2.tgz#335643787ab282d5d2fee4f360c2b62cce3db962" + integrity sha512-baKNDuiQMzkV0LFzBwEqS0JYtXIUe5KH+MVizGR6Zv10B8l/gIrUzWI/nnqDtbts6gmY9OBDXkCTcgMQBwKqjw== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" jquery "~3.7.1" -"@abp/lodash@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.1.0.tgz#62c784bd99c9e1ccfa82905e843375c594ef8584" - integrity sha512-cCE3ZwwWcPrh+ccKx4AS1USL1ttbrXBqci/l+ROjXhFzrs6GYnhI0+30xRZgPdPRm3U3nGPrSaDAU7UuHkRScg== +"@abp/lodash@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.2.0-rc.2.tgz#521bccafcc09780c751932688b6ec45c368c2854" + integrity sha512-T6eYsDovUl4AYD9n2N4WZq9MeC4/ryke5ctvdhzuArqM7WpPvohq3VKGugrP/MLo9oD3MZ5DVQvMJ0AVg+IXNQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" lodash "^4.17.21" -"@abp/luxon@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.1.0.tgz#b246f3627bbc7ce41b7878fce1679da473b0e0ae" - integrity sha512-AouS/qEK+i69hwr4XtUOCepONLuR+1gdHFb50RpOEbUoDfTYmYpxPBdiICmZPeJ3CExErITPZxzzngCnKPPOUw== +"@abp/luxon@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.2.0-rc.2.tgz#da366d335f9575b104ea2206e076af35cbbfcfba" + integrity sha512-mr8ipYrJDDJPoImIwORK7Z6d1C07APl95L17ufRbig4sjdxjNEDR5uUXVviVafF1No2bVkYiS0xGKQmojA2twg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" luxon "^3.5.0" -"@abp/malihu-custom-scrollbar-plugin@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.1.0.tgz#288ae1916a78100f2c728dfd05a560ee99fafae4" - integrity sha512-McdMRDtbTZBjp//3GMVjRjkc16HNYDEdPc2ar1xrY817e0tEln6/TBkItAcfOvjso0TsLcvlNL1+1bjbMjj8OA== +"@abp/malihu-custom-scrollbar-plugin@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.2.0-rc.2.tgz#cb79018adf08276041eb5c5dcf75130c8995686b" + integrity sha512-jdHXx7kPduaEfFXDvf5s+QVl10RqshNbnZkkqPx0mMkhz2H/qxwzF1e0mcLFL38nEgs+0gxIkcA8/MKq85tyqQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.1.0.tgz#f3c0ae02209e496913cda53df752dabc5be648bd" - integrity sha512-WxR1l09vwxRhqyvAVP4m4IBOFjIC4pzLOmelloqES+gL/klw+T/AmY21Z/z+OV0p7aJ3V3Q1aQStuMCDCyGFnQ== +"@abp/moment@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.2.0-rc.2.tgz#f4339138668ca72be1d700624b91789344557bff" + integrity sha512-J4ZaCDEcXLGmJ+8zqgaMK9Lutnjga28LSbuiFaGLlZ1QgYZ5HDScE128o3vX9qr60tc6IIY0cDkHi4bBHo7ozw== dependencies: moment "^2.30.1" -"@abp/prismjs@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.1.0.tgz#40b688c82ae808ef92516ea5731ccb38c19d2384" - integrity sha512-trSW1K6VtIEaAyxe7umQGimCTLrgdT6DwSiQ8PONvKa8WR5ZmzSDU7lK8SliorufkbWtdkjCvuXB81NGt3tAhQ== +"@abp/prismjs@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.2.0-rc.2.tgz#1ee12acdd9e1dcc9ae2ce4a9f21f159c3f9e3dec" + integrity sha512-GuKz6bujBlYHAgLhW1RT7aEOwKyZ+vnyMIhN0/fQ34+nbqg0PC4htwJcN6spgN8gZKD06lCqBH+GjLE2WtE/Rw== dependencies: - "@abp/clipboard" "~9.1.0" - "@abp/core" "~9.1.0" + "@abp/clipboard" "~9.2.0-rc.2" + "@abp/core" "~9.2.0-rc.2" prismjs "^1.29.0" -"@abp/select2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.1.0.tgz#9c2c6751f8506fb375c9b7c42a008c168a8f4c75" - integrity sha512-n6HqsH8aFLMrqL6LJ0gsHFlaq6T1QSt/x7HkkZyhDC7CwhXajxtsJEstDnDF9naHSXX/lerPbrHEgzn2QRMBqg== +"@abp/select2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.2.0-rc.2.tgz#ba6e839644b7ecdd198fe5286101302a3dc79ce3" + integrity sha512-AQMXb5ATgbN7grpKU8lB2xFFfirCe45lIvd9krW5SwUHCYZP7pRPbwQXAew+BqlIIYJJF2GA7/Nf//e/71bKtg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" select2 "^4.0.13" -"@abp/sweetalert2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.1.0.tgz#660f9b1db60cc04bde6cadbcf3c55320d20c2f3e" - integrity sha512-z3JMeAwva2vR8qZSRJ/BisQDdAEqjDJ/6oIGu/JYXQEchO1Lnd9UMV376nc3crLJdnyRRO7Qs6zx4FfVOCH0kw== +"@abp/sweetalert2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.2.0-rc.2.tgz#075634dc3ec996607757694d636d276ba2350e06" + integrity sha512-ZgHgqM8Q+V9ECQyAyepM5KpshKpQP463xRHwgY45oR7/CfCcO/vS56YQgJNg2zE6+lRQZaRQ9OL7lph8975vVg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" sweetalert2 "^11.14.1" -"@abp/timeago@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.1.0.tgz#496e40153241bae5d418d79cc6c65c9db6306447" - integrity sha512-a/UAYQ2GrhYFGx4p0hqr3x+teBqnjnOiWJEPq9//RTlz+EZ+OUyHbJrcUtqZRvaGJtOAsfmljD3jYnOXUlU7DA== +"@abp/timeago@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.2.0-rc.2.tgz#25324732966438bd6f505bdd932511ba2cc8c22b" + integrity sha512-WfWxxiycg77vVN2hPHqUOu552la+zxr0EZnHMqnEnnBe+gFYe9C0CpdYuyBq2eQXRXHoVzsB+4YRVNk5sbSGng== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" timeago "^1.6.7" -"@abp/toastr@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.1.0.tgz#501cbe211164b2e7416a2d4878cb34aed3550b7c" - integrity sha512-BkTDuU0p9Qa4+HT9CYwiPQjVTrbTSNBgQ18h69S3lIadLJwdC6pXeLbL9bhe+5fiuhq2f+0Oqo3X75LNEgzHkw== - dependencies: - "@abp/jquery" "~9.1.0" - toastr "^2.1.4" - -"@abp/utils@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.1.0.tgz#a4fd37d943df18b5c11f5d4869efd5d8882b7959" - integrity sha512-+UZo0ocTEZlRO41SzzHNdinjdFUDVq8wYZSj5BA1bqCVeWYhfjI2+rl9DuogCN8UEuCACA4K/qb4XcE/untQ3g== +"@abp/utils@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.2.0-rc.2.tgz#669885ad1e3ac7b8e182d7c392ea1b633df65aac" + integrity sha512-rfQxhJy04Qf50P3ciasKbDlJ+9JidD60xW77TjcoUJvljJE+A8iwTX8Wwqz7+YcC7rks2ILM4y3Ay7/47NOICQ== dependencies: just-compare "^2.3.0" @@ -307,7 +298,7 @@ jquery-validation@>=1.19, jquery-validation@^1.21.0: resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.21.0.tgz#78fc05ab76020912a246af3661b3f54a438bca93" integrity sha512-xNot0rlUIgu7duMcQ5qb6MGkGL/Z1PQaRJQoZAURW9+a/2PGOUxY36o/WyNeP2T9R6jvWB8Z9lUVvvQWI/Zs5w== -jquery@>=1.10, jquery@>=1.12.0, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2, "jquery@>=3.4.0 <4.0.0", jquery@^3.6.0, jquery@~3.7.1: +jquery@>=1.10, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2, "jquery@>=3.4.0 <4.0.0", jquery@^3.6.0, jquery@~3.7.1: version "3.7.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de" integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== @@ -370,10 +361,3 @@ tiny-emitter@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== - -toastr@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/toastr/-/toastr-2.1.4.tgz#8b43be64fb9d0c414871446f2db8e8ca4e95f181" - integrity sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA== - dependencies: - jquery ">=1.12.0" diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/AbpAspNetCoreMvcUiThemeBasicDemoModule.cs b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/AbpAspNetCoreMvcUiThemeBasicDemoModule.cs index 4ebeb18498..c887825b2b 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/AbpAspNetCoreMvcUiThemeBasicDemoModule.cs +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/AbpAspNetCoreMvcUiThemeBasicDemoModule.cs @@ -55,8 +55,8 @@ public class AbpAspNetCoreMvcUiThemeBasicDemoModule : AbpModule app.UseDeveloperExceptionPage(); } - app.MapAbpStaticAssets(); app.UseRouting(); + app.MapAbpStaticAssets(); app.UseConfiguredEndpoints(); } } diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Program.cs b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Program.cs index a27f797715..d2ca3053b8 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Program.cs +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Program.cs @@ -1,30 +1,42 @@ using System; -using Microsoft.AspNetCore.Hosting; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Serilog; -using Serilog.Events; namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo; public class Program { - public static int Main(string[] args) + public async static Task Main(string[] args) { Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .Enrich.FromLogContext() - .WriteTo.Async(c => c.File("Logs/logs.txt")) .WriteTo.Async(c => c.Console()) .CreateLogger(); try { Log.Information("Starting web host."); - CreateHostBuilder(args).Build().Run(); + var builder = WebApplication.CreateBuilder(args); + builder.Host.AddAppSettingsSecretsJson() + .UseAutofac() + .UseSerilog(); + await builder.AddApplicationAsync(); + var app = builder.Build(); + await app.InitializeApplicationAsync(); + await app.RunAsync(); return 0; } catch (Exception ex) { + if (ex is HostAbortedException) + { + throw; + } + Log.Fatal(ex, "Host terminated unexpectedly!"); return 1; } @@ -33,14 +45,4 @@ public class Program Log.CloseAndFlush(); } } - - - internal static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }) - .UseAutofac() - .UseSerilog(); } diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Properties/launchSettings.json b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Properties/launchSettings.json index 9bee4298ff..4ae3999794 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Properties/launchSettings.json +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Properties/launchSettings.json @@ -1,24 +1,9 @@ { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:61659", - "sslPort": 0 - } - }, "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, "Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo": { "commandName": "Project", "launchBrowser": true, - "applicationUrl": "http://localhost:5000", + "applicationUrl": "https://localhost:5001", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Startup.cs b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Startup.cs deleted file mode 100644 index a11ca855da..0000000000 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Startup.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo; - -public class Startup -{ - public void ConfigureServices(IServiceCollection services) - { - services.AddApplication(); - } - - public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) - { - app.InitializeApplication(); - } -} diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json index ba5af1ba94..94e80fd62f 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json @@ -3,8 +3,8 @@ "name": "asp.net", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0", - "@abp/prismjs": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2", + "@abp/prismjs": "~9.2.0-rc.2" }, "devDependencies": {} } diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock index 7f5281eb00..f48539b111 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock @@ -2,211 +2,202 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.1.0.tgz#e9e0321037c34b43145dfc7827c422fbf79f59ab" - integrity sha512-vLkyrdFXw0/MPyU2hzqGJJk7trmIlVMdflNIe/rF77UIRmJYHWaTmPt9i5PLN/O4s0jnE9kWlaaJ/VnnFWeGsQ== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~9.1.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.1.0.tgz#35a73580b9b67a33708f2cf897243b50c9ad7100" - integrity sha512-rggTYL9poOFICsOseDX22JNhD0KWH8Oxs0YH91TZjvm/F67cPemW99eBlJc/YdJ7bN6icRiebN8m81y9MSanyg== - dependencies: - "@abp/aspnetcore.mvc.ui" "~9.1.0" - "@abp/bootstrap" "~9.1.0" - "@abp/bootstrap-datepicker" "~9.1.0" - "@abp/bootstrap-daterangepicker" "~9.1.0" - "@abp/datatables.net-bs5" "~9.1.0" - "@abp/font-awesome" "~9.1.0" - "@abp/jquery-form" "~9.1.0" - "@abp/jquery-validation-unobtrusive" "~9.1.0" - "@abp/lodash" "~9.1.0" - "@abp/luxon" "~9.1.0" - "@abp/malihu-custom-scrollbar-plugin" "~9.1.0" - "@abp/moment" "~9.1.0" - "@abp/select2" "~9.1.0" - "@abp/sweetalert2" "~9.1.0" - "@abp/timeago" "~9.1.0" - "@abp/toastr" "~9.1.0" - -"@abp/aspnetcore.mvc.ui@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.1.0.tgz#38d058ab148a36ad4d63870e8a501ca339e4a70e" - integrity sha512-lUw1ij87KJp+Rj4cz8oSQkdRuoMq76l/wIAGwtXH+EOKusVpSJTOTioAV/tPKeKZ15ubE4bO5jWcQGxZkXgH3w== +"@abp/aspnetcore.mvc.ui.theme.basic@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.2.0-rc.2.tgz#e32c8bd046d7377ae3f6a703eac9069e10bcd600" + integrity sha512-oj4lGrzllgGrq7rI7gz1Xvq5TU+xT+isGVf4T8hF3INE/J2nIyK45HnkjDq5wpdM3P+HkdM1+RtWNrLSVu0Klg== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.2.0-rc.2.tgz#77562013b604d0193e361efa6801235179dc7b4f" + integrity sha512-5KcMGg6o6W5uwsujrjUARlZFgWmZeYBNCf1vkf3btakkwTzmPDbypUx6VrgBJbt4AUnYlMQbSHvbfK0d3I1e6w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~9.2.0-rc.2" + "@abp/bootstrap" "~9.2.0-rc.2" + "@abp/bootstrap-datepicker" "~9.2.0-rc.2" + "@abp/bootstrap-daterangepicker" "~9.2.0-rc.2" + "@abp/datatables.net-bs5" "~9.2.0-rc.2" + "@abp/font-awesome" "~9.2.0-rc.2" + "@abp/jquery-form" "~9.2.0-rc.2" + "@abp/jquery-validation-unobtrusive" "~9.2.0-rc.2" + "@abp/lodash" "~9.2.0-rc.2" + "@abp/luxon" "~9.2.0-rc.2" + "@abp/malihu-custom-scrollbar-plugin" "~9.2.0-rc.2" + "@abp/moment" "~9.2.0-rc.2" + "@abp/select2" "~9.2.0-rc.2" + "@abp/sweetalert2" "~9.2.0-rc.2" + "@abp/timeago" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.2.0-rc.2.tgz#e1c0e4b0280d5fe37659da8dc696150f0d461336" + integrity sha512-WG4WWGS1mSb2QWAVOA2nrvoi9/3oueEaKEG18xbPgWiLz5GCq8gsopKqUgW/fmBaw+1Jh9tsuCTH7eED/2tUxA== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.1.0.tgz#96a1875a2b92f03b8f90f7842ebf8849cf8a4d04" - integrity sha512-3FQm1SK8qmzoP7LOvXkZpq1AzrdPHKRdQJjoqK9N+lQxQjNfOxEFMxeDSg6WJKO619Cnve++jAyMidojXlCiwQ== +"@abp/bootstrap-datepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.2.0-rc.2.tgz#429c2d44a22f9190013aea0efa420854b7e83a4f" + integrity sha512-bxMpaxTgnF5/VELbKvtZVw0tI0bowzUQFVSBQJqO4o7wJmxyHNzJuaom8Is+tCsoMrpo8t9EGMGfpWHV1IsrjQ== dependencies: bootstrap-datepicker "^1.10.0" -"@abp/bootstrap-daterangepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.1.0.tgz#1993c539837eb31de174cba9f0bccd441a98f209" - integrity sha512-jS1jTqP6KCXQYX9xZpDAI07C94nwzXrNJD3H0pJXsXoOva3TTBQDD8khf6RDb7tiZrkrNJqmMyUiCfVTDbO7zg== +"@abp/bootstrap-daterangepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.2.0-rc.2.tgz#85bb869d5d43eb6c97f50943ea2c390cb4d37d54" + integrity sha512-rLLsY6GCY16IOwRkmx7vZ25sVHOM9dPdTlcZiuUuEi5+QrmgGiqZmIwqYiu4WFSPPTJtufsyMaHzXxqiZRpXVg== dependencies: bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.1.0.tgz#a06c1cd4c11554c29973db70c6a383efebb10b4e" - integrity sha512-r6onkBnpqoWrdYLDVZTiqgQBvH5UZerYRrPB3Ky+RkXJhgwn7AM3LAVrB42sIvFZRNbwOlvuAL+a0+5yDOH67Q== +"@abp/bootstrap@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.2.0-rc.2.tgz#ba2ab98d930501e7f004f99e7d7e7d4e6d3c0998" + integrity sha512-u2mtHHbmXNgngExl4hM5uo/E3k453n0im2Ci02+4dnhZoFicLZlBdtJusZAw5QR1XTLPybW6RXofWBvkZcgiwg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" bootstrap "^5.3.3" -"@abp/clipboard@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.1.0.tgz#5d5678ef7e8c355715c5a74e4aabd0a21d5e6cbe" - integrity sha512-1TxZbB/q5DPeLhYzrc6y0OVekxnjM+CRH/Gu8nAXQMX46XjdTboXvBtvgKHV1aCznGNsWyjd5A2O5G/XGg9NNw== +"@abp/clipboard@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.2.0-rc.2.tgz#608bdb3d4a3ff1a7b38ad5850385746f7cfe6c0c" + integrity sha512-w/JiTmidNc7SZuvpkQumyNj6FTBq6hLnhXeBBh4/xega1Ob2q+tbrOSpemgpK6YOXUQbmH8a4vEPvwt7uzNfvQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" clipboard "^2.0.11" -"@abp/core@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.1.0.tgz#af78fc096932d4ec45216b5e910c87a72e479b1f" - integrity sha512-6P0KGh5+IqlhQnUOyzaGaKrKzOAm1G0gg8Ay3DXyW2ksY6Zr+lyi5cxNsSi4ix1dicqfydIKvLfV+DF3mQf/dw== +"@abp/core@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.2.0-rc.2.tgz#0eab20842f8c0801d1dca036d3d66a67e155e019" + integrity sha512-8bC1MAV4iSFLJvRvM5mvrI9+N5FPAjaG79hr3p0xJtNixFOFpNELDf4p0zwy97zY8juL+AIEyJAkg+KIIK90DQ== dependencies: - "@abp/utils" "~9.1.0" + "@abp/utils" "~9.2.0-rc.2" -"@abp/datatables.net-bs5@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.1.0.tgz#3b2e7d68e947db3d3432ab5e8bff4da89a85d05d" - integrity sha512-zBrzOrfvTPJcc6mvHZDPVdXbWesKU0lKlrb7vuBZxWjrFNwVC0d/KNJPcYVvyQ3zf9rrQB5Xg0MDgu0k5Tt7tQ== +"@abp/datatables.net-bs5@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.2.0-rc.2.tgz#8c07544cafd2d2257f45150d3265c25eafc0ec0f" + integrity sha512-l3VOtc0fleUsx+ZUMr49+nPBWRBUVw4Z26ogT7O7uRR8doeerNU1GfA4yFJCUp02WIwt8cmZ+apJHADykn9Vng== dependencies: - "@abp/datatables.net" "~9.1.0" + "@abp/datatables.net" "~9.2.0-rc.2" datatables.net-bs5 "^2.1.8" -"@abp/datatables.net@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.1.0.tgz#12d6e1412aac4a2e0a61437dcdfbfbe1add7fed7" - integrity sha512-lYRvEZUkntaI+dS9Xkz23Z54eDeZ1R4Wzhqcg6VIsz3iO5kYAa5GiQjcOiBssNGJSLuaet6R5Pqh1ThEUjfvvA== +"@abp/datatables.net@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.2.0-rc.2.tgz#2880ccb61750faa1689aec15f9e49d92ab1e94a1" + integrity sha512-nljq0p8mxoBvMZngtViInyQJqX/MI7EyQeCIYNICF2LO42RRZfpKaRy5zX+4Uc3z/wz209CfK8QfRNH/d1HtBQ== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" datatables.net "^2.1.8" -"@abp/font-awesome@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.1.0.tgz#af21ff2d7c332deb1df626404b53a78d790ba842" - integrity sha512-HcJzDCACGejOIvhxsorCdaesyDHnupR/sl5YFCsGikSqATyOSKQy4pw8wKsb1z+FCT5JYCTR7ROJ3lB1lTEgZw== +"@abp/font-awesome@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.2.0-rc.2.tgz#51eb750f5697668db538ad68cc6b9279517da537" + integrity sha512-X9JfF4n4MDZLszwZHIHuxSdGGxImEzV91ROKqNeclErCx3bOXyq/l9C+haZl/3TLzTAOSDMLgHGrT3MUCVPF5w== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" "@fortawesome/fontawesome-free" "^6.6.0" -"@abp/jquery-form@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.1.0.tgz#bdb96436c54b2a09ac4259db97e1e577410e1fc3" - integrity sha512-xjHXWstVY6B3MPB4/8g0EJeHlVvlV010OMZKFmfCGLFi6aHAEKqqWh1rMItAiVuSmCCzL0m/208ZkpoCI++jGQ== +"@abp/jquery-form@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.2.0-rc.2.tgz#58ce31a32bdfb355e5d4e3f97cb4761f594aa8e1" + integrity sha512-xN2xjK1RWKygwaqzTaWmpMlU0kGWGa2nKaudM6g9k/B6b1Pfdr8ykI4DaSuPb4BkRmurGkgy4r9o7IUoqv0T8Q== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-form "^4.3.0" -"@abp/jquery-validation-unobtrusive@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.1.0.tgz#798551e7c14ba286a819a48804107a53e974c1ee" - integrity sha512-TggKckfbLtB7Ig2kNt2kH//GaJUns8OZbpdKdh7vCWIAGedw2FLNCABCpoGnyNp0s/dVOtdyxRl9eFRN3+a4tw== +"@abp/jquery-validation-unobtrusive@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.2.0-rc.2.tgz#4e624e22cbf149d1c78da46aabaf6fc18d3b65ff" + integrity sha512-adKJxnWnhCrIYWoVdo41zgO5p4SjyypCxoHeQ7mkohry2WN6WuQG5nmHqhpdevw966DENvmVzFfz7vnO72vtrg== dependencies: - "@abp/jquery-validation" "~9.1.0" + "@abp/jquery-validation" "~9.2.0-rc.2" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.1.0.tgz#b771652377fbfb3c92ce03b7b7c069a3bb2ae70b" - integrity sha512-bmPSOHcxjitB8NkiLv0SxxuSS3FlCq9SiTUGvsT8nOStDsm+KpyYSzZQq8LSCyqWWj54tlOARTfcD4tMK7/K3Q== +"@abp/jquery-validation@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.2.0-rc.2.tgz#b6a8124d5056ddad515733bffc78703f6bd422be" + integrity sha512-4j1fg1qImif2vFZQFJ7z1olEUnjc3BByKS+7joNQ+96/NqOQdOQjRjB2n490od7ExCinKnvYx3JFttpYjHU3Xw== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-validation "^1.21.0" -"@abp/jquery@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.1.0.tgz#952b61ea3e4a99e6551f667297dbb637d83f3c50" - integrity sha512-Ulw8ClAbqmxceNYCb76bR6dq7e9b7Hs6mjlEy05zdbsSNuZW3UoycsJ+BquXi+dUNBo6OppqEmhVeiIjAKTaFg== +"@abp/jquery@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.2.0-rc.2.tgz#335643787ab282d5d2fee4f360c2b62cce3db962" + integrity sha512-baKNDuiQMzkV0LFzBwEqS0JYtXIUe5KH+MVizGR6Zv10B8l/gIrUzWI/nnqDtbts6gmY9OBDXkCTcgMQBwKqjw== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" jquery "~3.7.1" -"@abp/lodash@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.1.0.tgz#62c784bd99c9e1ccfa82905e843375c594ef8584" - integrity sha512-cCE3ZwwWcPrh+ccKx4AS1USL1ttbrXBqci/l+ROjXhFzrs6GYnhI0+30xRZgPdPRm3U3nGPrSaDAU7UuHkRScg== +"@abp/lodash@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.2.0-rc.2.tgz#521bccafcc09780c751932688b6ec45c368c2854" + integrity sha512-T6eYsDovUl4AYD9n2N4WZq9MeC4/ryke5ctvdhzuArqM7WpPvohq3VKGugrP/MLo9oD3MZ5DVQvMJ0AVg+IXNQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" lodash "^4.17.21" -"@abp/luxon@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.1.0.tgz#b246f3627bbc7ce41b7878fce1679da473b0e0ae" - integrity sha512-AouS/qEK+i69hwr4XtUOCepONLuR+1gdHFb50RpOEbUoDfTYmYpxPBdiICmZPeJ3CExErITPZxzzngCnKPPOUw== +"@abp/luxon@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.2.0-rc.2.tgz#da366d335f9575b104ea2206e076af35cbbfcfba" + integrity sha512-mr8ipYrJDDJPoImIwORK7Z6d1C07APl95L17ufRbig4sjdxjNEDR5uUXVviVafF1No2bVkYiS0xGKQmojA2twg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" luxon "^3.5.0" -"@abp/malihu-custom-scrollbar-plugin@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.1.0.tgz#288ae1916a78100f2c728dfd05a560ee99fafae4" - integrity sha512-McdMRDtbTZBjp//3GMVjRjkc16HNYDEdPc2ar1xrY817e0tEln6/TBkItAcfOvjso0TsLcvlNL1+1bjbMjj8OA== +"@abp/malihu-custom-scrollbar-plugin@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.2.0-rc.2.tgz#cb79018adf08276041eb5c5dcf75130c8995686b" + integrity sha512-jdHXx7kPduaEfFXDvf5s+QVl10RqshNbnZkkqPx0mMkhz2H/qxwzF1e0mcLFL38nEgs+0gxIkcA8/MKq85tyqQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.1.0.tgz#f3c0ae02209e496913cda53df752dabc5be648bd" - integrity sha512-WxR1l09vwxRhqyvAVP4m4IBOFjIC4pzLOmelloqES+gL/klw+T/AmY21Z/z+OV0p7aJ3V3Q1aQStuMCDCyGFnQ== +"@abp/moment@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.2.0-rc.2.tgz#f4339138668ca72be1d700624b91789344557bff" + integrity sha512-J4ZaCDEcXLGmJ+8zqgaMK9Lutnjga28LSbuiFaGLlZ1QgYZ5HDScE128o3vX9qr60tc6IIY0cDkHi4bBHo7ozw== dependencies: moment "^2.30.1" -"@abp/prismjs@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.1.0.tgz#40b688c82ae808ef92516ea5731ccb38c19d2384" - integrity sha512-trSW1K6VtIEaAyxe7umQGimCTLrgdT6DwSiQ8PONvKa8WR5ZmzSDU7lK8SliorufkbWtdkjCvuXB81NGt3tAhQ== +"@abp/prismjs@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.2.0-rc.2.tgz#1ee12acdd9e1dcc9ae2ce4a9f21f159c3f9e3dec" + integrity sha512-GuKz6bujBlYHAgLhW1RT7aEOwKyZ+vnyMIhN0/fQ34+nbqg0PC4htwJcN6spgN8gZKD06lCqBH+GjLE2WtE/Rw== dependencies: - "@abp/clipboard" "~9.1.0" - "@abp/core" "~9.1.0" + "@abp/clipboard" "~9.2.0-rc.2" + "@abp/core" "~9.2.0-rc.2" prismjs "^1.29.0" -"@abp/select2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.1.0.tgz#9c2c6751f8506fb375c9b7c42a008c168a8f4c75" - integrity sha512-n6HqsH8aFLMrqL6LJ0gsHFlaq6T1QSt/x7HkkZyhDC7CwhXajxtsJEstDnDF9naHSXX/lerPbrHEgzn2QRMBqg== +"@abp/select2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.2.0-rc.2.tgz#ba6e839644b7ecdd198fe5286101302a3dc79ce3" + integrity sha512-AQMXb5ATgbN7grpKU8lB2xFFfirCe45lIvd9krW5SwUHCYZP7pRPbwQXAew+BqlIIYJJF2GA7/Nf//e/71bKtg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" select2 "^4.0.13" -"@abp/sweetalert2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.1.0.tgz#660f9b1db60cc04bde6cadbcf3c55320d20c2f3e" - integrity sha512-z3JMeAwva2vR8qZSRJ/BisQDdAEqjDJ/6oIGu/JYXQEchO1Lnd9UMV376nc3crLJdnyRRO7Qs6zx4FfVOCH0kw== +"@abp/sweetalert2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.2.0-rc.2.tgz#075634dc3ec996607757694d636d276ba2350e06" + integrity sha512-ZgHgqM8Q+V9ECQyAyepM5KpshKpQP463xRHwgY45oR7/CfCcO/vS56YQgJNg2zE6+lRQZaRQ9OL7lph8975vVg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" sweetalert2 "^11.14.1" -"@abp/timeago@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.1.0.tgz#496e40153241bae5d418d79cc6c65c9db6306447" - integrity sha512-a/UAYQ2GrhYFGx4p0hqr3x+teBqnjnOiWJEPq9//RTlz+EZ+OUyHbJrcUtqZRvaGJtOAsfmljD3jYnOXUlU7DA== +"@abp/timeago@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.2.0-rc.2.tgz#25324732966438bd6f505bdd932511ba2cc8c22b" + integrity sha512-WfWxxiycg77vVN2hPHqUOu552la+zxr0EZnHMqnEnnBe+gFYe9C0CpdYuyBq2eQXRXHoVzsB+4YRVNk5sbSGng== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" timeago "^1.6.7" -"@abp/toastr@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.1.0.tgz#501cbe211164b2e7416a2d4878cb34aed3550b7c" - integrity sha512-BkTDuU0p9Qa4+HT9CYwiPQjVTrbTSNBgQ18h69S3lIadLJwdC6pXeLbL9bhe+5fiuhq2f+0Oqo3X75LNEgzHkw== - dependencies: - "@abp/jquery" "~9.1.0" - toastr "^2.1.4" - -"@abp/utils@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.1.0.tgz#a4fd37d943df18b5c11f5d4869efd5d8882b7959" - integrity sha512-+UZo0ocTEZlRO41SzzHNdinjdFUDVq8wYZSj5BA1bqCVeWYhfjI2+rl9DuogCN8UEuCACA4K/qb4XcE/untQ3g== +"@abp/utils@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.2.0-rc.2.tgz#669885ad1e3ac7b8e182d7c392ea1b633df65aac" + integrity sha512-rfQxhJy04Qf50P3ciasKbDlJ+9JidD60xW77TjcoUJvljJE+A8iwTX8Wwqz7+YcC7rks2ILM4y3Ay7/47NOICQ== dependencies: just-compare "^2.3.0" @@ -301,7 +292,7 @@ jquery-validation@>=1.19, jquery-validation@^1.21.0: resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.21.0.tgz#78fc05ab76020912a246af3661b3f54a438bca93" integrity sha512-xNot0rlUIgu7duMcQ5qb6MGkGL/Z1PQaRJQoZAURW9+a/2PGOUxY36o/WyNeP2T9R6jvWB8Z9lUVvvQWI/Zs5w== -jquery@>=1.10, jquery@>=1.12.0, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2: +jquery@>=1.10, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2: version "3.6.4" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.4.tgz#ba065c188142100be4833699852bf7c24dc0252f" integrity sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ== @@ -374,10 +365,3 @@ tiny-emitter@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== - -toastr@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/toastr/-/toastr-2.1.4.tgz#8b43be64fb9d0c414871446f2db8e8ca4e95f181" - integrity sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA== - dependencies: - jquery ">=1.12.0" diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/Volo.Abp.BlobStoring.Database.MongoDB.Tests.csproj b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/Volo.Abp.BlobStoring.Database.MongoDB.Tests.csproj index 61c0d81d02..f5c1f561af 100644 --- a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/Volo.Abp.BlobStoring.Database.MongoDB.Tests.csproj +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/Volo.Abp.BlobStoring.Database.MongoDB.Tests.csproj @@ -8,9 +8,9 @@ - - - + + + diff --git a/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.abppkg.analyze.json b/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.abppkg.analyze.json index 4e811c43b5..2a6e3fda82 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.abppkg.analyze.json +++ b/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.abppkg.analyze.json @@ -36,9 +36,9 @@ "name": "BloggingAdminApplicationModule" }, { - "declaringAssemblyName": "Volo.BloggingTestApp.EntityFrameworkCore", - "namespace": "Volo.BloggingTestApp.EntityFrameworkCore", - "name": "BloggingTestAppEntityFrameworkCoreModule" + "declaringAssemblyName": "Volo.BloggingTestApp.MongoDB", + "namespace": "Volo.BloggingTestApp.MongoDB", + "name": "BloggingTestAppMongoDbModule" }, { "declaringAssemblyName": "Volo.Abp.Account.Web", diff --git a/modules/blogging/app/Volo.BloggingTestApp/package.json b/modules/blogging/app/Volo.BloggingTestApp/package.json index 2c0e71ec56..a391f0da97 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/package.json +++ b/modules/blogging/app/Volo.BloggingTestApp/package.json @@ -3,7 +3,7 @@ "name": "volo.blogtestapp", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0", - "@abp/blogging": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2", + "@abp/blogging": "~9.2.0-rc.2" } } diff --git a/modules/blogging/app/Volo.BloggingTestApp/yarn.lock b/modules/blogging/app/Volo.BloggingTestApp/yarn.lock index 783bf4eae3..a2099198fc 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/yarn.lock +++ b/modules/blogging/app/Volo.BloggingTestApp/yarn.lock @@ -2,237 +2,228 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.1.0.tgz#e9e0321037c34b43145dfc7827c422fbf79f59ab" - integrity sha512-vLkyrdFXw0/MPyU2hzqGJJk7trmIlVMdflNIe/rF77UIRmJYHWaTmPt9i5PLN/O4s0jnE9kWlaaJ/VnnFWeGsQ== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~9.1.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.1.0.tgz#35a73580b9b67a33708f2cf897243b50c9ad7100" - integrity sha512-rggTYL9poOFICsOseDX22JNhD0KWH8Oxs0YH91TZjvm/F67cPemW99eBlJc/YdJ7bN6icRiebN8m81y9MSanyg== - dependencies: - "@abp/aspnetcore.mvc.ui" "~9.1.0" - "@abp/bootstrap" "~9.1.0" - "@abp/bootstrap-datepicker" "~9.1.0" - "@abp/bootstrap-daterangepicker" "~9.1.0" - "@abp/datatables.net-bs5" "~9.1.0" - "@abp/font-awesome" "~9.1.0" - "@abp/jquery-form" "~9.1.0" - "@abp/jquery-validation-unobtrusive" "~9.1.0" - "@abp/lodash" "~9.1.0" - "@abp/luxon" "~9.1.0" - "@abp/malihu-custom-scrollbar-plugin" "~9.1.0" - "@abp/moment" "~9.1.0" - "@abp/select2" "~9.1.0" - "@abp/sweetalert2" "~9.1.0" - "@abp/timeago" "~9.1.0" - "@abp/toastr" "~9.1.0" - -"@abp/aspnetcore.mvc.ui@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.1.0.tgz#38d058ab148a36ad4d63870e8a501ca339e4a70e" - integrity sha512-lUw1ij87KJp+Rj4cz8oSQkdRuoMq76l/wIAGwtXH+EOKusVpSJTOTioAV/tPKeKZ15ubE4bO5jWcQGxZkXgH3w== +"@abp/aspnetcore.mvc.ui.theme.basic@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.2.0-rc.2.tgz#e32c8bd046d7377ae3f6a703eac9069e10bcd600" + integrity sha512-oj4lGrzllgGrq7rI7gz1Xvq5TU+xT+isGVf4T8hF3INE/J2nIyK45HnkjDq5wpdM3P+HkdM1+RtWNrLSVu0Klg== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.2.0-rc.2.tgz#77562013b604d0193e361efa6801235179dc7b4f" + integrity sha512-5KcMGg6o6W5uwsujrjUARlZFgWmZeYBNCf1vkf3btakkwTzmPDbypUx6VrgBJbt4AUnYlMQbSHvbfK0d3I1e6w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~9.2.0-rc.2" + "@abp/bootstrap" "~9.2.0-rc.2" + "@abp/bootstrap-datepicker" "~9.2.0-rc.2" + "@abp/bootstrap-daterangepicker" "~9.2.0-rc.2" + "@abp/datatables.net-bs5" "~9.2.0-rc.2" + "@abp/font-awesome" "~9.2.0-rc.2" + "@abp/jquery-form" "~9.2.0-rc.2" + "@abp/jquery-validation-unobtrusive" "~9.2.0-rc.2" + "@abp/lodash" "~9.2.0-rc.2" + "@abp/luxon" "~9.2.0-rc.2" + "@abp/malihu-custom-scrollbar-plugin" "~9.2.0-rc.2" + "@abp/moment" "~9.2.0-rc.2" + "@abp/select2" "~9.2.0-rc.2" + "@abp/sweetalert2" "~9.2.0-rc.2" + "@abp/timeago" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.2.0-rc.2.tgz#e1c0e4b0280d5fe37659da8dc696150f0d461336" + integrity sha512-WG4WWGS1mSb2QWAVOA2nrvoi9/3oueEaKEG18xbPgWiLz5GCq8gsopKqUgW/fmBaw+1Jh9tsuCTH7eED/2tUxA== dependencies: ansi-colors "^4.1.3" -"@abp/blogging@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/blogging/-/blogging-9.1.0.tgz#17f9ad066361e385136fb0a59315952e263ef68e" - integrity sha512-KQVZMd0bBPn4HMYD4ZyIKMQpoUQGMCGVhfaOfSsnJMd9iHYXKcBEPLqAcO97ZtaSrnhTpgeb14Ai/9lmvVN5nw== +"@abp/blogging@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/blogging/-/blogging-9.2.0-rc.2.tgz#8ac5e0727991f71d062aa8e34b2b46f5d3135a44" + integrity sha512-AFXjnGKONqdyykqtVMMNpjlphtivzSkOZTxQURIRV1aS5n+LaYDJDwGzLC8QyHtyafGqhuwYc//wxitz8gLR9Q== dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~9.1.0" - "@abp/owl.carousel" "~9.1.0" - "@abp/prismjs" "~9.1.0" - "@abp/tui-editor" "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.shared" "~9.2.0-rc.2" + "@abp/owl.carousel" "~9.2.0-rc.2" + "@abp/prismjs" "~9.2.0-rc.2" + "@abp/tui-editor" "~9.2.0-rc.2" -"@abp/bootstrap-datepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.1.0.tgz#96a1875a2b92f03b8f90f7842ebf8849cf8a4d04" - integrity sha512-3FQm1SK8qmzoP7LOvXkZpq1AzrdPHKRdQJjoqK9N+lQxQjNfOxEFMxeDSg6WJKO619Cnve++jAyMidojXlCiwQ== +"@abp/bootstrap-datepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.2.0-rc.2.tgz#429c2d44a22f9190013aea0efa420854b7e83a4f" + integrity sha512-bxMpaxTgnF5/VELbKvtZVw0tI0bowzUQFVSBQJqO4o7wJmxyHNzJuaom8Is+tCsoMrpo8t9EGMGfpWHV1IsrjQ== dependencies: bootstrap-datepicker "^1.10.0" -"@abp/bootstrap-daterangepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.1.0.tgz#1993c539837eb31de174cba9f0bccd441a98f209" - integrity sha512-jS1jTqP6KCXQYX9xZpDAI07C94nwzXrNJD3H0pJXsXoOva3TTBQDD8khf6RDb7tiZrkrNJqmMyUiCfVTDbO7zg== +"@abp/bootstrap-daterangepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.2.0-rc.2.tgz#85bb869d5d43eb6c97f50943ea2c390cb4d37d54" + integrity sha512-rLLsY6GCY16IOwRkmx7vZ25sVHOM9dPdTlcZiuUuEi5+QrmgGiqZmIwqYiu4WFSPPTJtufsyMaHzXxqiZRpXVg== dependencies: bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.1.0.tgz#a06c1cd4c11554c29973db70c6a383efebb10b4e" - integrity sha512-r6onkBnpqoWrdYLDVZTiqgQBvH5UZerYRrPB3Ky+RkXJhgwn7AM3LAVrB42sIvFZRNbwOlvuAL+a0+5yDOH67Q== +"@abp/bootstrap@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.2.0-rc.2.tgz#ba2ab98d930501e7f004f99e7d7e7d4e6d3c0998" + integrity sha512-u2mtHHbmXNgngExl4hM5uo/E3k453n0im2Ci02+4dnhZoFicLZlBdtJusZAw5QR1XTLPybW6RXofWBvkZcgiwg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" bootstrap "^5.3.3" -"@abp/clipboard@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.1.0.tgz#5d5678ef7e8c355715c5a74e4aabd0a21d5e6cbe" - integrity sha512-1TxZbB/q5DPeLhYzrc6y0OVekxnjM+CRH/Gu8nAXQMX46XjdTboXvBtvgKHV1aCznGNsWyjd5A2O5G/XGg9NNw== +"@abp/clipboard@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.2.0-rc.2.tgz#608bdb3d4a3ff1a7b38ad5850385746f7cfe6c0c" + integrity sha512-w/JiTmidNc7SZuvpkQumyNj6FTBq6hLnhXeBBh4/xega1Ob2q+tbrOSpemgpK6YOXUQbmH8a4vEPvwt7uzNfvQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" clipboard "^2.0.11" -"@abp/core@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.1.0.tgz#af78fc096932d4ec45216b5e910c87a72e479b1f" - integrity sha512-6P0KGh5+IqlhQnUOyzaGaKrKzOAm1G0gg8Ay3DXyW2ksY6Zr+lyi5cxNsSi4ix1dicqfydIKvLfV+DF3mQf/dw== +"@abp/core@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.2.0-rc.2.tgz#0eab20842f8c0801d1dca036d3d66a67e155e019" + integrity sha512-8bC1MAV4iSFLJvRvM5mvrI9+N5FPAjaG79hr3p0xJtNixFOFpNELDf4p0zwy97zY8juL+AIEyJAkg+KIIK90DQ== dependencies: - "@abp/utils" "~9.1.0" + "@abp/utils" "~9.2.0-rc.2" -"@abp/datatables.net-bs5@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.1.0.tgz#3b2e7d68e947db3d3432ab5e8bff4da89a85d05d" - integrity sha512-zBrzOrfvTPJcc6mvHZDPVdXbWesKU0lKlrb7vuBZxWjrFNwVC0d/KNJPcYVvyQ3zf9rrQB5Xg0MDgu0k5Tt7tQ== +"@abp/datatables.net-bs5@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.2.0-rc.2.tgz#8c07544cafd2d2257f45150d3265c25eafc0ec0f" + integrity sha512-l3VOtc0fleUsx+ZUMr49+nPBWRBUVw4Z26ogT7O7uRR8doeerNU1GfA4yFJCUp02WIwt8cmZ+apJHADykn9Vng== dependencies: - "@abp/datatables.net" "~9.1.0" + "@abp/datatables.net" "~9.2.0-rc.2" datatables.net-bs5 "^2.1.8" -"@abp/datatables.net@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.1.0.tgz#12d6e1412aac4a2e0a61437dcdfbfbe1add7fed7" - integrity sha512-lYRvEZUkntaI+dS9Xkz23Z54eDeZ1R4Wzhqcg6VIsz3iO5kYAa5GiQjcOiBssNGJSLuaet6R5Pqh1ThEUjfvvA== +"@abp/datatables.net@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.2.0-rc.2.tgz#2880ccb61750faa1689aec15f9e49d92ab1e94a1" + integrity sha512-nljq0p8mxoBvMZngtViInyQJqX/MI7EyQeCIYNICF2LO42RRZfpKaRy5zX+4Uc3z/wz209CfK8QfRNH/d1HtBQ== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" datatables.net "^2.1.8" -"@abp/font-awesome@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.1.0.tgz#af21ff2d7c332deb1df626404b53a78d790ba842" - integrity sha512-HcJzDCACGejOIvhxsorCdaesyDHnupR/sl5YFCsGikSqATyOSKQy4pw8wKsb1z+FCT5JYCTR7ROJ3lB1lTEgZw== +"@abp/font-awesome@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.2.0-rc.2.tgz#51eb750f5697668db538ad68cc6b9279517da537" + integrity sha512-X9JfF4n4MDZLszwZHIHuxSdGGxImEzV91ROKqNeclErCx3bOXyq/l9C+haZl/3TLzTAOSDMLgHGrT3MUCVPF5w== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" "@fortawesome/fontawesome-free" "^6.6.0" -"@abp/jquery-form@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.1.0.tgz#bdb96436c54b2a09ac4259db97e1e577410e1fc3" - integrity sha512-xjHXWstVY6B3MPB4/8g0EJeHlVvlV010OMZKFmfCGLFi6aHAEKqqWh1rMItAiVuSmCCzL0m/208ZkpoCI++jGQ== +"@abp/jquery-form@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.2.0-rc.2.tgz#58ce31a32bdfb355e5d4e3f97cb4761f594aa8e1" + integrity sha512-xN2xjK1RWKygwaqzTaWmpMlU0kGWGa2nKaudM6g9k/B6b1Pfdr8ykI4DaSuPb4BkRmurGkgy4r9o7IUoqv0T8Q== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-form "^4.3.0" -"@abp/jquery-validation-unobtrusive@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.1.0.tgz#798551e7c14ba286a819a48804107a53e974c1ee" - integrity sha512-TggKckfbLtB7Ig2kNt2kH//GaJUns8OZbpdKdh7vCWIAGedw2FLNCABCpoGnyNp0s/dVOtdyxRl9eFRN3+a4tw== +"@abp/jquery-validation-unobtrusive@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.2.0-rc.2.tgz#4e624e22cbf149d1c78da46aabaf6fc18d3b65ff" + integrity sha512-adKJxnWnhCrIYWoVdo41zgO5p4SjyypCxoHeQ7mkohry2WN6WuQG5nmHqhpdevw966DENvmVzFfz7vnO72vtrg== dependencies: - "@abp/jquery-validation" "~9.1.0" + "@abp/jquery-validation" "~9.2.0-rc.2" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.1.0.tgz#b771652377fbfb3c92ce03b7b7c069a3bb2ae70b" - integrity sha512-bmPSOHcxjitB8NkiLv0SxxuSS3FlCq9SiTUGvsT8nOStDsm+KpyYSzZQq8LSCyqWWj54tlOARTfcD4tMK7/K3Q== +"@abp/jquery-validation@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.2.0-rc.2.tgz#b6a8124d5056ddad515733bffc78703f6bd422be" + integrity sha512-4j1fg1qImif2vFZQFJ7z1olEUnjc3BByKS+7joNQ+96/NqOQdOQjRjB2n490od7ExCinKnvYx3JFttpYjHU3Xw== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-validation "^1.21.0" -"@abp/jquery@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.1.0.tgz#952b61ea3e4a99e6551f667297dbb637d83f3c50" - integrity sha512-Ulw8ClAbqmxceNYCb76bR6dq7e9b7Hs6mjlEy05zdbsSNuZW3UoycsJ+BquXi+dUNBo6OppqEmhVeiIjAKTaFg== +"@abp/jquery@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.2.0-rc.2.tgz#335643787ab282d5d2fee4f360c2b62cce3db962" + integrity sha512-baKNDuiQMzkV0LFzBwEqS0JYtXIUe5KH+MVizGR6Zv10B8l/gIrUzWI/nnqDtbts6gmY9OBDXkCTcgMQBwKqjw== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" jquery "~3.7.1" -"@abp/lodash@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.1.0.tgz#62c784bd99c9e1ccfa82905e843375c594ef8584" - integrity sha512-cCE3ZwwWcPrh+ccKx4AS1USL1ttbrXBqci/l+ROjXhFzrs6GYnhI0+30xRZgPdPRm3U3nGPrSaDAU7UuHkRScg== +"@abp/lodash@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.2.0-rc.2.tgz#521bccafcc09780c751932688b6ec45c368c2854" + integrity sha512-T6eYsDovUl4AYD9n2N4WZq9MeC4/ryke5ctvdhzuArqM7WpPvohq3VKGugrP/MLo9oD3MZ5DVQvMJ0AVg+IXNQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" lodash "^4.17.21" -"@abp/luxon@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.1.0.tgz#b246f3627bbc7ce41b7878fce1679da473b0e0ae" - integrity sha512-AouS/qEK+i69hwr4XtUOCepONLuR+1gdHFb50RpOEbUoDfTYmYpxPBdiICmZPeJ3CExErITPZxzzngCnKPPOUw== +"@abp/luxon@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.2.0-rc.2.tgz#da366d335f9575b104ea2206e076af35cbbfcfba" + integrity sha512-mr8ipYrJDDJPoImIwORK7Z6d1C07APl95L17ufRbig4sjdxjNEDR5uUXVviVafF1No2bVkYiS0xGKQmojA2twg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" luxon "^3.5.0" -"@abp/malihu-custom-scrollbar-plugin@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.1.0.tgz#288ae1916a78100f2c728dfd05a560ee99fafae4" - integrity sha512-McdMRDtbTZBjp//3GMVjRjkc16HNYDEdPc2ar1xrY817e0tEln6/TBkItAcfOvjso0TsLcvlNL1+1bjbMjj8OA== +"@abp/malihu-custom-scrollbar-plugin@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.2.0-rc.2.tgz#cb79018adf08276041eb5c5dcf75130c8995686b" + integrity sha512-jdHXx7kPduaEfFXDvf5s+QVl10RqshNbnZkkqPx0mMkhz2H/qxwzF1e0mcLFL38nEgs+0gxIkcA8/MKq85tyqQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.1.0.tgz#f3c0ae02209e496913cda53df752dabc5be648bd" - integrity sha512-WxR1l09vwxRhqyvAVP4m4IBOFjIC4pzLOmelloqES+gL/klw+T/AmY21Z/z+OV0p7aJ3V3Q1aQStuMCDCyGFnQ== +"@abp/moment@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.2.0-rc.2.tgz#f4339138668ca72be1d700624b91789344557bff" + integrity sha512-J4ZaCDEcXLGmJ+8zqgaMK9Lutnjga28LSbuiFaGLlZ1QgYZ5HDScE128o3vX9qr60tc6IIY0cDkHi4bBHo7ozw== dependencies: moment "^2.30.1" -"@abp/owl.carousel@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/owl.carousel/-/owl.carousel-9.1.0.tgz#fbb77abb9645239329d4aab50d7582095a5338e3" - integrity sha512-taX/vBaaJx4gxjbFCyYtscgd24ATqOhLAK/RmMM9wb2aBUFeJWsJJaJ2Ruc/s7UvTYn+fHSZP2+EHfZLUZdTfA== +"@abp/owl.carousel@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/owl.carousel/-/owl.carousel-9.2.0-rc.2.tgz#b00cfb3784c2d04fea06ff9ad21daabbb3411e11" + integrity sha512-gj9YNlvqoho1TenZTmd1eDOLfSaQFzsSk+vKvqIn9Y5fbL71WoY1yc18NXZYTBes0JDBYi8xqYsT9dh9KhFY6Q== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" owl.carousel "^2.3.4" -"@abp/prismjs@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.1.0.tgz#40b688c82ae808ef92516ea5731ccb38c19d2384" - integrity sha512-trSW1K6VtIEaAyxe7umQGimCTLrgdT6DwSiQ8PONvKa8WR5ZmzSDU7lK8SliorufkbWtdkjCvuXB81NGt3tAhQ== +"@abp/prismjs@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.2.0-rc.2.tgz#1ee12acdd9e1dcc9ae2ce4a9f21f159c3f9e3dec" + integrity sha512-GuKz6bujBlYHAgLhW1RT7aEOwKyZ+vnyMIhN0/fQ34+nbqg0PC4htwJcN6spgN8gZKD06lCqBH+GjLE2WtE/Rw== dependencies: - "@abp/clipboard" "~9.1.0" - "@abp/core" "~9.1.0" + "@abp/clipboard" "~9.2.0-rc.2" + "@abp/core" "~9.2.0-rc.2" prismjs "^1.29.0" -"@abp/select2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.1.0.tgz#9c2c6751f8506fb375c9b7c42a008c168a8f4c75" - integrity sha512-n6HqsH8aFLMrqL6LJ0gsHFlaq6T1QSt/x7HkkZyhDC7CwhXajxtsJEstDnDF9naHSXX/lerPbrHEgzn2QRMBqg== +"@abp/select2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.2.0-rc.2.tgz#ba6e839644b7ecdd198fe5286101302a3dc79ce3" + integrity sha512-AQMXb5ATgbN7grpKU8lB2xFFfirCe45lIvd9krW5SwUHCYZP7pRPbwQXAew+BqlIIYJJF2GA7/Nf//e/71bKtg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" select2 "^4.0.13" -"@abp/sweetalert2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.1.0.tgz#660f9b1db60cc04bde6cadbcf3c55320d20c2f3e" - integrity sha512-z3JMeAwva2vR8qZSRJ/BisQDdAEqjDJ/6oIGu/JYXQEchO1Lnd9UMV376nc3crLJdnyRRO7Qs6zx4FfVOCH0kw== +"@abp/sweetalert2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.2.0-rc.2.tgz#075634dc3ec996607757694d636d276ba2350e06" + integrity sha512-ZgHgqM8Q+V9ECQyAyepM5KpshKpQP463xRHwgY45oR7/CfCcO/vS56YQgJNg2zE6+lRQZaRQ9OL7lph8975vVg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" sweetalert2 "^11.14.1" -"@abp/timeago@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.1.0.tgz#496e40153241bae5d418d79cc6c65c9db6306447" - integrity sha512-a/UAYQ2GrhYFGx4p0hqr3x+teBqnjnOiWJEPq9//RTlz+EZ+OUyHbJrcUtqZRvaGJtOAsfmljD3jYnOXUlU7DA== +"@abp/timeago@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.2.0-rc.2.tgz#25324732966438bd6f505bdd932511ba2cc8c22b" + integrity sha512-WfWxxiycg77vVN2hPHqUOu552la+zxr0EZnHMqnEnnBe+gFYe9C0CpdYuyBq2eQXRXHoVzsB+4YRVNk5sbSGng== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" timeago "^1.6.7" -"@abp/toastr@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.1.0.tgz#501cbe211164b2e7416a2d4878cb34aed3550b7c" - integrity sha512-BkTDuU0p9Qa4+HT9CYwiPQjVTrbTSNBgQ18h69S3lIadLJwdC6pXeLbL9bhe+5fiuhq2f+0Oqo3X75LNEgzHkw== +"@abp/tui-editor@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/tui-editor/-/tui-editor-9.2.0-rc.2.tgz#8f755bfb7413d1636fe904d409efe6363f48516b" + integrity sha512-Ap2JOZShRvbHI4+5f1pGRgP26i6bel+2eBT1w33UWMclyNGhNYVcm9VNIyLaS9KbGM5bphij2bMdnTUpjQ10Dw== dependencies: - "@abp/jquery" "~9.1.0" - toastr "^2.1.4" + "@abp/jquery" "~9.2.0-rc.2" + "@abp/prismjs" "~9.2.0-rc.2" -"@abp/tui-editor@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/tui-editor/-/tui-editor-9.1.0.tgz#cc4106f0ea61ad1ee2497ecfec1eefb62189f57f" - integrity sha512-Ttmzn881+knyFst/5BZP+4vb4R+gx5loqWtbuQXthO2saisr/+QJX4EzqwYqPTVl81Y1HZwhcdTr/hMHOCMFpg== - dependencies: - "@abp/jquery" "~9.1.0" - "@abp/prismjs" "~9.1.0" - -"@abp/utils@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.1.0.tgz#a4fd37d943df18b5c11f5d4869efd5d8882b7959" - integrity sha512-+UZo0ocTEZlRO41SzzHNdinjdFUDVq8wYZSj5BA1bqCVeWYhfjI2+rl9DuogCN8UEuCACA4K/qb4XcE/untQ3g== +"@abp/utils@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.2.0-rc.2.tgz#669885ad1e3ac7b8e182d7c392ea1b633df65aac" + integrity sha512-rfQxhJy04Qf50P3ciasKbDlJ+9JidD60xW77TjcoUJvljJE+A8iwTX8Wwqz7+YcC7rks2ILM4y3Ay7/47NOICQ== dependencies: just-compare "^2.3.0" @@ -327,7 +318,7 @@ jquery-validation@>=1.19, jquery-validation@^1.21.0: resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.21.0.tgz#78fc05ab76020912a246af3661b3f54a438bca93" integrity sha512-xNot0rlUIgu7duMcQ5qb6MGkGL/Z1PQaRJQoZAURW9+a/2PGOUxY36o/WyNeP2T9R6jvWB8Z9lUVvvQWI/Zs5w== -jquery@>=1.10, jquery@>=1.12.0, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2, jquery@>=1.8.3: +jquery@>=1.10, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2, jquery@>=1.8.3: version "3.6.4" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.4.tgz#ba065c188142100be4833699852bf7c24dc0252f" integrity sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ== @@ -407,10 +398,3 @@ tiny-emitter@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== - -toastr@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/toastr/-/toastr-2.1.4.tgz#8b43be64fb9d0c414871446f2db8e8ca4e95f181" - integrity sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA== - dependencies: - jquery ">=1.12.0" diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj index 7dcac50e69..c71d73c65d 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj @@ -8,9 +8,9 @@ - - - + + + diff --git a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package.json b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package.json index b762a1f3f7..5de62f5e2c 100644 --- a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package.json +++ b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package.json @@ -3,6 +3,6 @@ "name": "client-simulation-web", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2" } } diff --git a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/yarn.lock b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/yarn.lock index 38a43d8ed5..7a9396e283 100644 --- a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/yarn.lock +++ b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/yarn.lock @@ -2,194 +2,185 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.1.0.tgz#e9e0321037c34b43145dfc7827c422fbf79f59ab" - integrity sha512-vLkyrdFXw0/MPyU2hzqGJJk7trmIlVMdflNIe/rF77UIRmJYHWaTmPt9i5PLN/O4s0jnE9kWlaaJ/VnnFWeGsQ== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~9.1.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.1.0.tgz#35a73580b9b67a33708f2cf897243b50c9ad7100" - integrity sha512-rggTYL9poOFICsOseDX22JNhD0KWH8Oxs0YH91TZjvm/F67cPemW99eBlJc/YdJ7bN6icRiebN8m81y9MSanyg== - dependencies: - "@abp/aspnetcore.mvc.ui" "~9.1.0" - "@abp/bootstrap" "~9.1.0" - "@abp/bootstrap-datepicker" "~9.1.0" - "@abp/bootstrap-daterangepicker" "~9.1.0" - "@abp/datatables.net-bs5" "~9.1.0" - "@abp/font-awesome" "~9.1.0" - "@abp/jquery-form" "~9.1.0" - "@abp/jquery-validation-unobtrusive" "~9.1.0" - "@abp/lodash" "~9.1.0" - "@abp/luxon" "~9.1.0" - "@abp/malihu-custom-scrollbar-plugin" "~9.1.0" - "@abp/moment" "~9.1.0" - "@abp/select2" "~9.1.0" - "@abp/sweetalert2" "~9.1.0" - "@abp/timeago" "~9.1.0" - "@abp/toastr" "~9.1.0" - -"@abp/aspnetcore.mvc.ui@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.1.0.tgz#38d058ab148a36ad4d63870e8a501ca339e4a70e" - integrity sha512-lUw1ij87KJp+Rj4cz8oSQkdRuoMq76l/wIAGwtXH+EOKusVpSJTOTioAV/tPKeKZ15ubE4bO5jWcQGxZkXgH3w== +"@abp/aspnetcore.mvc.ui.theme.basic@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.2.0-rc.2.tgz#e32c8bd046d7377ae3f6a703eac9069e10bcd600" + integrity sha512-oj4lGrzllgGrq7rI7gz1Xvq5TU+xT+isGVf4T8hF3INE/J2nIyK45HnkjDq5wpdM3P+HkdM1+RtWNrLSVu0Klg== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.2.0-rc.2.tgz#77562013b604d0193e361efa6801235179dc7b4f" + integrity sha512-5KcMGg6o6W5uwsujrjUARlZFgWmZeYBNCf1vkf3btakkwTzmPDbypUx6VrgBJbt4AUnYlMQbSHvbfK0d3I1e6w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~9.2.0-rc.2" + "@abp/bootstrap" "~9.2.0-rc.2" + "@abp/bootstrap-datepicker" "~9.2.0-rc.2" + "@abp/bootstrap-daterangepicker" "~9.2.0-rc.2" + "@abp/datatables.net-bs5" "~9.2.0-rc.2" + "@abp/font-awesome" "~9.2.0-rc.2" + "@abp/jquery-form" "~9.2.0-rc.2" + "@abp/jquery-validation-unobtrusive" "~9.2.0-rc.2" + "@abp/lodash" "~9.2.0-rc.2" + "@abp/luxon" "~9.2.0-rc.2" + "@abp/malihu-custom-scrollbar-plugin" "~9.2.0-rc.2" + "@abp/moment" "~9.2.0-rc.2" + "@abp/select2" "~9.2.0-rc.2" + "@abp/sweetalert2" "~9.2.0-rc.2" + "@abp/timeago" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.2.0-rc.2.tgz#e1c0e4b0280d5fe37659da8dc696150f0d461336" + integrity sha512-WG4WWGS1mSb2QWAVOA2nrvoi9/3oueEaKEG18xbPgWiLz5GCq8gsopKqUgW/fmBaw+1Jh9tsuCTH7eED/2tUxA== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.1.0.tgz#96a1875a2b92f03b8f90f7842ebf8849cf8a4d04" - integrity sha512-3FQm1SK8qmzoP7LOvXkZpq1AzrdPHKRdQJjoqK9N+lQxQjNfOxEFMxeDSg6WJKO619Cnve++jAyMidojXlCiwQ== +"@abp/bootstrap-datepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.2.0-rc.2.tgz#429c2d44a22f9190013aea0efa420854b7e83a4f" + integrity sha512-bxMpaxTgnF5/VELbKvtZVw0tI0bowzUQFVSBQJqO4o7wJmxyHNzJuaom8Is+tCsoMrpo8t9EGMGfpWHV1IsrjQ== dependencies: bootstrap-datepicker "^1.10.0" -"@abp/bootstrap-daterangepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.1.0.tgz#1993c539837eb31de174cba9f0bccd441a98f209" - integrity sha512-jS1jTqP6KCXQYX9xZpDAI07C94nwzXrNJD3H0pJXsXoOva3TTBQDD8khf6RDb7tiZrkrNJqmMyUiCfVTDbO7zg== +"@abp/bootstrap-daterangepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.2.0-rc.2.tgz#85bb869d5d43eb6c97f50943ea2c390cb4d37d54" + integrity sha512-rLLsY6GCY16IOwRkmx7vZ25sVHOM9dPdTlcZiuUuEi5+QrmgGiqZmIwqYiu4WFSPPTJtufsyMaHzXxqiZRpXVg== dependencies: bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.1.0.tgz#a06c1cd4c11554c29973db70c6a383efebb10b4e" - integrity sha512-r6onkBnpqoWrdYLDVZTiqgQBvH5UZerYRrPB3Ky+RkXJhgwn7AM3LAVrB42sIvFZRNbwOlvuAL+a0+5yDOH67Q== +"@abp/bootstrap@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.2.0-rc.2.tgz#ba2ab98d930501e7f004f99e7d7e7d4e6d3c0998" + integrity sha512-u2mtHHbmXNgngExl4hM5uo/E3k453n0im2Ci02+4dnhZoFicLZlBdtJusZAw5QR1XTLPybW6RXofWBvkZcgiwg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" bootstrap "^5.3.3" -"@abp/core@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.1.0.tgz#af78fc096932d4ec45216b5e910c87a72e479b1f" - integrity sha512-6P0KGh5+IqlhQnUOyzaGaKrKzOAm1G0gg8Ay3DXyW2ksY6Zr+lyi5cxNsSi4ix1dicqfydIKvLfV+DF3mQf/dw== +"@abp/core@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.2.0-rc.2.tgz#0eab20842f8c0801d1dca036d3d66a67e155e019" + integrity sha512-8bC1MAV4iSFLJvRvM5mvrI9+N5FPAjaG79hr3p0xJtNixFOFpNELDf4p0zwy97zY8juL+AIEyJAkg+KIIK90DQ== dependencies: - "@abp/utils" "~9.1.0" + "@abp/utils" "~9.2.0-rc.2" -"@abp/datatables.net-bs5@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.1.0.tgz#3b2e7d68e947db3d3432ab5e8bff4da89a85d05d" - integrity sha512-zBrzOrfvTPJcc6mvHZDPVdXbWesKU0lKlrb7vuBZxWjrFNwVC0d/KNJPcYVvyQ3zf9rrQB5Xg0MDgu0k5Tt7tQ== +"@abp/datatables.net-bs5@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.2.0-rc.2.tgz#8c07544cafd2d2257f45150d3265c25eafc0ec0f" + integrity sha512-l3VOtc0fleUsx+ZUMr49+nPBWRBUVw4Z26ogT7O7uRR8doeerNU1GfA4yFJCUp02WIwt8cmZ+apJHADykn9Vng== dependencies: - "@abp/datatables.net" "~9.1.0" + "@abp/datatables.net" "~9.2.0-rc.2" datatables.net-bs5 "^2.1.8" -"@abp/datatables.net@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.1.0.tgz#12d6e1412aac4a2e0a61437dcdfbfbe1add7fed7" - integrity sha512-lYRvEZUkntaI+dS9Xkz23Z54eDeZ1R4Wzhqcg6VIsz3iO5kYAa5GiQjcOiBssNGJSLuaet6R5Pqh1ThEUjfvvA== +"@abp/datatables.net@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.2.0-rc.2.tgz#2880ccb61750faa1689aec15f9e49d92ab1e94a1" + integrity sha512-nljq0p8mxoBvMZngtViInyQJqX/MI7EyQeCIYNICF2LO42RRZfpKaRy5zX+4Uc3z/wz209CfK8QfRNH/d1HtBQ== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" datatables.net "^2.1.8" -"@abp/font-awesome@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.1.0.tgz#af21ff2d7c332deb1df626404b53a78d790ba842" - integrity sha512-HcJzDCACGejOIvhxsorCdaesyDHnupR/sl5YFCsGikSqATyOSKQy4pw8wKsb1z+FCT5JYCTR7ROJ3lB1lTEgZw== +"@abp/font-awesome@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.2.0-rc.2.tgz#51eb750f5697668db538ad68cc6b9279517da537" + integrity sha512-X9JfF4n4MDZLszwZHIHuxSdGGxImEzV91ROKqNeclErCx3bOXyq/l9C+haZl/3TLzTAOSDMLgHGrT3MUCVPF5w== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" "@fortawesome/fontawesome-free" "^6.6.0" -"@abp/jquery-form@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.1.0.tgz#bdb96436c54b2a09ac4259db97e1e577410e1fc3" - integrity sha512-xjHXWstVY6B3MPB4/8g0EJeHlVvlV010OMZKFmfCGLFi6aHAEKqqWh1rMItAiVuSmCCzL0m/208ZkpoCI++jGQ== +"@abp/jquery-form@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.2.0-rc.2.tgz#58ce31a32bdfb355e5d4e3f97cb4761f594aa8e1" + integrity sha512-xN2xjK1RWKygwaqzTaWmpMlU0kGWGa2nKaudM6g9k/B6b1Pfdr8ykI4DaSuPb4BkRmurGkgy4r9o7IUoqv0T8Q== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-form "^4.3.0" -"@abp/jquery-validation-unobtrusive@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.1.0.tgz#798551e7c14ba286a819a48804107a53e974c1ee" - integrity sha512-TggKckfbLtB7Ig2kNt2kH//GaJUns8OZbpdKdh7vCWIAGedw2FLNCABCpoGnyNp0s/dVOtdyxRl9eFRN3+a4tw== +"@abp/jquery-validation-unobtrusive@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.2.0-rc.2.tgz#4e624e22cbf149d1c78da46aabaf6fc18d3b65ff" + integrity sha512-adKJxnWnhCrIYWoVdo41zgO5p4SjyypCxoHeQ7mkohry2WN6WuQG5nmHqhpdevw966DENvmVzFfz7vnO72vtrg== dependencies: - "@abp/jquery-validation" "~9.1.0" + "@abp/jquery-validation" "~9.2.0-rc.2" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.1.0.tgz#b771652377fbfb3c92ce03b7b7c069a3bb2ae70b" - integrity sha512-bmPSOHcxjitB8NkiLv0SxxuSS3FlCq9SiTUGvsT8nOStDsm+KpyYSzZQq8LSCyqWWj54tlOARTfcD4tMK7/K3Q== +"@abp/jquery-validation@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.2.0-rc.2.tgz#b6a8124d5056ddad515733bffc78703f6bd422be" + integrity sha512-4j1fg1qImif2vFZQFJ7z1olEUnjc3BByKS+7joNQ+96/NqOQdOQjRjB2n490od7ExCinKnvYx3JFttpYjHU3Xw== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-validation "^1.21.0" -"@abp/jquery@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.1.0.tgz#952b61ea3e4a99e6551f667297dbb637d83f3c50" - integrity sha512-Ulw8ClAbqmxceNYCb76bR6dq7e9b7Hs6mjlEy05zdbsSNuZW3UoycsJ+BquXi+dUNBo6OppqEmhVeiIjAKTaFg== +"@abp/jquery@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.2.0-rc.2.tgz#335643787ab282d5d2fee4f360c2b62cce3db962" + integrity sha512-baKNDuiQMzkV0LFzBwEqS0JYtXIUe5KH+MVizGR6Zv10B8l/gIrUzWI/nnqDtbts6gmY9OBDXkCTcgMQBwKqjw== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" jquery "~3.7.1" -"@abp/lodash@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.1.0.tgz#62c784bd99c9e1ccfa82905e843375c594ef8584" - integrity sha512-cCE3ZwwWcPrh+ccKx4AS1USL1ttbrXBqci/l+ROjXhFzrs6GYnhI0+30xRZgPdPRm3U3nGPrSaDAU7UuHkRScg== +"@abp/lodash@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.2.0-rc.2.tgz#521bccafcc09780c751932688b6ec45c368c2854" + integrity sha512-T6eYsDovUl4AYD9n2N4WZq9MeC4/ryke5ctvdhzuArqM7WpPvohq3VKGugrP/MLo9oD3MZ5DVQvMJ0AVg+IXNQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" lodash "^4.17.21" -"@abp/luxon@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.1.0.tgz#b246f3627bbc7ce41b7878fce1679da473b0e0ae" - integrity sha512-AouS/qEK+i69hwr4XtUOCepONLuR+1gdHFb50RpOEbUoDfTYmYpxPBdiICmZPeJ3CExErITPZxzzngCnKPPOUw== +"@abp/luxon@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.2.0-rc.2.tgz#da366d335f9575b104ea2206e076af35cbbfcfba" + integrity sha512-mr8ipYrJDDJPoImIwORK7Z6d1C07APl95L17ufRbig4sjdxjNEDR5uUXVviVafF1No2bVkYiS0xGKQmojA2twg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" luxon "^3.5.0" -"@abp/malihu-custom-scrollbar-plugin@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.1.0.tgz#288ae1916a78100f2c728dfd05a560ee99fafae4" - integrity sha512-McdMRDtbTZBjp//3GMVjRjkc16HNYDEdPc2ar1xrY817e0tEln6/TBkItAcfOvjso0TsLcvlNL1+1bjbMjj8OA== +"@abp/malihu-custom-scrollbar-plugin@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.2.0-rc.2.tgz#cb79018adf08276041eb5c5dcf75130c8995686b" + integrity sha512-jdHXx7kPduaEfFXDvf5s+QVl10RqshNbnZkkqPx0mMkhz2H/qxwzF1e0mcLFL38nEgs+0gxIkcA8/MKq85tyqQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.1.0.tgz#f3c0ae02209e496913cda53df752dabc5be648bd" - integrity sha512-WxR1l09vwxRhqyvAVP4m4IBOFjIC4pzLOmelloqES+gL/klw+T/AmY21Z/z+OV0p7aJ3V3Q1aQStuMCDCyGFnQ== +"@abp/moment@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.2.0-rc.2.tgz#f4339138668ca72be1d700624b91789344557bff" + integrity sha512-J4ZaCDEcXLGmJ+8zqgaMK9Lutnjga28LSbuiFaGLlZ1QgYZ5HDScE128o3vX9qr60tc6IIY0cDkHi4bBHo7ozw== dependencies: moment "^2.30.1" -"@abp/select2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.1.0.tgz#9c2c6751f8506fb375c9b7c42a008c168a8f4c75" - integrity sha512-n6HqsH8aFLMrqL6LJ0gsHFlaq6T1QSt/x7HkkZyhDC7CwhXajxtsJEstDnDF9naHSXX/lerPbrHEgzn2QRMBqg== +"@abp/select2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.2.0-rc.2.tgz#ba6e839644b7ecdd198fe5286101302a3dc79ce3" + integrity sha512-AQMXb5ATgbN7grpKU8lB2xFFfirCe45lIvd9krW5SwUHCYZP7pRPbwQXAew+BqlIIYJJF2GA7/Nf//e/71bKtg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" select2 "^4.0.13" -"@abp/sweetalert2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.1.0.tgz#660f9b1db60cc04bde6cadbcf3c55320d20c2f3e" - integrity sha512-z3JMeAwva2vR8qZSRJ/BisQDdAEqjDJ/6oIGu/JYXQEchO1Lnd9UMV376nc3crLJdnyRRO7Qs6zx4FfVOCH0kw== +"@abp/sweetalert2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.2.0-rc.2.tgz#075634dc3ec996607757694d636d276ba2350e06" + integrity sha512-ZgHgqM8Q+V9ECQyAyepM5KpshKpQP463xRHwgY45oR7/CfCcO/vS56YQgJNg2zE6+lRQZaRQ9OL7lph8975vVg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" sweetalert2 "^11.14.1" -"@abp/timeago@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.1.0.tgz#496e40153241bae5d418d79cc6c65c9db6306447" - integrity sha512-a/UAYQ2GrhYFGx4p0hqr3x+teBqnjnOiWJEPq9//RTlz+EZ+OUyHbJrcUtqZRvaGJtOAsfmljD3jYnOXUlU7DA== +"@abp/timeago@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.2.0-rc.2.tgz#25324732966438bd6f505bdd932511ba2cc8c22b" + integrity sha512-WfWxxiycg77vVN2hPHqUOu552la+zxr0EZnHMqnEnnBe+gFYe9C0CpdYuyBq2eQXRXHoVzsB+4YRVNk5sbSGng== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" timeago "^1.6.7" -"@abp/toastr@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.1.0.tgz#501cbe211164b2e7416a2d4878cb34aed3550b7c" - integrity sha512-BkTDuU0p9Qa4+HT9CYwiPQjVTrbTSNBgQ18h69S3lIadLJwdC6pXeLbL9bhe+5fiuhq2f+0Oqo3X75LNEgzHkw== - dependencies: - "@abp/jquery" "~9.1.0" - toastr "^2.1.4" - -"@abp/utils@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.1.0.tgz#a4fd37d943df18b5c11f5d4869efd5d8882b7959" - integrity sha512-+UZo0ocTEZlRO41SzzHNdinjdFUDVq8wYZSj5BA1bqCVeWYhfjI2+rl9DuogCN8UEuCACA4K/qb4XcE/untQ3g== +"@abp/utils@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.2.0-rc.2.tgz#669885ad1e3ac7b8e182d7c392ea1b633df65aac" + integrity sha512-rfQxhJy04Qf50P3ciasKbDlJ+9JidD60xW77TjcoUJvljJE+A8iwTX8Wwqz7+YcC7rks2ILM4y3Ay7/47NOICQ== dependencies: just-compare "^2.3.0" @@ -263,7 +254,7 @@ jquery-validation@>=1.19, jquery-validation@^1.21.0: resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.21.0.tgz#78fc05ab76020912a246af3661b3f54a438bca93" integrity sha512-xNot0rlUIgu7duMcQ5qb6MGkGL/Z1PQaRJQoZAURW9+a/2PGOUxY36o/WyNeP2T9R6jvWB8Z9lUVvvQWI/Zs5w== -jquery@>=1.10, jquery@>=1.12.0, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2: +jquery@>=1.10, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2: version "3.6.4" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.4.tgz#ba065c188142100be4833699852bf7c24dc0252f" integrity sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ== @@ -321,10 +312,3 @@ timeago@^1.6.7: integrity sha512-FikcjN98+ij0siKH4VO4dZ358PR3oDDq4Vdl1+sN9gWz1/+JXGr3uZbUShYH/hL7bMhcTpPbplJU5Tej4b4jbQ== dependencies: jquery ">=1.5.0 <4.0" - -toastr@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/toastr/-/toastr-2.1.4.tgz#8b43be64fb9d0c414871446f2db8e8ca4e95f181" - integrity sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA== - dependencies: - jquery ">=1.12.0" diff --git a/modules/cms-kit/angular/package.json b/modules/cms-kit/angular/package.json index 22be2478a6..8e8c9c00f3 100644 --- a/modules/cms-kit/angular/package.json +++ b/modules/cms-kit/angular/package.json @@ -15,11 +15,11 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~9.1.0", - "@abp/ng.identity": "~9.1.0", - "@abp/ng.setting-management": "~9.1.0", - "@abp/ng.tenant-management": "~9.1.0", - "@abp/ng.theme.basic": "~9.1.0", + "@abp/ng.account": "~9.2.0-rc.2", + "@abp/ng.identity": "~9.2.0-rc.2", + "@abp/ng.setting-management": "~9.2.0-rc.2", + "@abp/ng.tenant-management": "~9.2.0-rc.2", + "@abp/ng.theme.basic": "~9.2.0-rc.2", "@angular/animations": "~10.0.0", "@angular/common": "~10.0.0", "@angular/compiler": "~10.0.0", diff --git a/modules/cms-kit/angular/projects/cms-kit/package.json b/modules/cms-kit/angular/projects/cms-kit/package.json index 809fdf22e7..85e1f747dc 100644 --- a/modules/cms-kit/angular/projects/cms-kit/package.json +++ b/modules/cms-kit/angular/projects/cms-kit/package.json @@ -4,8 +4,8 @@ "peerDependencies": { "@angular/common": "^9.1.11", "@angular/core": "^9.1.11", - "@abp/ng.core": ">=9.1.0", - "@abp/ng.theme.shared": ">=9.1.0" + "@abp/ng.core": ">=9.2.0-rc.2", + "@abp/ng.theme.shared": ">=9.2.0-rc.2" }, "dependencies": { "tslib": "^2.0.0" diff --git a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/package.json b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/package.json index f87ddf48c9..b39f45087b 100644 --- a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/package.json +++ b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/package.json @@ -3,6 +3,6 @@ "name": "my-app-identityserver", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2" } } diff --git a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/yarn.lock b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/yarn.lock index 38a43d8ed5..7a9396e283 100644 --- a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/yarn.lock +++ b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/yarn.lock @@ -2,194 +2,185 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.1.0.tgz#e9e0321037c34b43145dfc7827c422fbf79f59ab" - integrity sha512-vLkyrdFXw0/MPyU2hzqGJJk7trmIlVMdflNIe/rF77UIRmJYHWaTmPt9i5PLN/O4s0jnE9kWlaaJ/VnnFWeGsQ== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~9.1.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.1.0.tgz#35a73580b9b67a33708f2cf897243b50c9ad7100" - integrity sha512-rggTYL9poOFICsOseDX22JNhD0KWH8Oxs0YH91TZjvm/F67cPemW99eBlJc/YdJ7bN6icRiebN8m81y9MSanyg== - dependencies: - "@abp/aspnetcore.mvc.ui" "~9.1.0" - "@abp/bootstrap" "~9.1.0" - "@abp/bootstrap-datepicker" "~9.1.0" - "@abp/bootstrap-daterangepicker" "~9.1.0" - "@abp/datatables.net-bs5" "~9.1.0" - "@abp/font-awesome" "~9.1.0" - "@abp/jquery-form" "~9.1.0" - "@abp/jquery-validation-unobtrusive" "~9.1.0" - "@abp/lodash" "~9.1.0" - "@abp/luxon" "~9.1.0" - "@abp/malihu-custom-scrollbar-plugin" "~9.1.0" - "@abp/moment" "~9.1.0" - "@abp/select2" "~9.1.0" - "@abp/sweetalert2" "~9.1.0" - "@abp/timeago" "~9.1.0" - "@abp/toastr" "~9.1.0" - -"@abp/aspnetcore.mvc.ui@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.1.0.tgz#38d058ab148a36ad4d63870e8a501ca339e4a70e" - integrity sha512-lUw1ij87KJp+Rj4cz8oSQkdRuoMq76l/wIAGwtXH+EOKusVpSJTOTioAV/tPKeKZ15ubE4bO5jWcQGxZkXgH3w== +"@abp/aspnetcore.mvc.ui.theme.basic@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.2.0-rc.2.tgz#e32c8bd046d7377ae3f6a703eac9069e10bcd600" + integrity sha512-oj4lGrzllgGrq7rI7gz1Xvq5TU+xT+isGVf4T8hF3INE/J2nIyK45HnkjDq5wpdM3P+HkdM1+RtWNrLSVu0Klg== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.2.0-rc.2.tgz#77562013b604d0193e361efa6801235179dc7b4f" + integrity sha512-5KcMGg6o6W5uwsujrjUARlZFgWmZeYBNCf1vkf3btakkwTzmPDbypUx6VrgBJbt4AUnYlMQbSHvbfK0d3I1e6w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~9.2.0-rc.2" + "@abp/bootstrap" "~9.2.0-rc.2" + "@abp/bootstrap-datepicker" "~9.2.0-rc.2" + "@abp/bootstrap-daterangepicker" "~9.2.0-rc.2" + "@abp/datatables.net-bs5" "~9.2.0-rc.2" + "@abp/font-awesome" "~9.2.0-rc.2" + "@abp/jquery-form" "~9.2.0-rc.2" + "@abp/jquery-validation-unobtrusive" "~9.2.0-rc.2" + "@abp/lodash" "~9.2.0-rc.2" + "@abp/luxon" "~9.2.0-rc.2" + "@abp/malihu-custom-scrollbar-plugin" "~9.2.0-rc.2" + "@abp/moment" "~9.2.0-rc.2" + "@abp/select2" "~9.2.0-rc.2" + "@abp/sweetalert2" "~9.2.0-rc.2" + "@abp/timeago" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.2.0-rc.2.tgz#e1c0e4b0280d5fe37659da8dc696150f0d461336" + integrity sha512-WG4WWGS1mSb2QWAVOA2nrvoi9/3oueEaKEG18xbPgWiLz5GCq8gsopKqUgW/fmBaw+1Jh9tsuCTH7eED/2tUxA== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.1.0.tgz#96a1875a2b92f03b8f90f7842ebf8849cf8a4d04" - integrity sha512-3FQm1SK8qmzoP7LOvXkZpq1AzrdPHKRdQJjoqK9N+lQxQjNfOxEFMxeDSg6WJKO619Cnve++jAyMidojXlCiwQ== +"@abp/bootstrap-datepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.2.0-rc.2.tgz#429c2d44a22f9190013aea0efa420854b7e83a4f" + integrity sha512-bxMpaxTgnF5/VELbKvtZVw0tI0bowzUQFVSBQJqO4o7wJmxyHNzJuaom8Is+tCsoMrpo8t9EGMGfpWHV1IsrjQ== dependencies: bootstrap-datepicker "^1.10.0" -"@abp/bootstrap-daterangepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.1.0.tgz#1993c539837eb31de174cba9f0bccd441a98f209" - integrity sha512-jS1jTqP6KCXQYX9xZpDAI07C94nwzXrNJD3H0pJXsXoOva3TTBQDD8khf6RDb7tiZrkrNJqmMyUiCfVTDbO7zg== +"@abp/bootstrap-daterangepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.2.0-rc.2.tgz#85bb869d5d43eb6c97f50943ea2c390cb4d37d54" + integrity sha512-rLLsY6GCY16IOwRkmx7vZ25sVHOM9dPdTlcZiuUuEi5+QrmgGiqZmIwqYiu4WFSPPTJtufsyMaHzXxqiZRpXVg== dependencies: bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.1.0.tgz#a06c1cd4c11554c29973db70c6a383efebb10b4e" - integrity sha512-r6onkBnpqoWrdYLDVZTiqgQBvH5UZerYRrPB3Ky+RkXJhgwn7AM3LAVrB42sIvFZRNbwOlvuAL+a0+5yDOH67Q== +"@abp/bootstrap@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.2.0-rc.2.tgz#ba2ab98d930501e7f004f99e7d7e7d4e6d3c0998" + integrity sha512-u2mtHHbmXNgngExl4hM5uo/E3k453n0im2Ci02+4dnhZoFicLZlBdtJusZAw5QR1XTLPybW6RXofWBvkZcgiwg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" bootstrap "^5.3.3" -"@abp/core@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.1.0.tgz#af78fc096932d4ec45216b5e910c87a72e479b1f" - integrity sha512-6P0KGh5+IqlhQnUOyzaGaKrKzOAm1G0gg8Ay3DXyW2ksY6Zr+lyi5cxNsSi4ix1dicqfydIKvLfV+DF3mQf/dw== +"@abp/core@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.2.0-rc.2.tgz#0eab20842f8c0801d1dca036d3d66a67e155e019" + integrity sha512-8bC1MAV4iSFLJvRvM5mvrI9+N5FPAjaG79hr3p0xJtNixFOFpNELDf4p0zwy97zY8juL+AIEyJAkg+KIIK90DQ== dependencies: - "@abp/utils" "~9.1.0" + "@abp/utils" "~9.2.0-rc.2" -"@abp/datatables.net-bs5@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.1.0.tgz#3b2e7d68e947db3d3432ab5e8bff4da89a85d05d" - integrity sha512-zBrzOrfvTPJcc6mvHZDPVdXbWesKU0lKlrb7vuBZxWjrFNwVC0d/KNJPcYVvyQ3zf9rrQB5Xg0MDgu0k5Tt7tQ== +"@abp/datatables.net-bs5@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.2.0-rc.2.tgz#8c07544cafd2d2257f45150d3265c25eafc0ec0f" + integrity sha512-l3VOtc0fleUsx+ZUMr49+nPBWRBUVw4Z26ogT7O7uRR8doeerNU1GfA4yFJCUp02WIwt8cmZ+apJHADykn9Vng== dependencies: - "@abp/datatables.net" "~9.1.0" + "@abp/datatables.net" "~9.2.0-rc.2" datatables.net-bs5 "^2.1.8" -"@abp/datatables.net@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.1.0.tgz#12d6e1412aac4a2e0a61437dcdfbfbe1add7fed7" - integrity sha512-lYRvEZUkntaI+dS9Xkz23Z54eDeZ1R4Wzhqcg6VIsz3iO5kYAa5GiQjcOiBssNGJSLuaet6R5Pqh1ThEUjfvvA== +"@abp/datatables.net@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.2.0-rc.2.tgz#2880ccb61750faa1689aec15f9e49d92ab1e94a1" + integrity sha512-nljq0p8mxoBvMZngtViInyQJqX/MI7EyQeCIYNICF2LO42RRZfpKaRy5zX+4Uc3z/wz209CfK8QfRNH/d1HtBQ== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" datatables.net "^2.1.8" -"@abp/font-awesome@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.1.0.tgz#af21ff2d7c332deb1df626404b53a78d790ba842" - integrity sha512-HcJzDCACGejOIvhxsorCdaesyDHnupR/sl5YFCsGikSqATyOSKQy4pw8wKsb1z+FCT5JYCTR7ROJ3lB1lTEgZw== +"@abp/font-awesome@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.2.0-rc.2.tgz#51eb750f5697668db538ad68cc6b9279517da537" + integrity sha512-X9JfF4n4MDZLszwZHIHuxSdGGxImEzV91ROKqNeclErCx3bOXyq/l9C+haZl/3TLzTAOSDMLgHGrT3MUCVPF5w== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" "@fortawesome/fontawesome-free" "^6.6.0" -"@abp/jquery-form@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.1.0.tgz#bdb96436c54b2a09ac4259db97e1e577410e1fc3" - integrity sha512-xjHXWstVY6B3MPB4/8g0EJeHlVvlV010OMZKFmfCGLFi6aHAEKqqWh1rMItAiVuSmCCzL0m/208ZkpoCI++jGQ== +"@abp/jquery-form@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.2.0-rc.2.tgz#58ce31a32bdfb355e5d4e3f97cb4761f594aa8e1" + integrity sha512-xN2xjK1RWKygwaqzTaWmpMlU0kGWGa2nKaudM6g9k/B6b1Pfdr8ykI4DaSuPb4BkRmurGkgy4r9o7IUoqv0T8Q== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-form "^4.3.0" -"@abp/jquery-validation-unobtrusive@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.1.0.tgz#798551e7c14ba286a819a48804107a53e974c1ee" - integrity sha512-TggKckfbLtB7Ig2kNt2kH//GaJUns8OZbpdKdh7vCWIAGedw2FLNCABCpoGnyNp0s/dVOtdyxRl9eFRN3+a4tw== +"@abp/jquery-validation-unobtrusive@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.2.0-rc.2.tgz#4e624e22cbf149d1c78da46aabaf6fc18d3b65ff" + integrity sha512-adKJxnWnhCrIYWoVdo41zgO5p4SjyypCxoHeQ7mkohry2WN6WuQG5nmHqhpdevw966DENvmVzFfz7vnO72vtrg== dependencies: - "@abp/jquery-validation" "~9.1.0" + "@abp/jquery-validation" "~9.2.0-rc.2" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.1.0.tgz#b771652377fbfb3c92ce03b7b7c069a3bb2ae70b" - integrity sha512-bmPSOHcxjitB8NkiLv0SxxuSS3FlCq9SiTUGvsT8nOStDsm+KpyYSzZQq8LSCyqWWj54tlOARTfcD4tMK7/K3Q== +"@abp/jquery-validation@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.2.0-rc.2.tgz#b6a8124d5056ddad515733bffc78703f6bd422be" + integrity sha512-4j1fg1qImif2vFZQFJ7z1olEUnjc3BByKS+7joNQ+96/NqOQdOQjRjB2n490od7ExCinKnvYx3JFttpYjHU3Xw== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-validation "^1.21.0" -"@abp/jquery@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.1.0.tgz#952b61ea3e4a99e6551f667297dbb637d83f3c50" - integrity sha512-Ulw8ClAbqmxceNYCb76bR6dq7e9b7Hs6mjlEy05zdbsSNuZW3UoycsJ+BquXi+dUNBo6OppqEmhVeiIjAKTaFg== +"@abp/jquery@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.2.0-rc.2.tgz#335643787ab282d5d2fee4f360c2b62cce3db962" + integrity sha512-baKNDuiQMzkV0LFzBwEqS0JYtXIUe5KH+MVizGR6Zv10B8l/gIrUzWI/nnqDtbts6gmY9OBDXkCTcgMQBwKqjw== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" jquery "~3.7.1" -"@abp/lodash@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.1.0.tgz#62c784bd99c9e1ccfa82905e843375c594ef8584" - integrity sha512-cCE3ZwwWcPrh+ccKx4AS1USL1ttbrXBqci/l+ROjXhFzrs6GYnhI0+30xRZgPdPRm3U3nGPrSaDAU7UuHkRScg== +"@abp/lodash@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.2.0-rc.2.tgz#521bccafcc09780c751932688b6ec45c368c2854" + integrity sha512-T6eYsDovUl4AYD9n2N4WZq9MeC4/ryke5ctvdhzuArqM7WpPvohq3VKGugrP/MLo9oD3MZ5DVQvMJ0AVg+IXNQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" lodash "^4.17.21" -"@abp/luxon@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.1.0.tgz#b246f3627bbc7ce41b7878fce1679da473b0e0ae" - integrity sha512-AouS/qEK+i69hwr4XtUOCepONLuR+1gdHFb50RpOEbUoDfTYmYpxPBdiICmZPeJ3CExErITPZxzzngCnKPPOUw== +"@abp/luxon@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.2.0-rc.2.tgz#da366d335f9575b104ea2206e076af35cbbfcfba" + integrity sha512-mr8ipYrJDDJPoImIwORK7Z6d1C07APl95L17ufRbig4sjdxjNEDR5uUXVviVafF1No2bVkYiS0xGKQmojA2twg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" luxon "^3.5.0" -"@abp/malihu-custom-scrollbar-plugin@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.1.0.tgz#288ae1916a78100f2c728dfd05a560ee99fafae4" - integrity sha512-McdMRDtbTZBjp//3GMVjRjkc16HNYDEdPc2ar1xrY817e0tEln6/TBkItAcfOvjso0TsLcvlNL1+1bjbMjj8OA== +"@abp/malihu-custom-scrollbar-plugin@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.2.0-rc.2.tgz#cb79018adf08276041eb5c5dcf75130c8995686b" + integrity sha512-jdHXx7kPduaEfFXDvf5s+QVl10RqshNbnZkkqPx0mMkhz2H/qxwzF1e0mcLFL38nEgs+0gxIkcA8/MKq85tyqQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.1.0.tgz#f3c0ae02209e496913cda53df752dabc5be648bd" - integrity sha512-WxR1l09vwxRhqyvAVP4m4IBOFjIC4pzLOmelloqES+gL/klw+T/AmY21Z/z+OV0p7aJ3V3Q1aQStuMCDCyGFnQ== +"@abp/moment@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.2.0-rc.2.tgz#f4339138668ca72be1d700624b91789344557bff" + integrity sha512-J4ZaCDEcXLGmJ+8zqgaMK9Lutnjga28LSbuiFaGLlZ1QgYZ5HDScE128o3vX9qr60tc6IIY0cDkHi4bBHo7ozw== dependencies: moment "^2.30.1" -"@abp/select2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.1.0.tgz#9c2c6751f8506fb375c9b7c42a008c168a8f4c75" - integrity sha512-n6HqsH8aFLMrqL6LJ0gsHFlaq6T1QSt/x7HkkZyhDC7CwhXajxtsJEstDnDF9naHSXX/lerPbrHEgzn2QRMBqg== +"@abp/select2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.2.0-rc.2.tgz#ba6e839644b7ecdd198fe5286101302a3dc79ce3" + integrity sha512-AQMXb5ATgbN7grpKU8lB2xFFfirCe45lIvd9krW5SwUHCYZP7pRPbwQXAew+BqlIIYJJF2GA7/Nf//e/71bKtg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" select2 "^4.0.13" -"@abp/sweetalert2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.1.0.tgz#660f9b1db60cc04bde6cadbcf3c55320d20c2f3e" - integrity sha512-z3JMeAwva2vR8qZSRJ/BisQDdAEqjDJ/6oIGu/JYXQEchO1Lnd9UMV376nc3crLJdnyRRO7Qs6zx4FfVOCH0kw== +"@abp/sweetalert2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.2.0-rc.2.tgz#075634dc3ec996607757694d636d276ba2350e06" + integrity sha512-ZgHgqM8Q+V9ECQyAyepM5KpshKpQP463xRHwgY45oR7/CfCcO/vS56YQgJNg2zE6+lRQZaRQ9OL7lph8975vVg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" sweetalert2 "^11.14.1" -"@abp/timeago@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.1.0.tgz#496e40153241bae5d418d79cc6c65c9db6306447" - integrity sha512-a/UAYQ2GrhYFGx4p0hqr3x+teBqnjnOiWJEPq9//RTlz+EZ+OUyHbJrcUtqZRvaGJtOAsfmljD3jYnOXUlU7DA== +"@abp/timeago@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.2.0-rc.2.tgz#25324732966438bd6f505bdd932511ba2cc8c22b" + integrity sha512-WfWxxiycg77vVN2hPHqUOu552la+zxr0EZnHMqnEnnBe+gFYe9C0CpdYuyBq2eQXRXHoVzsB+4YRVNk5sbSGng== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" timeago "^1.6.7" -"@abp/toastr@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.1.0.tgz#501cbe211164b2e7416a2d4878cb34aed3550b7c" - integrity sha512-BkTDuU0p9Qa4+HT9CYwiPQjVTrbTSNBgQ18h69S3lIadLJwdC6pXeLbL9bhe+5fiuhq2f+0Oqo3X75LNEgzHkw== - dependencies: - "@abp/jquery" "~9.1.0" - toastr "^2.1.4" - -"@abp/utils@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.1.0.tgz#a4fd37d943df18b5c11f5d4869efd5d8882b7959" - integrity sha512-+UZo0ocTEZlRO41SzzHNdinjdFUDVq8wYZSj5BA1bqCVeWYhfjI2+rl9DuogCN8UEuCACA4K/qb4XcE/untQ3g== +"@abp/utils@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.2.0-rc.2.tgz#669885ad1e3ac7b8e182d7c392ea1b633df65aac" + integrity sha512-rfQxhJy04Qf50P3ciasKbDlJ+9JidD60xW77TjcoUJvljJE+A8iwTX8Wwqz7+YcC7rks2ILM4y3Ay7/47NOICQ== dependencies: just-compare "^2.3.0" @@ -263,7 +254,7 @@ jquery-validation@>=1.19, jquery-validation@^1.21.0: resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.21.0.tgz#78fc05ab76020912a246af3661b3f54a438bca93" integrity sha512-xNot0rlUIgu7duMcQ5qb6MGkGL/Z1PQaRJQoZAURW9+a/2PGOUxY36o/WyNeP2T9R6jvWB8Z9lUVvvQWI/Zs5w== -jquery@>=1.10, jquery@>=1.12.0, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2: +jquery@>=1.10, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2: version "3.6.4" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.4.tgz#ba065c188142100be4833699852bf7c24dc0252f" integrity sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ== @@ -321,10 +312,3 @@ timeago@^1.6.7: integrity sha512-FikcjN98+ij0siKH4VO4dZ358PR3oDDq4Vdl1+sN9gWz1/+JXGr3uZbUShYH/hL7bMhcTpPbplJU5Tej4b4jbQ== dependencies: jquery ">=1.5.0 <4.0" - -toastr@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/toastr/-/toastr-2.1.4.tgz#8b43be64fb9d0c414871446f2db8e8ca4e95f181" - integrity sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA== - dependencies: - jquery ">=1.12.0" diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Host/package.json b/modules/cms-kit/host/Volo.CmsKit.Web.Host/package.json index ef17244517..701670d422 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Host/package.json +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2" } } diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Host/yarn.lock b/modules/cms-kit/host/Volo.CmsKit.Web.Host/yarn.lock index 38a43d8ed5..7a9396e283 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Host/yarn.lock +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Host/yarn.lock @@ -2,194 +2,185 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.1.0.tgz#e9e0321037c34b43145dfc7827c422fbf79f59ab" - integrity sha512-vLkyrdFXw0/MPyU2hzqGJJk7trmIlVMdflNIe/rF77UIRmJYHWaTmPt9i5PLN/O4s0jnE9kWlaaJ/VnnFWeGsQ== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~9.1.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.1.0.tgz#35a73580b9b67a33708f2cf897243b50c9ad7100" - integrity sha512-rggTYL9poOFICsOseDX22JNhD0KWH8Oxs0YH91TZjvm/F67cPemW99eBlJc/YdJ7bN6icRiebN8m81y9MSanyg== - dependencies: - "@abp/aspnetcore.mvc.ui" "~9.1.0" - "@abp/bootstrap" "~9.1.0" - "@abp/bootstrap-datepicker" "~9.1.0" - "@abp/bootstrap-daterangepicker" "~9.1.0" - "@abp/datatables.net-bs5" "~9.1.0" - "@abp/font-awesome" "~9.1.0" - "@abp/jquery-form" "~9.1.0" - "@abp/jquery-validation-unobtrusive" "~9.1.0" - "@abp/lodash" "~9.1.0" - "@abp/luxon" "~9.1.0" - "@abp/malihu-custom-scrollbar-plugin" "~9.1.0" - "@abp/moment" "~9.1.0" - "@abp/select2" "~9.1.0" - "@abp/sweetalert2" "~9.1.0" - "@abp/timeago" "~9.1.0" - "@abp/toastr" "~9.1.0" - -"@abp/aspnetcore.mvc.ui@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.1.0.tgz#38d058ab148a36ad4d63870e8a501ca339e4a70e" - integrity sha512-lUw1ij87KJp+Rj4cz8oSQkdRuoMq76l/wIAGwtXH+EOKusVpSJTOTioAV/tPKeKZ15ubE4bO5jWcQGxZkXgH3w== +"@abp/aspnetcore.mvc.ui.theme.basic@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.2.0-rc.2.tgz#e32c8bd046d7377ae3f6a703eac9069e10bcd600" + integrity sha512-oj4lGrzllgGrq7rI7gz1Xvq5TU+xT+isGVf4T8hF3INE/J2nIyK45HnkjDq5wpdM3P+HkdM1+RtWNrLSVu0Klg== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.2.0-rc.2.tgz#77562013b604d0193e361efa6801235179dc7b4f" + integrity sha512-5KcMGg6o6W5uwsujrjUARlZFgWmZeYBNCf1vkf3btakkwTzmPDbypUx6VrgBJbt4AUnYlMQbSHvbfK0d3I1e6w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~9.2.0-rc.2" + "@abp/bootstrap" "~9.2.0-rc.2" + "@abp/bootstrap-datepicker" "~9.2.0-rc.2" + "@abp/bootstrap-daterangepicker" "~9.2.0-rc.2" + "@abp/datatables.net-bs5" "~9.2.0-rc.2" + "@abp/font-awesome" "~9.2.0-rc.2" + "@abp/jquery-form" "~9.2.0-rc.2" + "@abp/jquery-validation-unobtrusive" "~9.2.0-rc.2" + "@abp/lodash" "~9.2.0-rc.2" + "@abp/luxon" "~9.2.0-rc.2" + "@abp/malihu-custom-scrollbar-plugin" "~9.2.0-rc.2" + "@abp/moment" "~9.2.0-rc.2" + "@abp/select2" "~9.2.0-rc.2" + "@abp/sweetalert2" "~9.2.0-rc.2" + "@abp/timeago" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.2.0-rc.2.tgz#e1c0e4b0280d5fe37659da8dc696150f0d461336" + integrity sha512-WG4WWGS1mSb2QWAVOA2nrvoi9/3oueEaKEG18xbPgWiLz5GCq8gsopKqUgW/fmBaw+1Jh9tsuCTH7eED/2tUxA== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.1.0.tgz#96a1875a2b92f03b8f90f7842ebf8849cf8a4d04" - integrity sha512-3FQm1SK8qmzoP7LOvXkZpq1AzrdPHKRdQJjoqK9N+lQxQjNfOxEFMxeDSg6WJKO619Cnve++jAyMidojXlCiwQ== +"@abp/bootstrap-datepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.2.0-rc.2.tgz#429c2d44a22f9190013aea0efa420854b7e83a4f" + integrity sha512-bxMpaxTgnF5/VELbKvtZVw0tI0bowzUQFVSBQJqO4o7wJmxyHNzJuaom8Is+tCsoMrpo8t9EGMGfpWHV1IsrjQ== dependencies: bootstrap-datepicker "^1.10.0" -"@abp/bootstrap-daterangepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.1.0.tgz#1993c539837eb31de174cba9f0bccd441a98f209" - integrity sha512-jS1jTqP6KCXQYX9xZpDAI07C94nwzXrNJD3H0pJXsXoOva3TTBQDD8khf6RDb7tiZrkrNJqmMyUiCfVTDbO7zg== +"@abp/bootstrap-daterangepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.2.0-rc.2.tgz#85bb869d5d43eb6c97f50943ea2c390cb4d37d54" + integrity sha512-rLLsY6GCY16IOwRkmx7vZ25sVHOM9dPdTlcZiuUuEi5+QrmgGiqZmIwqYiu4WFSPPTJtufsyMaHzXxqiZRpXVg== dependencies: bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.1.0.tgz#a06c1cd4c11554c29973db70c6a383efebb10b4e" - integrity sha512-r6onkBnpqoWrdYLDVZTiqgQBvH5UZerYRrPB3Ky+RkXJhgwn7AM3LAVrB42sIvFZRNbwOlvuAL+a0+5yDOH67Q== +"@abp/bootstrap@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.2.0-rc.2.tgz#ba2ab98d930501e7f004f99e7d7e7d4e6d3c0998" + integrity sha512-u2mtHHbmXNgngExl4hM5uo/E3k453n0im2Ci02+4dnhZoFicLZlBdtJusZAw5QR1XTLPybW6RXofWBvkZcgiwg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" bootstrap "^5.3.3" -"@abp/core@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.1.0.tgz#af78fc096932d4ec45216b5e910c87a72e479b1f" - integrity sha512-6P0KGh5+IqlhQnUOyzaGaKrKzOAm1G0gg8Ay3DXyW2ksY6Zr+lyi5cxNsSi4ix1dicqfydIKvLfV+DF3mQf/dw== +"@abp/core@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.2.0-rc.2.tgz#0eab20842f8c0801d1dca036d3d66a67e155e019" + integrity sha512-8bC1MAV4iSFLJvRvM5mvrI9+N5FPAjaG79hr3p0xJtNixFOFpNELDf4p0zwy97zY8juL+AIEyJAkg+KIIK90DQ== dependencies: - "@abp/utils" "~9.1.0" + "@abp/utils" "~9.2.0-rc.2" -"@abp/datatables.net-bs5@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.1.0.tgz#3b2e7d68e947db3d3432ab5e8bff4da89a85d05d" - integrity sha512-zBrzOrfvTPJcc6mvHZDPVdXbWesKU0lKlrb7vuBZxWjrFNwVC0d/KNJPcYVvyQ3zf9rrQB5Xg0MDgu0k5Tt7tQ== +"@abp/datatables.net-bs5@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.2.0-rc.2.tgz#8c07544cafd2d2257f45150d3265c25eafc0ec0f" + integrity sha512-l3VOtc0fleUsx+ZUMr49+nPBWRBUVw4Z26ogT7O7uRR8doeerNU1GfA4yFJCUp02WIwt8cmZ+apJHADykn9Vng== dependencies: - "@abp/datatables.net" "~9.1.0" + "@abp/datatables.net" "~9.2.0-rc.2" datatables.net-bs5 "^2.1.8" -"@abp/datatables.net@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.1.0.tgz#12d6e1412aac4a2e0a61437dcdfbfbe1add7fed7" - integrity sha512-lYRvEZUkntaI+dS9Xkz23Z54eDeZ1R4Wzhqcg6VIsz3iO5kYAa5GiQjcOiBssNGJSLuaet6R5Pqh1ThEUjfvvA== +"@abp/datatables.net@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.2.0-rc.2.tgz#2880ccb61750faa1689aec15f9e49d92ab1e94a1" + integrity sha512-nljq0p8mxoBvMZngtViInyQJqX/MI7EyQeCIYNICF2LO42RRZfpKaRy5zX+4Uc3z/wz209CfK8QfRNH/d1HtBQ== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" datatables.net "^2.1.8" -"@abp/font-awesome@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.1.0.tgz#af21ff2d7c332deb1df626404b53a78d790ba842" - integrity sha512-HcJzDCACGejOIvhxsorCdaesyDHnupR/sl5YFCsGikSqATyOSKQy4pw8wKsb1z+FCT5JYCTR7ROJ3lB1lTEgZw== +"@abp/font-awesome@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.2.0-rc.2.tgz#51eb750f5697668db538ad68cc6b9279517da537" + integrity sha512-X9JfF4n4MDZLszwZHIHuxSdGGxImEzV91ROKqNeclErCx3bOXyq/l9C+haZl/3TLzTAOSDMLgHGrT3MUCVPF5w== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" "@fortawesome/fontawesome-free" "^6.6.0" -"@abp/jquery-form@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.1.0.tgz#bdb96436c54b2a09ac4259db97e1e577410e1fc3" - integrity sha512-xjHXWstVY6B3MPB4/8g0EJeHlVvlV010OMZKFmfCGLFi6aHAEKqqWh1rMItAiVuSmCCzL0m/208ZkpoCI++jGQ== +"@abp/jquery-form@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.2.0-rc.2.tgz#58ce31a32bdfb355e5d4e3f97cb4761f594aa8e1" + integrity sha512-xN2xjK1RWKygwaqzTaWmpMlU0kGWGa2nKaudM6g9k/B6b1Pfdr8ykI4DaSuPb4BkRmurGkgy4r9o7IUoqv0T8Q== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-form "^4.3.0" -"@abp/jquery-validation-unobtrusive@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.1.0.tgz#798551e7c14ba286a819a48804107a53e974c1ee" - integrity sha512-TggKckfbLtB7Ig2kNt2kH//GaJUns8OZbpdKdh7vCWIAGedw2FLNCABCpoGnyNp0s/dVOtdyxRl9eFRN3+a4tw== +"@abp/jquery-validation-unobtrusive@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.2.0-rc.2.tgz#4e624e22cbf149d1c78da46aabaf6fc18d3b65ff" + integrity sha512-adKJxnWnhCrIYWoVdo41zgO5p4SjyypCxoHeQ7mkohry2WN6WuQG5nmHqhpdevw966DENvmVzFfz7vnO72vtrg== dependencies: - "@abp/jquery-validation" "~9.1.0" + "@abp/jquery-validation" "~9.2.0-rc.2" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.1.0.tgz#b771652377fbfb3c92ce03b7b7c069a3bb2ae70b" - integrity sha512-bmPSOHcxjitB8NkiLv0SxxuSS3FlCq9SiTUGvsT8nOStDsm+KpyYSzZQq8LSCyqWWj54tlOARTfcD4tMK7/K3Q== +"@abp/jquery-validation@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.2.0-rc.2.tgz#b6a8124d5056ddad515733bffc78703f6bd422be" + integrity sha512-4j1fg1qImif2vFZQFJ7z1olEUnjc3BByKS+7joNQ+96/NqOQdOQjRjB2n490od7ExCinKnvYx3JFttpYjHU3Xw== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-validation "^1.21.0" -"@abp/jquery@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.1.0.tgz#952b61ea3e4a99e6551f667297dbb637d83f3c50" - integrity sha512-Ulw8ClAbqmxceNYCb76bR6dq7e9b7Hs6mjlEy05zdbsSNuZW3UoycsJ+BquXi+dUNBo6OppqEmhVeiIjAKTaFg== +"@abp/jquery@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.2.0-rc.2.tgz#335643787ab282d5d2fee4f360c2b62cce3db962" + integrity sha512-baKNDuiQMzkV0LFzBwEqS0JYtXIUe5KH+MVizGR6Zv10B8l/gIrUzWI/nnqDtbts6gmY9OBDXkCTcgMQBwKqjw== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" jquery "~3.7.1" -"@abp/lodash@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.1.0.tgz#62c784bd99c9e1ccfa82905e843375c594ef8584" - integrity sha512-cCE3ZwwWcPrh+ccKx4AS1USL1ttbrXBqci/l+ROjXhFzrs6GYnhI0+30xRZgPdPRm3U3nGPrSaDAU7UuHkRScg== +"@abp/lodash@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.2.0-rc.2.tgz#521bccafcc09780c751932688b6ec45c368c2854" + integrity sha512-T6eYsDovUl4AYD9n2N4WZq9MeC4/ryke5ctvdhzuArqM7WpPvohq3VKGugrP/MLo9oD3MZ5DVQvMJ0AVg+IXNQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" lodash "^4.17.21" -"@abp/luxon@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.1.0.tgz#b246f3627bbc7ce41b7878fce1679da473b0e0ae" - integrity sha512-AouS/qEK+i69hwr4XtUOCepONLuR+1gdHFb50RpOEbUoDfTYmYpxPBdiICmZPeJ3CExErITPZxzzngCnKPPOUw== +"@abp/luxon@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.2.0-rc.2.tgz#da366d335f9575b104ea2206e076af35cbbfcfba" + integrity sha512-mr8ipYrJDDJPoImIwORK7Z6d1C07APl95L17ufRbig4sjdxjNEDR5uUXVviVafF1No2bVkYiS0xGKQmojA2twg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" luxon "^3.5.0" -"@abp/malihu-custom-scrollbar-plugin@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.1.0.tgz#288ae1916a78100f2c728dfd05a560ee99fafae4" - integrity sha512-McdMRDtbTZBjp//3GMVjRjkc16HNYDEdPc2ar1xrY817e0tEln6/TBkItAcfOvjso0TsLcvlNL1+1bjbMjj8OA== +"@abp/malihu-custom-scrollbar-plugin@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.2.0-rc.2.tgz#cb79018adf08276041eb5c5dcf75130c8995686b" + integrity sha512-jdHXx7kPduaEfFXDvf5s+QVl10RqshNbnZkkqPx0mMkhz2H/qxwzF1e0mcLFL38nEgs+0gxIkcA8/MKq85tyqQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.1.0.tgz#f3c0ae02209e496913cda53df752dabc5be648bd" - integrity sha512-WxR1l09vwxRhqyvAVP4m4IBOFjIC4pzLOmelloqES+gL/klw+T/AmY21Z/z+OV0p7aJ3V3Q1aQStuMCDCyGFnQ== +"@abp/moment@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.2.0-rc.2.tgz#f4339138668ca72be1d700624b91789344557bff" + integrity sha512-J4ZaCDEcXLGmJ+8zqgaMK9Lutnjga28LSbuiFaGLlZ1QgYZ5HDScE128o3vX9qr60tc6IIY0cDkHi4bBHo7ozw== dependencies: moment "^2.30.1" -"@abp/select2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.1.0.tgz#9c2c6751f8506fb375c9b7c42a008c168a8f4c75" - integrity sha512-n6HqsH8aFLMrqL6LJ0gsHFlaq6T1QSt/x7HkkZyhDC7CwhXajxtsJEstDnDF9naHSXX/lerPbrHEgzn2QRMBqg== +"@abp/select2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.2.0-rc.2.tgz#ba6e839644b7ecdd198fe5286101302a3dc79ce3" + integrity sha512-AQMXb5ATgbN7grpKU8lB2xFFfirCe45lIvd9krW5SwUHCYZP7pRPbwQXAew+BqlIIYJJF2GA7/Nf//e/71bKtg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" select2 "^4.0.13" -"@abp/sweetalert2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.1.0.tgz#660f9b1db60cc04bde6cadbcf3c55320d20c2f3e" - integrity sha512-z3JMeAwva2vR8qZSRJ/BisQDdAEqjDJ/6oIGu/JYXQEchO1Lnd9UMV376nc3crLJdnyRRO7Qs6zx4FfVOCH0kw== +"@abp/sweetalert2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.2.0-rc.2.tgz#075634dc3ec996607757694d636d276ba2350e06" + integrity sha512-ZgHgqM8Q+V9ECQyAyepM5KpshKpQP463xRHwgY45oR7/CfCcO/vS56YQgJNg2zE6+lRQZaRQ9OL7lph8975vVg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" sweetalert2 "^11.14.1" -"@abp/timeago@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.1.0.tgz#496e40153241bae5d418d79cc6c65c9db6306447" - integrity sha512-a/UAYQ2GrhYFGx4p0hqr3x+teBqnjnOiWJEPq9//RTlz+EZ+OUyHbJrcUtqZRvaGJtOAsfmljD3jYnOXUlU7DA== +"@abp/timeago@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.2.0-rc.2.tgz#25324732966438bd6f505bdd932511ba2cc8c22b" + integrity sha512-WfWxxiycg77vVN2hPHqUOu552la+zxr0EZnHMqnEnnBe+gFYe9C0CpdYuyBq2eQXRXHoVzsB+4YRVNk5sbSGng== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" timeago "^1.6.7" -"@abp/toastr@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.1.0.tgz#501cbe211164b2e7416a2d4878cb34aed3550b7c" - integrity sha512-BkTDuU0p9Qa4+HT9CYwiPQjVTrbTSNBgQ18h69S3lIadLJwdC6pXeLbL9bhe+5fiuhq2f+0Oqo3X75LNEgzHkw== - dependencies: - "@abp/jquery" "~9.1.0" - toastr "^2.1.4" - -"@abp/utils@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.1.0.tgz#a4fd37d943df18b5c11f5d4869efd5d8882b7959" - integrity sha512-+UZo0ocTEZlRO41SzzHNdinjdFUDVq8wYZSj5BA1bqCVeWYhfjI2+rl9DuogCN8UEuCACA4K/qb4XcE/untQ3g== +"@abp/utils@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.2.0-rc.2.tgz#669885ad1e3ac7b8e182d7c392ea1b633df65aac" + integrity sha512-rfQxhJy04Qf50P3ciasKbDlJ+9JidD60xW77TjcoUJvljJE+A8iwTX8Wwqz7+YcC7rks2ILM4y3Ay7/47NOICQ== dependencies: just-compare "^2.3.0" @@ -263,7 +254,7 @@ jquery-validation@>=1.19, jquery-validation@^1.21.0: resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.21.0.tgz#78fc05ab76020912a246af3661b3f54a438bca93" integrity sha512-xNot0rlUIgu7duMcQ5qb6MGkGL/Z1PQaRJQoZAURW9+a/2PGOUxY36o/WyNeP2T9R6jvWB8Z9lUVvvQWI/Zs5w== -jquery@>=1.10, jquery@>=1.12.0, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2: +jquery@>=1.10, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2: version "3.6.4" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.4.tgz#ba065c188142100be4833699852bf7c24dc0252f" integrity sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ== @@ -321,10 +312,3 @@ timeago@^1.6.7: integrity sha512-FikcjN98+ij0siKH4VO4dZ358PR3oDDq4Vdl1+sN9gWz1/+JXGr3uZbUShYH/hL7bMhcTpPbplJU5Tej4b4jbQ== dependencies: jquery ">=1.5.0 <4.0" - -toastr@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/toastr/-/toastr-2.1.4.tgz#8b43be64fb9d0c414871446f2db8e8ca4e95f181" - integrity sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA== - dependencies: - jquery ">=1.12.0" diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json index 8efc393397..5243b8b399 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0", - "@abp/cms-kit": "9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2", + "@abp/cms-kit": "9.2.0-rc.2" } } diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock index b2df3e3058..dd4e5281d4 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock @@ -2,302 +2,293 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.1.0.tgz#e9e0321037c34b43145dfc7827c422fbf79f59ab" - integrity sha512-vLkyrdFXw0/MPyU2hzqGJJk7trmIlVMdflNIe/rF77UIRmJYHWaTmPt9i5PLN/O4s0jnE9kWlaaJ/VnnFWeGsQ== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~9.1.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.1.0.tgz#35a73580b9b67a33708f2cf897243b50c9ad7100" - integrity sha512-rggTYL9poOFICsOseDX22JNhD0KWH8Oxs0YH91TZjvm/F67cPemW99eBlJc/YdJ7bN6icRiebN8m81y9MSanyg== - dependencies: - "@abp/aspnetcore.mvc.ui" "~9.1.0" - "@abp/bootstrap" "~9.1.0" - "@abp/bootstrap-datepicker" "~9.1.0" - "@abp/bootstrap-daterangepicker" "~9.1.0" - "@abp/datatables.net-bs5" "~9.1.0" - "@abp/font-awesome" "~9.1.0" - "@abp/jquery-form" "~9.1.0" - "@abp/jquery-validation-unobtrusive" "~9.1.0" - "@abp/lodash" "~9.1.0" - "@abp/luxon" "~9.1.0" - "@abp/malihu-custom-scrollbar-plugin" "~9.1.0" - "@abp/moment" "~9.1.0" - "@abp/select2" "~9.1.0" - "@abp/sweetalert2" "~9.1.0" - "@abp/timeago" "~9.1.0" - "@abp/toastr" "~9.1.0" - -"@abp/aspnetcore.mvc.ui@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.1.0.tgz#38d058ab148a36ad4d63870e8a501ca339e4a70e" - integrity sha512-lUw1ij87KJp+Rj4cz8oSQkdRuoMq76l/wIAGwtXH+EOKusVpSJTOTioAV/tPKeKZ15ubE4bO5jWcQGxZkXgH3w== +"@abp/aspnetcore.mvc.ui.theme.basic@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.2.0-rc.2.tgz#e32c8bd046d7377ae3f6a703eac9069e10bcd600" + integrity sha512-oj4lGrzllgGrq7rI7gz1Xvq5TU+xT+isGVf4T8hF3INE/J2nIyK45HnkjDq5wpdM3P+HkdM1+RtWNrLSVu0Klg== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.2.0-rc.2.tgz#77562013b604d0193e361efa6801235179dc7b4f" + integrity sha512-5KcMGg6o6W5uwsujrjUARlZFgWmZeYBNCf1vkf3btakkwTzmPDbypUx6VrgBJbt4AUnYlMQbSHvbfK0d3I1e6w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~9.2.0-rc.2" + "@abp/bootstrap" "~9.2.0-rc.2" + "@abp/bootstrap-datepicker" "~9.2.0-rc.2" + "@abp/bootstrap-daterangepicker" "~9.2.0-rc.2" + "@abp/datatables.net-bs5" "~9.2.0-rc.2" + "@abp/font-awesome" "~9.2.0-rc.2" + "@abp/jquery-form" "~9.2.0-rc.2" + "@abp/jquery-validation-unobtrusive" "~9.2.0-rc.2" + "@abp/lodash" "~9.2.0-rc.2" + "@abp/luxon" "~9.2.0-rc.2" + "@abp/malihu-custom-scrollbar-plugin" "~9.2.0-rc.2" + "@abp/moment" "~9.2.0-rc.2" + "@abp/select2" "~9.2.0-rc.2" + "@abp/sweetalert2" "~9.2.0-rc.2" + "@abp/timeago" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.2.0-rc.2.tgz#e1c0e4b0280d5fe37659da8dc696150f0d461336" + integrity sha512-WG4WWGS1mSb2QWAVOA2nrvoi9/3oueEaKEG18xbPgWiLz5GCq8gsopKqUgW/fmBaw+1Jh9tsuCTH7eED/2tUxA== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.1.0.tgz#96a1875a2b92f03b8f90f7842ebf8849cf8a4d04" - integrity sha512-3FQm1SK8qmzoP7LOvXkZpq1AzrdPHKRdQJjoqK9N+lQxQjNfOxEFMxeDSg6WJKO619Cnve++jAyMidojXlCiwQ== +"@abp/bootstrap-datepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.2.0-rc.2.tgz#429c2d44a22f9190013aea0efa420854b7e83a4f" + integrity sha512-bxMpaxTgnF5/VELbKvtZVw0tI0bowzUQFVSBQJqO4o7wJmxyHNzJuaom8Is+tCsoMrpo8t9EGMGfpWHV1IsrjQ== dependencies: bootstrap-datepicker "^1.10.0" -"@abp/bootstrap-daterangepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.1.0.tgz#1993c539837eb31de174cba9f0bccd441a98f209" - integrity sha512-jS1jTqP6KCXQYX9xZpDAI07C94nwzXrNJD3H0pJXsXoOva3TTBQDD8khf6RDb7tiZrkrNJqmMyUiCfVTDbO7zg== +"@abp/bootstrap-daterangepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.2.0-rc.2.tgz#85bb869d5d43eb6c97f50943ea2c390cb4d37d54" + integrity sha512-rLLsY6GCY16IOwRkmx7vZ25sVHOM9dPdTlcZiuUuEi5+QrmgGiqZmIwqYiu4WFSPPTJtufsyMaHzXxqiZRpXVg== dependencies: bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.1.0.tgz#a06c1cd4c11554c29973db70c6a383efebb10b4e" - integrity sha512-r6onkBnpqoWrdYLDVZTiqgQBvH5UZerYRrPB3Ky+RkXJhgwn7AM3LAVrB42sIvFZRNbwOlvuAL+a0+5yDOH67Q== +"@abp/bootstrap@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.2.0-rc.2.tgz#ba2ab98d930501e7f004f99e7d7e7d4e6d3c0998" + integrity sha512-u2mtHHbmXNgngExl4hM5uo/E3k453n0im2Ci02+4dnhZoFicLZlBdtJusZAw5QR1XTLPybW6RXofWBvkZcgiwg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" bootstrap "^5.3.3" -"@abp/clipboard@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.1.0.tgz#5d5678ef7e8c355715c5a74e4aabd0a21d5e6cbe" - integrity sha512-1TxZbB/q5DPeLhYzrc6y0OVekxnjM+CRH/Gu8nAXQMX46XjdTboXvBtvgKHV1aCznGNsWyjd5A2O5G/XGg9NNw== +"@abp/clipboard@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.2.0-rc.2.tgz#608bdb3d4a3ff1a7b38ad5850385746f7cfe6c0c" + integrity sha512-w/JiTmidNc7SZuvpkQumyNj6FTBq6hLnhXeBBh4/xega1Ob2q+tbrOSpemgpK6YOXUQbmH8a4vEPvwt7uzNfvQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" clipboard "^2.0.11" -"@abp/cms-kit.admin@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/cms-kit.admin/-/cms-kit.admin-9.1.0.tgz#1563e47e83f816cded5af2f608a8958f68ac7fff" - integrity sha512-utsJXJ0GmR5SKQTSBSfcs/Uqvo6nleLa1LF6uOaiJa2nsck9gD1XcVX3GMi54ZoQpjAXFvXtwh5P6gzPfBNroQ== +"@abp/cms-kit.admin@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/cms-kit.admin/-/cms-kit.admin-9.2.0-rc.2.tgz#10f34b0bf826f1f402a8e86527a4631107ac62f7" + integrity sha512-LkEC7JJPs0nJrCRJJgNYKnUNBXfRYixORmVRnSa+hm+Wi3hqm1sRNQr86rUTOqlw90s9LtrVcBCnDkGmb0/pdg== dependencies: - "@abp/codemirror" "~9.1.0" - "@abp/jstree" "~9.1.0" - "@abp/markdown-it" "~9.1.0" - "@abp/slugify" "~9.1.0" - "@abp/tui-editor" "~9.1.0" - "@abp/uppy" "~9.1.0" + "@abp/codemirror" "~9.2.0-rc.2" + "@abp/jstree" "~9.2.0-rc.2" + "@abp/markdown-it" "~9.2.0-rc.2" + "@abp/slugify" "~9.2.0-rc.2" + "@abp/tui-editor" "~9.2.0-rc.2" + "@abp/uppy" "~9.2.0-rc.2" -"@abp/cms-kit.public@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/cms-kit.public/-/cms-kit.public-9.1.0.tgz#1d7dc0527e84e4da7afee217d33a69b1ca6fc76e" - integrity sha512-sVB4X73zyjM8TeJj4XdV0XQtxodEUvmXz3I+PoPXKIj0pzOHZ1XhyQjl3yrxkDnk4fcx2jfRoBEvPYAK796nXg== +"@abp/cms-kit.public@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/cms-kit.public/-/cms-kit.public-9.2.0-rc.2.tgz#f2ba7951a33e52224d28a78921ef882a506d5611" + integrity sha512-R5se5hJrmbQx8zbgx9dDGnH0e1CatPLSLb7miVt7rdfyT6jD0bWLBKwtSSRws3jBhNWu8neKVokJubPRoWuz1A== dependencies: - "@abp/highlight.js" "~9.1.0" - "@abp/star-rating-svg" "~9.1.0" + "@abp/highlight.js" "~9.2.0-rc.2" + "@abp/star-rating-svg" "~9.2.0-rc.2" -"@abp/cms-kit@9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/cms-kit/-/cms-kit-9.1.0.tgz#f3498a2e0213163abd65d62c1404a1c76ad9db04" - integrity sha512-aUTNq5qAlP7zUHmhv8uYb1S6bzgqRUvcgoNpArhojG5URvBjrKE4a65YFZgoC7Bb7q+Ljx6c5nZ7ehylZpiz2g== +"@abp/cms-kit@9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/cms-kit/-/cms-kit-9.2.0-rc.2.tgz#73acef605cc400e33378b7ae56808a26eb5ac1b9" + integrity sha512-0TLLsLfDuLznpyWjEGUxHmtvW88eVmKahsqcym1RhotUYr6p8qxZikh2prvqqL9UuXEuY4RDhM7i9fyw3PhS/A== dependencies: - "@abp/cms-kit.admin" "~9.1.0" - "@abp/cms-kit.public" "~9.1.0" + "@abp/cms-kit.admin" "~9.2.0-rc.2" + "@abp/cms-kit.public" "~9.2.0-rc.2" -"@abp/codemirror@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/codemirror/-/codemirror-9.1.0.tgz#66cb3bde616e580fba06dbfdb0f84b48eb5e9ce3" - integrity sha512-tfUnfqYDUlNfs96wVvilF1K+GmvDUsXIK465qHUux9fHcA1muN0IRnfTZKtmeAkiAKgvxl/WkbfpnvjBKQZfbw== +"@abp/codemirror@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/codemirror/-/codemirror-9.2.0-rc.2.tgz#185bf41b3aa9dc11a344e7d360f7d5accc045c5e" + integrity sha512-jaLvLRTUfIC6c93ND0rfVZJTuE1CrsSP30wZIXoEt9TgmNmEfnirJiDBDNupAg7mIwBvVjoEVr3k+KspPQOmZA== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" codemirror "^5.65.1" -"@abp/core@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.1.0.tgz#af78fc096932d4ec45216b5e910c87a72e479b1f" - integrity sha512-6P0KGh5+IqlhQnUOyzaGaKrKzOAm1G0gg8Ay3DXyW2ksY6Zr+lyi5cxNsSi4ix1dicqfydIKvLfV+DF3mQf/dw== +"@abp/core@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.2.0-rc.2.tgz#0eab20842f8c0801d1dca036d3d66a67e155e019" + integrity sha512-8bC1MAV4iSFLJvRvM5mvrI9+N5FPAjaG79hr3p0xJtNixFOFpNELDf4p0zwy97zY8juL+AIEyJAkg+KIIK90DQ== dependencies: - "@abp/utils" "~9.1.0" + "@abp/utils" "~9.2.0-rc.2" -"@abp/datatables.net-bs5@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.1.0.tgz#3b2e7d68e947db3d3432ab5e8bff4da89a85d05d" - integrity sha512-zBrzOrfvTPJcc6mvHZDPVdXbWesKU0lKlrb7vuBZxWjrFNwVC0d/KNJPcYVvyQ3zf9rrQB5Xg0MDgu0k5Tt7tQ== +"@abp/datatables.net-bs5@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.2.0-rc.2.tgz#8c07544cafd2d2257f45150d3265c25eafc0ec0f" + integrity sha512-l3VOtc0fleUsx+ZUMr49+nPBWRBUVw4Z26ogT7O7uRR8doeerNU1GfA4yFJCUp02WIwt8cmZ+apJHADykn9Vng== dependencies: - "@abp/datatables.net" "~9.1.0" + "@abp/datatables.net" "~9.2.0-rc.2" datatables.net-bs5 "^2.1.8" -"@abp/datatables.net@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.1.0.tgz#12d6e1412aac4a2e0a61437dcdfbfbe1add7fed7" - integrity sha512-lYRvEZUkntaI+dS9Xkz23Z54eDeZ1R4Wzhqcg6VIsz3iO5kYAa5GiQjcOiBssNGJSLuaet6R5Pqh1ThEUjfvvA== +"@abp/datatables.net@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.2.0-rc.2.tgz#2880ccb61750faa1689aec15f9e49d92ab1e94a1" + integrity sha512-nljq0p8mxoBvMZngtViInyQJqX/MI7EyQeCIYNICF2LO42RRZfpKaRy5zX+4Uc3z/wz209CfK8QfRNH/d1HtBQ== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" datatables.net "^2.1.8" -"@abp/font-awesome@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.1.0.tgz#af21ff2d7c332deb1df626404b53a78d790ba842" - integrity sha512-HcJzDCACGejOIvhxsorCdaesyDHnupR/sl5YFCsGikSqATyOSKQy4pw8wKsb1z+FCT5JYCTR7ROJ3lB1lTEgZw== +"@abp/font-awesome@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.2.0-rc.2.tgz#51eb750f5697668db538ad68cc6b9279517da537" + integrity sha512-X9JfF4n4MDZLszwZHIHuxSdGGxImEzV91ROKqNeclErCx3bOXyq/l9C+haZl/3TLzTAOSDMLgHGrT3MUCVPF5w== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" "@fortawesome/fontawesome-free" "^6.6.0" -"@abp/highlight.js@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-9.1.0.tgz#baed72c6ca38fd888b49174e632788c681306dce" - integrity sha512-kV6BFYESIIOJOJ1I9I3xUdLftrIkK8vZlIaBupJobxzNeOXh4/VRIVJE75xRXyMSmJRSMOZm8pjNoQsuKDgT3w== +"@abp/highlight.js@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-9.2.0-rc.2.tgz#9865cae50e39cca9ddd0c0390da794778ed36779" + integrity sha512-43NEl2omc8HWJjprEJpcMtb88QCD9bTCXrW7e2CO8ReyFuEoQh+79c0q2E5xJ5aitZpFhq4j4OOxxzUvAKORKw== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" "@highlightjs/cdn-assets" "~11.10.0" -"@abp/jquery-form@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.1.0.tgz#bdb96436c54b2a09ac4259db97e1e577410e1fc3" - integrity sha512-xjHXWstVY6B3MPB4/8g0EJeHlVvlV010OMZKFmfCGLFi6aHAEKqqWh1rMItAiVuSmCCzL0m/208ZkpoCI++jGQ== +"@abp/jquery-form@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.2.0-rc.2.tgz#58ce31a32bdfb355e5d4e3f97cb4761f594aa8e1" + integrity sha512-xN2xjK1RWKygwaqzTaWmpMlU0kGWGa2nKaudM6g9k/B6b1Pfdr8ykI4DaSuPb4BkRmurGkgy4r9o7IUoqv0T8Q== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-form "^4.3.0" -"@abp/jquery-validation-unobtrusive@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.1.0.tgz#798551e7c14ba286a819a48804107a53e974c1ee" - integrity sha512-TggKckfbLtB7Ig2kNt2kH//GaJUns8OZbpdKdh7vCWIAGedw2FLNCABCpoGnyNp0s/dVOtdyxRl9eFRN3+a4tw== +"@abp/jquery-validation-unobtrusive@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.2.0-rc.2.tgz#4e624e22cbf149d1c78da46aabaf6fc18d3b65ff" + integrity sha512-adKJxnWnhCrIYWoVdo41zgO5p4SjyypCxoHeQ7mkohry2WN6WuQG5nmHqhpdevw966DENvmVzFfz7vnO72vtrg== dependencies: - "@abp/jquery-validation" "~9.1.0" + "@abp/jquery-validation" "~9.2.0-rc.2" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.1.0.tgz#b771652377fbfb3c92ce03b7b7c069a3bb2ae70b" - integrity sha512-bmPSOHcxjitB8NkiLv0SxxuSS3FlCq9SiTUGvsT8nOStDsm+KpyYSzZQq8LSCyqWWj54tlOARTfcD4tMK7/K3Q== +"@abp/jquery-validation@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.2.0-rc.2.tgz#b6a8124d5056ddad515733bffc78703f6bd422be" + integrity sha512-4j1fg1qImif2vFZQFJ7z1olEUnjc3BByKS+7joNQ+96/NqOQdOQjRjB2n490od7ExCinKnvYx3JFttpYjHU3Xw== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-validation "^1.21.0" -"@abp/jquery@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.1.0.tgz#952b61ea3e4a99e6551f667297dbb637d83f3c50" - integrity sha512-Ulw8ClAbqmxceNYCb76bR6dq7e9b7Hs6mjlEy05zdbsSNuZW3UoycsJ+BquXi+dUNBo6OppqEmhVeiIjAKTaFg== +"@abp/jquery@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.2.0-rc.2.tgz#335643787ab282d5d2fee4f360c2b62cce3db962" + integrity sha512-baKNDuiQMzkV0LFzBwEqS0JYtXIUe5KH+MVizGR6Zv10B8l/gIrUzWI/nnqDtbts6gmY9OBDXkCTcgMQBwKqjw== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" jquery "~3.7.1" -"@abp/jstree@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jstree/-/jstree-9.1.0.tgz#1761e31b37cce010401f8812c7e1a3a56dbd3e8e" - integrity sha512-d3gX+7SuRcP9RR4R09gjGrk5ghmC6mQeuvEMVvQjMCruH92PRo/GZ7pqubanAyN45D8ct8BXAw1DKV9Yu7P67Q== +"@abp/jstree@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jstree/-/jstree-9.2.0-rc.2.tgz#65c9a263d51f93ec7a1935a8761b51ca9526b7c4" + integrity sha512-fhpiykHROkYx+CPVCnndXzrRkEuLwa2QlFCvBVn1A6FG/f+Z3luRqVJ7eF/hPVyMoAvsG/byRkpBEAg4B+8XNg== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jstree "^3.3.17" -"@abp/lodash@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.1.0.tgz#62c784bd99c9e1ccfa82905e843375c594ef8584" - integrity sha512-cCE3ZwwWcPrh+ccKx4AS1USL1ttbrXBqci/l+ROjXhFzrs6GYnhI0+30xRZgPdPRm3U3nGPrSaDAU7UuHkRScg== +"@abp/lodash@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.2.0-rc.2.tgz#521bccafcc09780c751932688b6ec45c368c2854" + integrity sha512-T6eYsDovUl4AYD9n2N4WZq9MeC4/ryke5ctvdhzuArqM7WpPvohq3VKGugrP/MLo9oD3MZ5DVQvMJ0AVg+IXNQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" lodash "^4.17.21" -"@abp/luxon@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.1.0.tgz#b246f3627bbc7ce41b7878fce1679da473b0e0ae" - integrity sha512-AouS/qEK+i69hwr4XtUOCepONLuR+1gdHFb50RpOEbUoDfTYmYpxPBdiICmZPeJ3CExErITPZxzzngCnKPPOUw== +"@abp/luxon@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.2.0-rc.2.tgz#da366d335f9575b104ea2206e076af35cbbfcfba" + integrity sha512-mr8ipYrJDDJPoImIwORK7Z6d1C07APl95L17ufRbig4sjdxjNEDR5uUXVviVafF1No2bVkYiS0xGKQmojA2twg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" luxon "^3.5.0" -"@abp/malihu-custom-scrollbar-plugin@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.1.0.tgz#288ae1916a78100f2c728dfd05a560ee99fafae4" - integrity sha512-McdMRDtbTZBjp//3GMVjRjkc16HNYDEdPc2ar1xrY817e0tEln6/TBkItAcfOvjso0TsLcvlNL1+1bjbMjj8OA== +"@abp/malihu-custom-scrollbar-plugin@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.2.0-rc.2.tgz#cb79018adf08276041eb5c5dcf75130c8995686b" + integrity sha512-jdHXx7kPduaEfFXDvf5s+QVl10RqshNbnZkkqPx0mMkhz2H/qxwzF1e0mcLFL38nEgs+0gxIkcA8/MKq85tyqQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/markdown-it@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/markdown-it/-/markdown-it-9.1.0.tgz#9d40057871ee15657d5e6e65b12a958cce6ed2c8" - integrity sha512-Vv25TO4b8q3+jaiZ1XC134kNUWoPl1aedh3NeIQU11NfrHM5tFO7gDLBXXPwM+Ahoptg25/WYGDcUnnx2WLpFg== +"@abp/markdown-it@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/markdown-it/-/markdown-it-9.2.0-rc.2.tgz#a91f28718e8b2fde0494c24e9af5f1acfcfd1fca" + integrity sha512-PsiOjpXYf8WGXVZUOQWES+GHHg7lEmVN6P9GHXiezDppeAks11mJSGbYIz6LcPr9YObZyfQY3kBTeBGBZQapQw== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" markdown-it "^14.1.0" -"@abp/moment@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.1.0.tgz#f3c0ae02209e496913cda53df752dabc5be648bd" - integrity sha512-WxR1l09vwxRhqyvAVP4m4IBOFjIC4pzLOmelloqES+gL/klw+T/AmY21Z/z+OV0p7aJ3V3Q1aQStuMCDCyGFnQ== +"@abp/moment@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.2.0-rc.2.tgz#f4339138668ca72be1d700624b91789344557bff" + integrity sha512-J4ZaCDEcXLGmJ+8zqgaMK9Lutnjga28LSbuiFaGLlZ1QgYZ5HDScE128o3vX9qr60tc6IIY0cDkHi4bBHo7ozw== dependencies: moment "^2.30.1" -"@abp/prismjs@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.1.0.tgz#40b688c82ae808ef92516ea5731ccb38c19d2384" - integrity sha512-trSW1K6VtIEaAyxe7umQGimCTLrgdT6DwSiQ8PONvKa8WR5ZmzSDU7lK8SliorufkbWtdkjCvuXB81NGt3tAhQ== +"@abp/prismjs@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.2.0-rc.2.tgz#1ee12acdd9e1dcc9ae2ce4a9f21f159c3f9e3dec" + integrity sha512-GuKz6bujBlYHAgLhW1RT7aEOwKyZ+vnyMIhN0/fQ34+nbqg0PC4htwJcN6spgN8gZKD06lCqBH+GjLE2WtE/Rw== dependencies: - "@abp/clipboard" "~9.1.0" - "@abp/core" "~9.1.0" + "@abp/clipboard" "~9.2.0-rc.2" + "@abp/core" "~9.2.0-rc.2" prismjs "^1.29.0" -"@abp/select2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.1.0.tgz#9c2c6751f8506fb375c9b7c42a008c168a8f4c75" - integrity sha512-n6HqsH8aFLMrqL6LJ0gsHFlaq6T1QSt/x7HkkZyhDC7CwhXajxtsJEstDnDF9naHSXX/lerPbrHEgzn2QRMBqg== +"@abp/select2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.2.0-rc.2.tgz#ba6e839644b7ecdd198fe5286101302a3dc79ce3" + integrity sha512-AQMXb5ATgbN7grpKU8lB2xFFfirCe45lIvd9krW5SwUHCYZP7pRPbwQXAew+BqlIIYJJF2GA7/Nf//e/71bKtg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" select2 "^4.0.13" -"@abp/slugify@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/slugify/-/slugify-9.1.0.tgz#e7a7b1d6f8a4a84f6cd70c72cc9da1c12a19257f" - integrity sha512-fve6FIbOkjKpQXg/i/Kel6B6rVo2/nYdzWDWwUzGj92ZcIhh3B1inLHXL54336+vjjXbb17SNrsNKRf8qKs9ow== +"@abp/slugify@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/slugify/-/slugify-9.2.0-rc.2.tgz#659a16bdf9d27d712442c6989d0fa764e6ab5d49" + integrity sha512-8YT9Dn1WDPDVPQsy+kg0rbMP2zIW94zBzs3U8xj4JWRARTz/8qP3YciXh/p7/ncJJwvyz3OvL2QHhj9xWlTHVA== dependencies: slugify "^1.6.6" -"@abp/star-rating-svg@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/star-rating-svg/-/star-rating-svg-9.1.0.tgz#5abeb17f487e737caf92cd1ef76930d376dcc60f" - integrity sha512-Ob0PrXtkrYgjCfB6pxvpCQ/ID3x4j5TxJbEa9wh14KqakmKsO/2hp17CZ1XjMgZsLJGL4p3qNZZ3WZIqbwZDJQ== +"@abp/star-rating-svg@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/star-rating-svg/-/star-rating-svg-9.2.0-rc.2.tgz#ae79e454c07910df4f345dbeea9da6ebc774b1f1" + integrity sha512-1psK0ZvbNvSLMxNKInJgg7fEgn4/wg8xr5Mytup+xHZQ9d1GprjIoMENRlmiu6BgfEr+du/QjuyAP1B8Y4fZmw== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" star-rating-svg "^3.5.0" -"@abp/sweetalert2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.1.0.tgz#660f9b1db60cc04bde6cadbcf3c55320d20c2f3e" - integrity sha512-z3JMeAwva2vR8qZSRJ/BisQDdAEqjDJ/6oIGu/JYXQEchO1Lnd9UMV376nc3crLJdnyRRO7Qs6zx4FfVOCH0kw== +"@abp/sweetalert2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.2.0-rc.2.tgz#075634dc3ec996607757694d636d276ba2350e06" + integrity sha512-ZgHgqM8Q+V9ECQyAyepM5KpshKpQP463xRHwgY45oR7/CfCcO/vS56YQgJNg2zE6+lRQZaRQ9OL7lph8975vVg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" sweetalert2 "^11.14.1" -"@abp/timeago@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.1.0.tgz#496e40153241bae5d418d79cc6c65c9db6306447" - integrity sha512-a/UAYQ2GrhYFGx4p0hqr3x+teBqnjnOiWJEPq9//RTlz+EZ+OUyHbJrcUtqZRvaGJtOAsfmljD3jYnOXUlU7DA== +"@abp/timeago@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.2.0-rc.2.tgz#25324732966438bd6f505bdd932511ba2cc8c22b" + integrity sha512-WfWxxiycg77vVN2hPHqUOu552la+zxr0EZnHMqnEnnBe+gFYe9C0CpdYuyBq2eQXRXHoVzsB+4YRVNk5sbSGng== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" timeago "^1.6.7" -"@abp/toastr@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.1.0.tgz#501cbe211164b2e7416a2d4878cb34aed3550b7c" - integrity sha512-BkTDuU0p9Qa4+HT9CYwiPQjVTrbTSNBgQ18h69S3lIadLJwdC6pXeLbL9bhe+5fiuhq2f+0Oqo3X75LNEgzHkw== +"@abp/tui-editor@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/tui-editor/-/tui-editor-9.2.0-rc.2.tgz#8f755bfb7413d1636fe904d409efe6363f48516b" + integrity sha512-Ap2JOZShRvbHI4+5f1pGRgP26i6bel+2eBT1w33UWMclyNGhNYVcm9VNIyLaS9KbGM5bphij2bMdnTUpjQ10Dw== dependencies: - "@abp/jquery" "~9.1.0" - toastr "^2.1.4" + "@abp/jquery" "~9.2.0-rc.2" + "@abp/prismjs" "~9.2.0-rc.2" -"@abp/tui-editor@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/tui-editor/-/tui-editor-9.1.0.tgz#cc4106f0ea61ad1ee2497ecfec1eefb62189f57f" - integrity sha512-Ttmzn881+knyFst/5BZP+4vb4R+gx5loqWtbuQXthO2saisr/+QJX4EzqwYqPTVl81Y1HZwhcdTr/hMHOCMFpg== +"@abp/uppy@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/uppy/-/uppy-9.2.0-rc.2.tgz#27672c7947410b0c0b57225cbf7c436268dcd05b" + integrity sha512-CfsHC54V0iDNr3urc5TctW8ZBuy2xCaTyqvA+7WmdSeGfEkB+aDaWekQF+xGKqZvsx/mWgz69AZHLHF4sRi4yQ== dependencies: - "@abp/jquery" "~9.1.0" - "@abp/prismjs" "~9.1.0" - -"@abp/uppy@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/uppy/-/uppy-9.1.0.tgz#fdd758f33437754b7a8e73f5a7bf8023dfbdfc5d" - integrity sha512-JlgibBIvNF3KjqjnjNaluQx2sZKAop/i6uxJ8XHW45fFth/vNYTStfsFRy0WkgcFQXOum95P3+RIlRaBU55DyA== - dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" uppy "^4.4.1" -"@abp/utils@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.1.0.tgz#a4fd37d943df18b5c11f5d4869efd5d8882b7959" - integrity sha512-+UZo0ocTEZlRO41SzzHNdinjdFUDVq8wYZSj5BA1bqCVeWYhfjI2+rl9DuogCN8UEuCACA4K/qb4XcE/untQ3g== +"@abp/utils@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.2.0-rc.2.tgz#669885ad1e3ac7b8e182d7c392ea1b633df65aac" + integrity sha512-rfQxhJy04Qf50P3ciasKbDlJ+9JidD60xW77TjcoUJvljJE+A8iwTX8Wwqz7+YcC7rks2ILM4y3Ay7/47NOICQ== dependencies: just-compare "^2.3.0" @@ -856,7 +847,7 @@ jquery-validation@>=1.19, jquery-validation@^1.21.0: resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.21.0.tgz#78fc05ab76020912a246af3661b3f54a438bca93" integrity sha512-xNot0rlUIgu7duMcQ5qb6MGkGL/Z1PQaRJQoZAURW9+a/2PGOUxY36o/WyNeP2T9R6jvWB8Z9lUVvvQWI/Zs5w== -jquery@>=1.10, jquery@>=1.12.0, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2, jquery@^3.5.0: +jquery@>=1.10, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2, jquery@^3.5.0: version "3.6.4" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.4.tgz#ba065c188142100be4833699852bf7c24dc0252f" integrity sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ== @@ -1124,13 +1115,6 @@ tiny-emitter@^2.0.0: resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== -toastr@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/toastr/-/toastr-2.1.4.tgz#8b43be64fb9d0c414871446f2db8e8ca4e95f181" - integrity sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA== - dependencies: - jquery ">=1.12.0" - tus-js-client@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/tus-js-client/-/tus-js-client-4.2.3.tgz#a72b44e93bdf1961085d644b2717e232f1ee1b57" diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo.CmsKit.Admin.Application.abppkg.analyze.json b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo.CmsKit.Admin.Application.abppkg.analyze.json index cb49ddb813..fed7298487 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo.CmsKit.Admin.Application.abppkg.analyze.json +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo.CmsKit.Admin.Application.abppkg.analyze.json @@ -803,6 +803,23 @@ "isOptional": false } ] + }, + { + "returnType": "ListResultDto", + "namespace": "Volo.CmsKit.Admin.Menus", + "name": "GetPermissionLookupAsync", + "summary": null, + "isAsync": true, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ + { + "type": "PermissionLookupInputDto", + "name": "inputDto", + "isOptional": false + } + ] } ], "contentType": "applicationService", @@ -1328,6 +1345,17 @@ } ] }, + { + "returnType": "ListResultDto", + "namespace": "Volo.CmsKit.Admin.Blogs", + "name": "GetAllListAsync", + "summary": null, + "isAsync": true, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [] + }, { "returnType": "BlogDto", "namespace": "Volo.CmsKit.Admin.Blogs", @@ -1367,6 +1395,28 @@ } ] }, + { + "returnType": "Void", + "namespace": "Volo.CmsKit.Admin.Blogs", + "name": "MoveAllBlogPostsAsync", + "summary": null, + "isAsync": true, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ + { + "type": "Guid", + "name": "blogId", + "isOptional": false + }, + { + "type": "Nullable", + "name": "assignToBlogId", + "isOptional": false + } + ] + }, { "returnType": "Void", "namespace": "Volo.CmsKit.Admin.Blogs", diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Approve/Index.cshtml b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Approve/Index.cshtml index d15396f33d..788ae3f682 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Approve/Index.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Approve/Index.cshtml @@ -35,7 +35,7 @@ -
+
diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Approve/index.js b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Approve/index.js index f8aa2ba80d..587e7f77ec 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Approve/index.js +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Approve/index.js @@ -6,15 +6,8 @@ moment()._locale.preparse = (string) => string; moment()._locale.postformat = (string) => string; - var getFormattedDate = function ($datePicker) { - if (!$datePicker.val()) { - return null; - } - var momentDate = moment($datePicker.val(), $datePicker.data('daterangepicker').locale.format); - return momentDate.isValid() ? momentDate.toISOString() : null; - }; - - $('.singledatepicker').daterangepicker({ + var singleDatePicker = $('#CmsKitCommentsApproveWrapper .singledatepicker'); + singleDatePicker.daterangepicker({ "singleDatePicker": true, "showDropdowns": true, "autoUpdateInput": false, @@ -23,22 +16,19 @@ "drops": "auto" }); - $('.singledatepicker').attr('autocomplete', 'off'); + singleDatePicker.attr('autocomplete', 'off'); - $('.singledatepicker').on('apply.daterangepicker', function (ev, picker) { + singleDatePicker.on('apply.daterangepicker', function (ev, picker) { $(this).val(picker.startDate.format('l')); }); var filterForm = $('#CmsKitCommentsFilterForm'); var getFilter = function () { - var filterObj = filterForm.serializeFormToObject(); - - filterObj.creationStartDate = getFormattedDate($('#CreationStartDate')); - filterObj.creationEndDate = getFormattedDate($('#CreationEndDate')); - filterObj.commentApproveState = "Waiting"; - - return filterObj; + filterForm.handleDatepicker('.singledatepicker'); + var formObject = filterForm.serializeFormToObject(); + formObject.commentApproveState = "Waiting"; + return formObject; }; var _dataTable = $('#CommentsTable').DataTable(abp.libs.datatables.normalizeConfiguration({ diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Details.cshtml b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Details.cshtml index a73475bac2..a9d064235f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Details.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Details.cshtml @@ -33,7 +33,7 @@ } -
+
diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/details.js b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/details.js index 2d007b0983..33f0b9211c 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/details.js +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/details.js @@ -8,15 +8,12 @@ $(function (){ if (commentRequireApprovement) { $('#IsApprovedSelectInput').show(); } - - var getFormattedDate = function ($datePicker) { - return $datePicker.data('date'); - }; - + moment.localeData().preparse = (s)=>s; moment.localeData().postformat = (s)=>s; - - $('.singledatepicker').daterangepicker({ + + var singleDatePicker = $('#CmsKitCommentsDetailsWrapper .singledatepicker'); + singleDatePicker.daterangepicker({ "singleDatePicker": true, "showDropdowns": true, "autoUpdateInput": false, @@ -27,9 +24,9 @@ $(function (){ "maxYear": 2199, }); - $('.singledatepicker').attr('autocomplete', 'off'); + singleDatePicker.attr('autocomplete', 'off'); - $('.singledatepicker').on('apply.daterangepicker', function (ev, picker) { + singleDatePicker.on('apply.daterangepicker', function (ev, picker) { $(this).val(picker.startDate.format('l')); $(this).data('date', picker.startDate.locale('en').format('YYYY-MM-DD')); }); @@ -37,12 +34,9 @@ $(function (){ var filterForm = $('#CmsKitCommentsFilterForm'); var getFilter = function () { - var filterObj = filterForm.serializeFormToObject(); - - filterObj.creationStartDate = getFormattedDate($('#creationStartDate')); - filterObj.creationEndDate = getFormattedDate($('#creationEndDate')); - - return filterObj; + filterForm.handleDatepicker('.singledatepicker'); + var formObject = filterForm.serializeFormToObject(); + return formObject; }; var _dataTable = $('#CommentsTable').DataTable(abp.libs.datatables.normalizeConfiguration({ diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/index.js b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/index.js index be652ce552..ad0b8049f6 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/index.js +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/index.js @@ -2,7 +2,6 @@ $(function () { var l = abp.localization.getResource("CmsKit"); var commentsService = volo.cmsKit.admin.comments.commentAdmin; - moment()._locale.preparse = (string) => string; moment()._locale.postformat = (string) => string; @@ -13,15 +12,8 @@ $(function () { $('#IsApprovedSelectInput').show(); } - var getFormattedDate = function ($datePicker) { - if (!$datePicker.val()) { - return null; - } - var momentDate = moment($datePicker.val(), $datePicker.data('daterangepicker').locale.format); - return momentDate.isValid() ? momentDate.toISOString() : null; - }; - - $('.singledatepicker').daterangepicker({ + var singleDatePicker = $('#CmsKitCommentsWrapper .singledatepicker'); + singleDatePicker.daterangepicker({ "singleDatePicker": true, "showDropdowns": true, "autoUpdateInput": false, @@ -30,21 +22,18 @@ $(function () { "drops": "auto" }); - $('.singledatepicker').attr('autocomplete', 'off'); + singleDatePicker.attr('autocomplete', 'off'); - $('.singledatepicker').on('apply.daterangepicker', function (ev, picker) { + singleDatePicker.on('apply.daterangepicker', function (ev, picker) { $(this).val(picker.startDate.format('l')); }); var filterForm = $('#CmsKitCommentsFilterForm'); var getFilter = function () { - var filterObj = filterForm.serializeFormToObject(); - - filterObj.creationStartDate = getFormattedDate($('#CreationStartDate')); - filterObj.creationEndDate = getFormattedDate($('#CreationEndDate')); - - return filterObj; + filterForm.handleDatepicker('.singledatepicker'); + var formObject = filterForm.serializeFormToObject(); + return formObject; }; var _dataTable = $('#CommentsTable').DataTable(abp.libs.datatables.normalizeConfiguration({ diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.abppkg.analyze.json b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.abppkg.analyze.json index b42079c218..a673b4c6a1 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.abppkg.analyze.json +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.abppkg.analyze.json @@ -159,6 +159,12 @@ "name": "CreateModalModel", "summary": null }, + { + "namespace": "Volo.CmsKit.Admin.Web.Pages.CmsKit.Blogs", + "contentType": "webPage", + "name": "DeleteBlogModal", + "summary": null + }, { "namespace": "Volo.CmsKit.Admin.Web.Pages.CmsKit.Blogs", "contentType": "webPage", diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.abppkg.analyze.json b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.abppkg.analyze.json index 3b127368a9..dc08dab7db 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.abppkg.analyze.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.abppkg.analyze.json @@ -1085,6 +1085,11 @@ "type": "System.Nullable`1[System.Guid]", "name": "TenantId", "summary": null + }, + { + "type": "System.String", + "name": "RequiredPermissionName", + "summary": null } ], "contentType": "aggregateRoot", @@ -2129,6 +2134,23 @@ } ] }, + { + "returnType": "Void", + "namespace": "Volo.CmsKit.Blogs", + "name": "SetBlogId", + "summary": null, + "isAsync": false, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ + { + "type": "Guid", + "name": "blogId", + "isOptional": false + } + ] + }, { "returnType": "Void", "namespace": "Volo.CmsKit.Blogs", @@ -3083,6 +3105,38 @@ "isOptional": false } ] + }, + { + "returnType": "List", + "namespace": "Volo.CmsKit.MarkedItems", + "name": "GetEntityIdsFilteredByUserAsync", + "summary": null, + "isAsync": true, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ + { + "type": "Guid", + "name": "userId", + "isOptional": false + }, + { + "type": "String", + "name": "entityType", + "isOptional": false + }, + { + "type": "Nullable", + "name": "tenantId", + "isOptional": true + }, + { + "type": "CancellationToken", + "name": "cancellationToken", + "isOptional": true + } + ] } ], "implementingInterfaces": [ @@ -3532,6 +3586,23 @@ "isOptional": false } ] + }, + { + "returnType": "Void", + "namespace": "Volo.CmsKit.Blogs", + "name": "DeleteAsync", + "summary": null, + "isAsync": true, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ + { + "type": "Guid", + "name": "blogId", + "isOptional": false + } + ] } ], "implementingInterfaces": [ @@ -5026,6 +5097,33 @@ "isOptional": true } ] + }, + { + "returnType": "Void", + "namespace": "Volo.CmsKit.Comments", + "name": "DeleteByEntityTypeAndIdAsync", + "summary": null, + "isAsync": true, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ + { + "type": "String", + "name": "entityType", + "isOptional": false + }, + { + "type": "String", + "name": "entityId", + "isOptional": false + }, + { + "type": "CancellationToken", + "name": "cancellationToken", + "isOptional": true + } + ] } ], "contentType": "repositoryInterface", @@ -5229,6 +5327,11 @@ "name": "tagId", "isOptional": true }, + { + "type": "Nullable", + "name": "favoriteUserId", + "isOptional": true + }, { "type": "Nullable", "name": "statusFilter", @@ -5271,6 +5374,11 @@ "name": "tagId", "isOptional": true }, + { + "type": "Nullable", + "name": "favoriteUserId", + "isOptional": true + }, { "type": "Nullable", "name": "statusFilter", @@ -5449,6 +5557,55 @@ "isOptional": true } ] + }, + { + "returnType": "Void", + "namespace": "Volo.CmsKit.Blogs", + "name": "UpdateBlogAsync", + "summary": null, + "isAsync": true, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ + { + "type": "Guid", + "name": "sourceBlogId", + "isOptional": false + }, + { + "type": "Nullable", + "name": "targetBlogId", + "isOptional": true + }, + { + "type": "CancellationToken", + "name": "cancellationToken", + "isOptional": true + } + ] + }, + { + "returnType": "Void", + "namespace": "Volo.CmsKit.Blogs", + "name": "DeleteByBlogIdAsync", + "summary": null, + "isAsync": true, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ + { + "type": "Guid", + "name": "blogId", + "isOptional": false + }, + { + "type": "CancellationToken", + "name": "cancellationToken", + "isOptional": true + } + ] } ], "contentType": "repositoryInterface", @@ -5535,6 +5692,43 @@ } ] }, + { + "returnType": "List", + "namespace": "Volo.CmsKit.Blogs", + "name": "GetListWithBlogPostCountAsync", + "summary": null, + "isAsync": true, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ + { + "type": "String", + "name": "filter", + "isOptional": true + }, + { + "type": "String", + "name": "sorting", + "isOptional": true + }, + { + "type": "Int32", + "name": "maxResultCount", + "isOptional": true + }, + { + "type": "Int32", + "name": "skipCount", + "isOptional": true + }, + { + "type": "CancellationToken", + "name": "cancellationToken", + "isOptional": true + } + ] + }, { "returnType": "Int64", "namespace": "Volo.CmsKit.Blogs", diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs index fd2984a3b4..8132202717 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs @@ -79,11 +79,6 @@ public class CmsKitPublicWebModule : AbpModule options.DisableModule(CmsKitPublicRemoteServiceConsts.ModuleName); }); - Configure(options => - { - options.KeyPrefix = "CmsKit:"; - }); - if (GlobalFeatureManager.Instance.IsEnabled()) { Configure(options => diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitHomePageRouteValueTransformer.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitHomePageRouteValueTransformer.cs index 3ac271938b..5ae76a364e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitHomePageRouteValueTransformer.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitHomePageRouteValueTransformer.cs @@ -1,27 +1,32 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; -using Volo.Abp.DependencyInjection; using Volo.Abp.Features; +using Volo.Abp.MultiTenancy; using Volo.CmsKit.Features; using Volo.CmsKit.Public.Pages; +using Volo.CmsKit.Public.Web.Pages.Public; namespace Volo.CmsKit.Public.Web.Pages; -public class CmsKitHomePageRouteValueTransformer : DynamicRouteValueTransformer, ITransientDependency +public class CmsKitHomePageRouteValueTransformer : CmsKitDynamicRouteValueTransformerBase { protected IFeatureChecker FeatureChecker { get; } protected IPagePublicAppService PagePublicAppService { get; } - public CmsKitHomePageRouteValueTransformer(IFeatureChecker featureChecker, IPagePublicAppService pagePublicAppService) + public CmsKitHomePageRouteValueTransformer( + ICurrentTenant currentTenant, + ITenantConfigurationProvider tenantConfigurationProvider, + IFeatureChecker featureChecker, + IPagePublicAppService pagePublicAppService) + : base(currentTenant, tenantConfigurationProvider) { FeatureChecker = featureChecker; PagePublicAppService = pagePublicAppService; } - public override async ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) + protected async override ValueTask DoTransformAsync(HttpContext httpContext, RouteValueDictionary values) { if (await FeatureChecker.IsEnabledAsync(CmsKitFeatures.PageEnable)) { diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitPageRouteValueTransformer.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitPageRouteValueTransformer.cs index 53f7fe50ba..996a449489 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitPageRouteValueTransformer.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitPageRouteValueTransformer.cs @@ -1,30 +1,36 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; using Volo.Abp.Caching; -using Volo.Abp.DependencyInjection; using Volo.Abp.Features; +using Volo.Abp.MultiTenancy; using Volo.CmsKit.Features; using Volo.CmsKit.Pages; using Volo.CmsKit.Public.Pages; +using Volo.CmsKit.Public.Web.Pages.Public; namespace Volo.CmsKit.Public.Web.Pages; -public class CmsKitPageRouteValueTransformer : DynamicRouteValueTransformer, ITransientDependency +public class CmsKitPageRouteValueTransformer : CmsKitDynamicRouteValueTransformerBase { protected IFeatureChecker FeatureChecker { get; } protected IPagePublicAppService PagePublicAppService { get; } protected IDistributedCache PageCache { get; } - public CmsKitPageRouteValueTransformer(IFeatureChecker featureChecker, IPagePublicAppService pagePublicAppService, IDistributedCache pageCache) + public CmsKitPageRouteValueTransformer( + ICurrentTenant currentTenant, + ITenantConfigurationProvider tenantConfigurationProvider, + IFeatureChecker featureChecker, + IPagePublicAppService pagePublicAppService, + IDistributedCache pageCache) + : base(currentTenant, tenantConfigurationProvider) { FeatureChecker = featureChecker; PagePublicAppService = pagePublicAppService; PageCache = pageCache; } - public async override ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) + protected async override ValueTask DoTransformAsync(HttpContext httpContext, RouteValueDictionary values) { if (values.TryGetValue("slug", out var slugParameter) && slugParameter is not null) { diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKitDynamicRouteValueTransformerBase.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKitDynamicRouteValueTransformerBase.cs new file mode 100644 index 0000000000..04fccd6473 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKitDynamicRouteValueTransformerBase.cs @@ -0,0 +1,41 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.Routing; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; + +namespace Volo.CmsKit.Public.Web.Pages.Public; + +public abstract class CmsKitDynamicRouteValueTransformerBase : DynamicRouteValueTransformer, ITransientDependency +{ + protected ICurrentTenant CurrentTenant { get; } + protected ITenantConfigurationProvider TenantConfigurationProvider { get; } + + protected CmsKitDynamicRouteValueTransformerBase(ICurrentTenant currentTenant, ITenantConfigurationProvider tenantConfigurationProvider) + { + CurrentTenant = currentTenant; + TenantConfigurationProvider = tenantConfigurationProvider; + } + + public async override ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) + { + TenantConfiguration tenant = null; + try + { + tenant = await TenantConfigurationProvider.GetAsync(saveResolveResult: false); + } + catch (Exception) + { + //Ignore + } + + using (CurrentTenant.Change(tenant?.Id, tenant?.Name)) + { + return await DoTransformAsync(httpContext, values); + } + } + + protected abstract ValueTask DoTransformAsync(HttpContext httpContext, RouteValueDictionary values); +} diff --git a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/Volo.CmsKit.MongoDB.Tests.csproj b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/Volo.CmsKit.MongoDB.Tests.csproj index 9c8dc44660..a537cf8943 100644 --- a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/Volo.CmsKit.MongoDB.Tests.csproj +++ b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/Volo.CmsKit.MongoDB.Tests.csproj @@ -8,9 +8,9 @@ - - - + + + diff --git a/modules/docs/Volo.Docs.abpmdl b/modules/docs/Volo.Docs.abpmdl index 17b5d6fab9..77c70bddf8 100644 --- a/modules/docs/Volo.Docs.abpmdl +++ b/modules/docs/Volo.Docs.abpmdl @@ -7,18 +7,6 @@ } }, "packages": { - "VoloDocs.Web": { - "path": "app/VoloDocs.Web/VoloDocs.Web.abppkg", - "folder": "app" - }, - "VoloDocs.EntityFrameworkCore": { - "path": "app/VoloDocs.EntityFrameworkCore/VoloDocs.EntityFrameworkCore.abppkg", - "folder": "app" - }, - "VoloDocs.Migrator": { - "path": "app/VoloDocs.Migrator/VoloDocs.Migrator.abppkg", - "folder": "app" - }, "Volo.Docs.Web": { "path": "src/Volo.Docs.Web/Volo.Docs.Web.abppkg", "folder": "src" diff --git a/modules/docs/app/VoloDocs.Web/package.json b/modules/docs/app/VoloDocs.Web/package.json index a92078e700..21b2acc98f 100644 --- a/modules/docs/app/VoloDocs.Web/package.json +++ b/modules/docs/app/VoloDocs.Web/package.json @@ -3,7 +3,7 @@ "name": "volo.docstestapp", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0", - "@abp/docs": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2", + "@abp/docs": "~9.2.0-rc.2" } } diff --git a/modules/docs/app/VoloDocs.Web/yarn.lock b/modules/docs/app/VoloDocs.Web/yarn.lock index 87929e3738..bfc550db80 100644 --- a/modules/docs/app/VoloDocs.Web/yarn.lock +++ b/modules/docs/app/VoloDocs.Web/yarn.lock @@ -2,238 +2,229 @@ # yarn lockfile v1 -"@abp/anchor-js@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/anchor-js/-/anchor-js-9.1.0.tgz#432ab4106a4e0ae8f5f39462b4ddafdbc5d39fab" - integrity sha512-0XmODAzc9ZaAJXnKdPBpuofsa2W4yp2JKvdd0HFuJZTU36q0k7uA+j+JGHsT95Cd/DyHVuxSV00Ys/Cz4sqcqQ== +"@abp/anchor-js@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/anchor-js/-/anchor-js-9.2.0-rc.2.tgz#c5c932bc8b441a41f775ca125113817c0fa726d8" + integrity sha512-xQZjCFuW5XsjLsoEh2tOAHGBFWcFpv2c5LVdC6GS23eYA43wEg7Cg75Y7Su1INj8IMrIa0RWgxm5KSPAFXJ4eA== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" anchor-js "^5.0.0" -"@abp/aspnetcore.mvc.ui.theme.basic@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.1.0.tgz#e9e0321037c34b43145dfc7827c422fbf79f59ab" - integrity sha512-vLkyrdFXw0/MPyU2hzqGJJk7trmIlVMdflNIe/rF77UIRmJYHWaTmPt9i5PLN/O4s0jnE9kWlaaJ/VnnFWeGsQ== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~9.1.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.1.0.tgz#35a73580b9b67a33708f2cf897243b50c9ad7100" - integrity sha512-rggTYL9poOFICsOseDX22JNhD0KWH8Oxs0YH91TZjvm/F67cPemW99eBlJc/YdJ7bN6icRiebN8m81y9MSanyg== - dependencies: - "@abp/aspnetcore.mvc.ui" "~9.1.0" - "@abp/bootstrap" "~9.1.0" - "@abp/bootstrap-datepicker" "~9.1.0" - "@abp/bootstrap-daterangepicker" "~9.1.0" - "@abp/datatables.net-bs5" "~9.1.0" - "@abp/font-awesome" "~9.1.0" - "@abp/jquery-form" "~9.1.0" - "@abp/jquery-validation-unobtrusive" "~9.1.0" - "@abp/lodash" "~9.1.0" - "@abp/luxon" "~9.1.0" - "@abp/malihu-custom-scrollbar-plugin" "~9.1.0" - "@abp/moment" "~9.1.0" - "@abp/select2" "~9.1.0" - "@abp/sweetalert2" "~9.1.0" - "@abp/timeago" "~9.1.0" - "@abp/toastr" "~9.1.0" - -"@abp/aspnetcore.mvc.ui@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.1.0.tgz#38d058ab148a36ad4d63870e8a501ca339e4a70e" - integrity sha512-lUw1ij87KJp+Rj4cz8oSQkdRuoMq76l/wIAGwtXH+EOKusVpSJTOTioAV/tPKeKZ15ubE4bO5jWcQGxZkXgH3w== +"@abp/aspnetcore.mvc.ui.theme.basic@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.2.0-rc.2.tgz#e32c8bd046d7377ae3f6a703eac9069e10bcd600" + integrity sha512-oj4lGrzllgGrq7rI7gz1Xvq5TU+xT+isGVf4T8hF3INE/J2nIyK45HnkjDq5wpdM3P+HkdM1+RtWNrLSVu0Klg== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.2.0-rc.2.tgz#77562013b604d0193e361efa6801235179dc7b4f" + integrity sha512-5KcMGg6o6W5uwsujrjUARlZFgWmZeYBNCf1vkf3btakkwTzmPDbypUx6VrgBJbt4AUnYlMQbSHvbfK0d3I1e6w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~9.2.0-rc.2" + "@abp/bootstrap" "~9.2.0-rc.2" + "@abp/bootstrap-datepicker" "~9.2.0-rc.2" + "@abp/bootstrap-daterangepicker" "~9.2.0-rc.2" + "@abp/datatables.net-bs5" "~9.2.0-rc.2" + "@abp/font-awesome" "~9.2.0-rc.2" + "@abp/jquery-form" "~9.2.0-rc.2" + "@abp/jquery-validation-unobtrusive" "~9.2.0-rc.2" + "@abp/lodash" "~9.2.0-rc.2" + "@abp/luxon" "~9.2.0-rc.2" + "@abp/malihu-custom-scrollbar-plugin" "~9.2.0-rc.2" + "@abp/moment" "~9.2.0-rc.2" + "@abp/select2" "~9.2.0-rc.2" + "@abp/sweetalert2" "~9.2.0-rc.2" + "@abp/timeago" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.2.0-rc.2.tgz#e1c0e4b0280d5fe37659da8dc696150f0d461336" + integrity sha512-WG4WWGS1mSb2QWAVOA2nrvoi9/3oueEaKEG18xbPgWiLz5GCq8gsopKqUgW/fmBaw+1Jh9tsuCTH7eED/2tUxA== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.1.0.tgz#96a1875a2b92f03b8f90f7842ebf8849cf8a4d04" - integrity sha512-3FQm1SK8qmzoP7LOvXkZpq1AzrdPHKRdQJjoqK9N+lQxQjNfOxEFMxeDSg6WJKO619Cnve++jAyMidojXlCiwQ== +"@abp/bootstrap-datepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.2.0-rc.2.tgz#429c2d44a22f9190013aea0efa420854b7e83a4f" + integrity sha512-bxMpaxTgnF5/VELbKvtZVw0tI0bowzUQFVSBQJqO4o7wJmxyHNzJuaom8Is+tCsoMrpo8t9EGMGfpWHV1IsrjQ== dependencies: bootstrap-datepicker "^1.10.0" -"@abp/bootstrap-daterangepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.1.0.tgz#1993c539837eb31de174cba9f0bccd441a98f209" - integrity sha512-jS1jTqP6KCXQYX9xZpDAI07C94nwzXrNJD3H0pJXsXoOva3TTBQDD8khf6RDb7tiZrkrNJqmMyUiCfVTDbO7zg== +"@abp/bootstrap-daterangepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.2.0-rc.2.tgz#85bb869d5d43eb6c97f50943ea2c390cb4d37d54" + integrity sha512-rLLsY6GCY16IOwRkmx7vZ25sVHOM9dPdTlcZiuUuEi5+QrmgGiqZmIwqYiu4WFSPPTJtufsyMaHzXxqiZRpXVg== dependencies: bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.1.0.tgz#a06c1cd4c11554c29973db70c6a383efebb10b4e" - integrity sha512-r6onkBnpqoWrdYLDVZTiqgQBvH5UZerYRrPB3Ky+RkXJhgwn7AM3LAVrB42sIvFZRNbwOlvuAL+a0+5yDOH67Q== +"@abp/bootstrap@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.2.0-rc.2.tgz#ba2ab98d930501e7f004f99e7d7e7d4e6d3c0998" + integrity sha512-u2mtHHbmXNgngExl4hM5uo/E3k453n0im2Ci02+4dnhZoFicLZlBdtJusZAw5QR1XTLPybW6RXofWBvkZcgiwg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" bootstrap "^5.3.3" -"@abp/clipboard@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.1.0.tgz#5d5678ef7e8c355715c5a74e4aabd0a21d5e6cbe" - integrity sha512-1TxZbB/q5DPeLhYzrc6y0OVekxnjM+CRH/Gu8nAXQMX46XjdTboXvBtvgKHV1aCznGNsWyjd5A2O5G/XGg9NNw== +"@abp/clipboard@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.2.0-rc.2.tgz#608bdb3d4a3ff1a7b38ad5850385746f7cfe6c0c" + integrity sha512-w/JiTmidNc7SZuvpkQumyNj6FTBq6hLnhXeBBh4/xega1Ob2q+tbrOSpemgpK6YOXUQbmH8a4vEPvwt7uzNfvQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" clipboard "^2.0.11" -"@abp/core@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.1.0.tgz#af78fc096932d4ec45216b5e910c87a72e479b1f" - integrity sha512-6P0KGh5+IqlhQnUOyzaGaKrKzOAm1G0gg8Ay3DXyW2ksY6Zr+lyi5cxNsSi4ix1dicqfydIKvLfV+DF3mQf/dw== +"@abp/core@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.2.0-rc.2.tgz#0eab20842f8c0801d1dca036d3d66a67e155e019" + integrity sha512-8bC1MAV4iSFLJvRvM5mvrI9+N5FPAjaG79hr3p0xJtNixFOFpNELDf4p0zwy97zY8juL+AIEyJAkg+KIIK90DQ== dependencies: - "@abp/utils" "~9.1.0" + "@abp/utils" "~9.2.0-rc.2" -"@abp/datatables.net-bs5@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.1.0.tgz#3b2e7d68e947db3d3432ab5e8bff4da89a85d05d" - integrity sha512-zBrzOrfvTPJcc6mvHZDPVdXbWesKU0lKlrb7vuBZxWjrFNwVC0d/KNJPcYVvyQ3zf9rrQB5Xg0MDgu0k5Tt7tQ== +"@abp/datatables.net-bs5@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.2.0-rc.2.tgz#8c07544cafd2d2257f45150d3265c25eafc0ec0f" + integrity sha512-l3VOtc0fleUsx+ZUMr49+nPBWRBUVw4Z26ogT7O7uRR8doeerNU1GfA4yFJCUp02WIwt8cmZ+apJHADykn9Vng== dependencies: - "@abp/datatables.net" "~9.1.0" + "@abp/datatables.net" "~9.2.0-rc.2" datatables.net-bs5 "^2.1.8" -"@abp/datatables.net@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.1.0.tgz#12d6e1412aac4a2e0a61437dcdfbfbe1add7fed7" - integrity sha512-lYRvEZUkntaI+dS9Xkz23Z54eDeZ1R4Wzhqcg6VIsz3iO5kYAa5GiQjcOiBssNGJSLuaet6R5Pqh1ThEUjfvvA== +"@abp/datatables.net@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.2.0-rc.2.tgz#2880ccb61750faa1689aec15f9e49d92ab1e94a1" + integrity sha512-nljq0p8mxoBvMZngtViInyQJqX/MI7EyQeCIYNICF2LO42RRZfpKaRy5zX+4Uc3z/wz209CfK8QfRNH/d1HtBQ== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" datatables.net "^2.1.8" -"@abp/docs@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/docs/-/docs-9.1.0.tgz#0344afd56cbbd4f5b104a578c2e39831f8f30efa" - integrity sha512-jg9CQYC9e8vjRAcTsbbEmJyQ243FC18jkmcKcBHwJgRP54hf3AhPobiFzO7qx9mB0MZWdPReRSAoJ6/BSZx+9g== +"@abp/docs@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/docs/-/docs-9.2.0-rc.2.tgz#5e2ea75f0fbbe543cc5d0b3346f4ffb9a8f605b2" + integrity sha512-jxl4lZYAepQx8yvY/KG/H5uHd3UwdyLS9cb1QFJRMZn5nZZq9890L0Ri4LeWGFM71Oswdj3Wavh+5cmL2leVqw== dependencies: - "@abp/anchor-js" "~9.1.0" - "@abp/clipboard" "~9.1.0" - "@abp/malihu-custom-scrollbar-plugin" "~9.1.0" - "@abp/popper.js" "~9.1.0" - "@abp/prismjs" "~9.1.0" + "@abp/anchor-js" "~9.2.0-rc.2" + "@abp/clipboard" "~9.2.0-rc.2" + "@abp/malihu-custom-scrollbar-plugin" "~9.2.0-rc.2" + "@abp/popper.js" "~9.2.0-rc.2" + "@abp/prismjs" "~9.2.0-rc.2" -"@abp/font-awesome@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.1.0.tgz#af21ff2d7c332deb1df626404b53a78d790ba842" - integrity sha512-HcJzDCACGejOIvhxsorCdaesyDHnupR/sl5YFCsGikSqATyOSKQy4pw8wKsb1z+FCT5JYCTR7ROJ3lB1lTEgZw== +"@abp/font-awesome@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.2.0-rc.2.tgz#51eb750f5697668db538ad68cc6b9279517da537" + integrity sha512-X9JfF4n4MDZLszwZHIHuxSdGGxImEzV91ROKqNeclErCx3bOXyq/l9C+haZl/3TLzTAOSDMLgHGrT3MUCVPF5w== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" "@fortawesome/fontawesome-free" "^6.6.0" -"@abp/jquery-form@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.1.0.tgz#bdb96436c54b2a09ac4259db97e1e577410e1fc3" - integrity sha512-xjHXWstVY6B3MPB4/8g0EJeHlVvlV010OMZKFmfCGLFi6aHAEKqqWh1rMItAiVuSmCCzL0m/208ZkpoCI++jGQ== +"@abp/jquery-form@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.2.0-rc.2.tgz#58ce31a32bdfb355e5d4e3f97cb4761f594aa8e1" + integrity sha512-xN2xjK1RWKygwaqzTaWmpMlU0kGWGa2nKaudM6g9k/B6b1Pfdr8ykI4DaSuPb4BkRmurGkgy4r9o7IUoqv0T8Q== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-form "^4.3.0" -"@abp/jquery-validation-unobtrusive@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.1.0.tgz#798551e7c14ba286a819a48804107a53e974c1ee" - integrity sha512-TggKckfbLtB7Ig2kNt2kH//GaJUns8OZbpdKdh7vCWIAGedw2FLNCABCpoGnyNp0s/dVOtdyxRl9eFRN3+a4tw== +"@abp/jquery-validation-unobtrusive@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.2.0-rc.2.tgz#4e624e22cbf149d1c78da46aabaf6fc18d3b65ff" + integrity sha512-adKJxnWnhCrIYWoVdo41zgO5p4SjyypCxoHeQ7mkohry2WN6WuQG5nmHqhpdevw966DENvmVzFfz7vnO72vtrg== dependencies: - "@abp/jquery-validation" "~9.1.0" + "@abp/jquery-validation" "~9.2.0-rc.2" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.1.0.tgz#b771652377fbfb3c92ce03b7b7c069a3bb2ae70b" - integrity sha512-bmPSOHcxjitB8NkiLv0SxxuSS3FlCq9SiTUGvsT8nOStDsm+KpyYSzZQq8LSCyqWWj54tlOARTfcD4tMK7/K3Q== +"@abp/jquery-validation@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.2.0-rc.2.tgz#b6a8124d5056ddad515733bffc78703f6bd422be" + integrity sha512-4j1fg1qImif2vFZQFJ7z1olEUnjc3BByKS+7joNQ+96/NqOQdOQjRjB2n490od7ExCinKnvYx3JFttpYjHU3Xw== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-validation "^1.21.0" -"@abp/jquery@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.1.0.tgz#952b61ea3e4a99e6551f667297dbb637d83f3c50" - integrity sha512-Ulw8ClAbqmxceNYCb76bR6dq7e9b7Hs6mjlEy05zdbsSNuZW3UoycsJ+BquXi+dUNBo6OppqEmhVeiIjAKTaFg== +"@abp/jquery@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.2.0-rc.2.tgz#335643787ab282d5d2fee4f360c2b62cce3db962" + integrity sha512-baKNDuiQMzkV0LFzBwEqS0JYtXIUe5KH+MVizGR6Zv10B8l/gIrUzWI/nnqDtbts6gmY9OBDXkCTcgMQBwKqjw== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" jquery "~3.7.1" -"@abp/lodash@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.1.0.tgz#62c784bd99c9e1ccfa82905e843375c594ef8584" - integrity sha512-cCE3ZwwWcPrh+ccKx4AS1USL1ttbrXBqci/l+ROjXhFzrs6GYnhI0+30xRZgPdPRm3U3nGPrSaDAU7UuHkRScg== +"@abp/lodash@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.2.0-rc.2.tgz#521bccafcc09780c751932688b6ec45c368c2854" + integrity sha512-T6eYsDovUl4AYD9n2N4WZq9MeC4/ryke5ctvdhzuArqM7WpPvohq3VKGugrP/MLo9oD3MZ5DVQvMJ0AVg+IXNQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" lodash "^4.17.21" -"@abp/luxon@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.1.0.tgz#b246f3627bbc7ce41b7878fce1679da473b0e0ae" - integrity sha512-AouS/qEK+i69hwr4XtUOCepONLuR+1gdHFb50RpOEbUoDfTYmYpxPBdiICmZPeJ3CExErITPZxzzngCnKPPOUw== +"@abp/luxon@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.2.0-rc.2.tgz#da366d335f9575b104ea2206e076af35cbbfcfba" + integrity sha512-mr8ipYrJDDJPoImIwORK7Z6d1C07APl95L17ufRbig4sjdxjNEDR5uUXVviVafF1No2bVkYiS0xGKQmojA2twg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" luxon "^3.5.0" -"@abp/malihu-custom-scrollbar-plugin@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.1.0.tgz#288ae1916a78100f2c728dfd05a560ee99fafae4" - integrity sha512-McdMRDtbTZBjp//3GMVjRjkc16HNYDEdPc2ar1xrY817e0tEln6/TBkItAcfOvjso0TsLcvlNL1+1bjbMjj8OA== +"@abp/malihu-custom-scrollbar-plugin@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.2.0-rc.2.tgz#cb79018adf08276041eb5c5dcf75130c8995686b" + integrity sha512-jdHXx7kPduaEfFXDvf5s+QVl10RqshNbnZkkqPx0mMkhz2H/qxwzF1e0mcLFL38nEgs+0gxIkcA8/MKq85tyqQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.1.0.tgz#f3c0ae02209e496913cda53df752dabc5be648bd" - integrity sha512-WxR1l09vwxRhqyvAVP4m4IBOFjIC4pzLOmelloqES+gL/klw+T/AmY21Z/z+OV0p7aJ3V3Q1aQStuMCDCyGFnQ== +"@abp/moment@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.2.0-rc.2.tgz#f4339138668ca72be1d700624b91789344557bff" + integrity sha512-J4ZaCDEcXLGmJ+8zqgaMK9Lutnjga28LSbuiFaGLlZ1QgYZ5HDScE128o3vX9qr60tc6IIY0cDkHi4bBHo7ozw== dependencies: moment "^2.30.1" -"@abp/popper.js@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-9.1.0.tgz#94d58274f9e759d4157b717410166bb58307e35a" - integrity sha512-4jvOmlhbXQZGnskqZMVzbCmqTBn19Z/WS0THc1Qed0tu4VkZSgT1FC1wOWruQ7q8s8UJ0v4DQVsjeZl6VUTXjA== +"@abp/popper.js@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-9.2.0-rc.2.tgz#b1c8edcbc2cbb7009848c147bc75a7f301fae067" + integrity sha512-RWodA4hYor/agFaiKVtnip/U0fI577fvJeZqXSWQzwVShiviTHZxtOmYqZBbbzNPoSlxcAYJRF7F0CvgrLxNDQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" "@popperjs/core" "^2.11.8" -"@abp/prismjs@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.1.0.tgz#40b688c82ae808ef92516ea5731ccb38c19d2384" - integrity sha512-trSW1K6VtIEaAyxe7umQGimCTLrgdT6DwSiQ8PONvKa8WR5ZmzSDU7lK8SliorufkbWtdkjCvuXB81NGt3tAhQ== +"@abp/prismjs@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.2.0-rc.2.tgz#1ee12acdd9e1dcc9ae2ce4a9f21f159c3f9e3dec" + integrity sha512-GuKz6bujBlYHAgLhW1RT7aEOwKyZ+vnyMIhN0/fQ34+nbqg0PC4htwJcN6spgN8gZKD06lCqBH+GjLE2WtE/Rw== dependencies: - "@abp/clipboard" "~9.1.0" - "@abp/core" "~9.1.0" + "@abp/clipboard" "~9.2.0-rc.2" + "@abp/core" "~9.2.0-rc.2" prismjs "^1.29.0" -"@abp/select2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.1.0.tgz#9c2c6751f8506fb375c9b7c42a008c168a8f4c75" - integrity sha512-n6HqsH8aFLMrqL6LJ0gsHFlaq6T1QSt/x7HkkZyhDC7CwhXajxtsJEstDnDF9naHSXX/lerPbrHEgzn2QRMBqg== +"@abp/select2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.2.0-rc.2.tgz#ba6e839644b7ecdd198fe5286101302a3dc79ce3" + integrity sha512-AQMXb5ATgbN7grpKU8lB2xFFfirCe45lIvd9krW5SwUHCYZP7pRPbwQXAew+BqlIIYJJF2GA7/Nf//e/71bKtg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" select2 "^4.0.13" -"@abp/sweetalert2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.1.0.tgz#660f9b1db60cc04bde6cadbcf3c55320d20c2f3e" - integrity sha512-z3JMeAwva2vR8qZSRJ/BisQDdAEqjDJ/6oIGu/JYXQEchO1Lnd9UMV376nc3crLJdnyRRO7Qs6zx4FfVOCH0kw== +"@abp/sweetalert2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.2.0-rc.2.tgz#075634dc3ec996607757694d636d276ba2350e06" + integrity sha512-ZgHgqM8Q+V9ECQyAyepM5KpshKpQP463xRHwgY45oR7/CfCcO/vS56YQgJNg2zE6+lRQZaRQ9OL7lph8975vVg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" sweetalert2 "^11.14.1" -"@abp/timeago@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.1.0.tgz#496e40153241bae5d418d79cc6c65c9db6306447" - integrity sha512-a/UAYQ2GrhYFGx4p0hqr3x+teBqnjnOiWJEPq9//RTlz+EZ+OUyHbJrcUtqZRvaGJtOAsfmljD3jYnOXUlU7DA== +"@abp/timeago@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.2.0-rc.2.tgz#25324732966438bd6f505bdd932511ba2cc8c22b" + integrity sha512-WfWxxiycg77vVN2hPHqUOu552la+zxr0EZnHMqnEnnBe+gFYe9C0CpdYuyBq2eQXRXHoVzsB+4YRVNk5sbSGng== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" timeago "^1.6.7" -"@abp/toastr@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.1.0.tgz#501cbe211164b2e7416a2d4878cb34aed3550b7c" - integrity sha512-BkTDuU0p9Qa4+HT9CYwiPQjVTrbTSNBgQ18h69S3lIadLJwdC6pXeLbL9bhe+5fiuhq2f+0Oqo3X75LNEgzHkw== - dependencies: - "@abp/jquery" "~9.1.0" - toastr "^2.1.4" - -"@abp/utils@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.1.0.tgz#a4fd37d943df18b5c11f5d4869efd5d8882b7959" - integrity sha512-+UZo0ocTEZlRO41SzzHNdinjdFUDVq8wYZSj5BA1bqCVeWYhfjI2+rl9DuogCN8UEuCACA4K/qb4XcE/untQ3g== +"@abp/utils@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.2.0-rc.2.tgz#669885ad1e3ac7b8e182d7c392ea1b633df65aac" + integrity sha512-rfQxhJy04Qf50P3ciasKbDlJ+9JidD60xW77TjcoUJvljJE+A8iwTX8Wwqz7+YcC7rks2ILM4y3Ay7/47NOICQ== dependencies: just-compare "^2.3.0" @@ -338,7 +329,7 @@ jquery-validation@>=1.19, jquery-validation@^1.21.0: resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.21.0.tgz#78fc05ab76020912a246af3661b3f54a438bca93" integrity sha512-xNot0rlUIgu7duMcQ5qb6MGkGL/Z1PQaRJQoZAURW9+a/2PGOUxY36o/WyNeP2T9R6jvWB8Z9lUVvvQWI/Zs5w== -jquery@>=1.10, jquery@>=1.12.0, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2: +jquery@>=1.10, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2: version "3.6.4" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.4.tgz#ba065c188142100be4833699852bf7c24dc0252f" integrity sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ== @@ -411,10 +402,3 @@ tiny-emitter@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== - -toastr@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/toastr/-/toastr-2.1.4.tgz#8b43be64fb9d0c414871446f2db8e8ca4e95f181" - integrity sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA== - dependencies: - jquery ">=1.12.0" diff --git a/modules/docs/src/Volo.Docs.Admin.Application/Volo.Docs.Admin.Application.abppkg.analyze.json b/modules/docs/src/Volo.Docs.Admin.Application/Volo.Docs.Admin.Application.abppkg.analyze.json index 777b36a3d0..8161f069fd 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application/Volo.Docs.Admin.Application.abppkg.analyze.json +++ b/modules/docs/src/Volo.Docs.Admin.Application/Volo.Docs.Admin.Application.abppkg.analyze.json @@ -455,6 +455,17 @@ "isPrivate": false, "isStatic": false, "parameters": [] + }, + { + "returnType": "List", + "namespace": "Volo.Docs.Admin.Documents", + "name": "GetProjectsAsync", + "summary": null, + "isAsync": true, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [] } ], "contentType": "applicationService", diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Documents/index.js b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Documents/index.js index 395ef8587e..91b3770c6f 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Documents/index.js +++ b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Documents/index.js @@ -6,10 +6,11 @@ $(function () { return $datePicker.data('date'); }; - moment.localeData().preparse = (s)=>s; + moment.localeData().preparse = (s)=>s; moment.localeData().postformat = (s)=>s; - - $('.singledatepicker').daterangepicker({ + + var singleDatePicker = $('#DocumentsContainer .singledatepicker'); + singleDatePicker.daterangepicker({ "singleDatePicker": true, "showDropdowns": true, "autoUpdateInput": false, @@ -20,16 +21,16 @@ $(function () { "maxYear": 2199, }); - $('.singledatepicker').attr('autocomplete', 'off'); + singleDatePicker.attr('autocomplete', 'off'); - $('.singledatepicker').on('apply.daterangepicker', function (ev, picker) { + singleDatePicker.on('apply.daterangepicker', function (ev, picker) { $(this).val(picker.startDate.format('l')); $(this).data('date', picker.startDate.locale('en').format('YYYY-MM-DD')); }); var comboboxItems = []; - + service.getFilterItems() .then(function (result) { comboboxItems = result; @@ -44,13 +45,13 @@ $(function () { $projectId.on('change', function () { fillOptions(); }); - + var comboboxs = { version: $('#Version'), languageCode: $('#LanguageCode'), format: $('#Format') }; - + for (var key in comboboxs) { comboboxs[key].on('change', function () { fillOptions(); @@ -65,7 +66,7 @@ $(function () { comboboxs[key].empty(); } } - + function getSelectedItem() { var item = {}; for (var key in comboboxs) { @@ -73,13 +74,13 @@ $(function () { } return item; } - + function SetComboboxsValues(item) { for (var key in comboboxs) { comboboxs[key].val(item[key]); } } - + function addComboboxsEmptyItem() { for (var key in comboboxs) { comboboxs[key].append($('
public bool AttachCultureInfo { get; set; } = true; + + /// + /// Set the url of the select account page. + /// + public string SelectAccountPage { get; set; } = "~/Account/SelectAccount"; } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs index f61d6fde45..5c2f6ef996 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs @@ -2,13 +2,15 @@ using System.Collections.Generic; using System.Linq; using System.Security.Claims; +using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Primitives; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using OpenIddict.Abstractions; using OpenIddict.Server.AspNetCore; using Volo.Abp.AspNetCore.Security; @@ -28,34 +30,22 @@ public class AuthorizeController : AbpOpenIdDictControllerBase { var request = await GetOpenIddictServerRequestAsync(HttpContext); - // If prompt=login was specified by the client application, - // immediately return the user agent to the login page. - if (request.HasPromptValue(OpenIddictConstants.PromptValues.Login)) - { - // To avoid endless login -> authorization redirects, the prompt=login flag - // is removed from the authorization request payload before redirecting the user. - var prompt = string.Join(" ", request.GetPromptValues().Remove(OpenIddictConstants.PromptValues.Login)); - - var parameters = Request.HasFormContentType ? - Request.Form.Where(parameter => parameter.Key != OpenIddictConstants.Parameters.Prompt).ToList() : - Request.Query.Where(parameter => parameter.Key != OpenIddictConstants.Parameters.Prompt).ToList(); - - parameters.Add(KeyValuePair.Create(OpenIddictConstants.Parameters.Prompt, new StringValues(prompt))); - - return Challenge( - authenticationSchemes: IdentityConstants.ApplicationScheme, - properties: new AuthenticationProperties - { - RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters) - }); - } - - // Retrieve the user principal stored in the authentication cookie. - // If a max_age parameter was provided, ensure that the cookie is not too old. - // If the user principal can't be extracted or the cookie is too old, redirect the user to the login page. + // Try to retrieve the user principal stored in the authentication cookie and redirect + // the user agent to the login page (or to an external provider) in the following cases: + // + // - If the user principal can't be extracted or the cookie is too old. + // - If prompt=login was specified by the client application. + // - If max_age=0 was specified by the client application (max_age=0 is equivalent to prompt=login). + // - If a max_age parameter was provided and the authentication cookie is not considered "fresh" enough. + // + // For scenarios where the default authentication handler configured in the ASP.NET Core + // authentication options shouldn't be used, a specific scheme can be specified here. var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme); - if (result == null || !result.Succeeded || (request.MaxAge != null && result.Properties?.IssuedUtc != null && - DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) + if (result is not { Succeeded: true } || + ((request.HasPromptValue(OpenIddictConstants.PromptValues.Login) || request.MaxAge is 0 || + (request.MaxAge != null && result.Properties?.IssuedUtc != null && + TimeProvider.System.GetUtcNow() - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) && + TempData["IgnoreAuthenticationChallenge"] is null or false)) { // If the client application requested promptless authentication, // return an error indicating that the user is not logged in. @@ -70,13 +60,33 @@ public class AuthorizeController : AbpOpenIdDictControllerBase })); } - return Challenge( - authenticationSchemes: IdentityConstants.ApplicationScheme, - properties: new AuthenticationProperties - { - RedirectUri = Request.PathBase + Request.Path + QueryString.Create( - Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList()) - }); + // To avoid endless login endpoint -> authorization endpoint redirects, a special temp data entry is + // used to skip the challenge if the user agent has already been redirected to the login endpoint. + // + // Note: this flag doesn't guarantee that the user has accepted to re-authenticate. If such a guarantee + // is needed, the existing authentication cookie MUST be deleted AND revoked (e.g using ASP.NET Core + // Identity's security stamp feature with an extremely short revalidation time span) before triggering + // a challenge to redirect the user agent to the login endpoint. + TempData["IgnoreAuthenticationChallenge"] = true; + + // For scenarios where the default challenge handler configured in the ASP.NET Core + // authentication options shouldn't be used, a specific scheme can be specified here. + return Challenge(new AuthenticationProperties + { + RedirectUri = Request.PathBase + Request.Path + QueryString.Create(Request.HasFormContentType ? Request.Form : Request.Query) + }); + } + + // If prompt=select_account was specified by the client application, + // We will redirect the user to the select_account page. + if (request.HasPromptValue(OpenIddictConstants.PromptValues.SelectAccount) && TempData["IgnoreSelectAccount"] is null or false) + { + // To avoid endless select account endpoint -> authorization endpoint redirects, a special temp data entry is + // used to skip the redirect if the user agent has already been redirected to the select account endpoint. + TempData["IgnoreSelectAccount"] = true; + + var selectAccountPath = HttpContext.RequestServices.GetRequiredService>().Value.SelectAccountPage.RemovePostFix("/"); + return Redirect(Url.Content($"{selectAccountPath}?RedirectUri={UrlEncoder.Default.Encode(Request.PathBase + Request.Path + QueryString.Create(Request.HasFormContentType ? Request.Form : Request.Query))}")); } // Retrieve the profile of the logged in user. @@ -90,8 +100,7 @@ public class AuthorizeController : AbpOpenIdDictControllerBase authenticationSchemes: IdentityConstants.ApplicationScheme, properties: new AuthenticationProperties { - RedirectUri = Request.PathBase + Request.Path + QueryString.Create( - Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList()) + RedirectUri = Request.PathBase + Request.Path + QueryString.Create(Request.HasFormContentType ? Request.Form : Request.Query) }); } } @@ -103,8 +112,7 @@ public class AuthorizeController : AbpOpenIdDictControllerBase authenticationSchemes: IdentityConstants.ApplicationScheme, properties: new AuthenticationProperties { - RedirectUri = Request.PathBase + Request.Path + QueryString.Create( - Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList()) + RedirectUri = Request.PathBase + Request.Path + QueryString.Create(Request.HasFormContentType ? Request.Form : Request.Query) }); } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/ar.json b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/ar.json index c5ffcabc1a..61c52f63c5 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/ar.json +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/ar.json @@ -9,7 +9,7 @@ "Authorization": "تفويض", "DoYouWantToGrantAccessToYourData": "هل تريد منح {0} حق الوصول إلى بياناتك؟", "ScopesRequested": "النطاقات المطلوبة", - "Accept": "يقبل", - "Deny": "ينكر" + "Accept": "قبول", + "Deny": "رفض" } -} \ No newline at end of file +} diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.abppkg.analyze.json b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.abppkg.analyze.json index 9820472a7d..0a68812c4c 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.abppkg.analyze.json +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.abppkg.analyze.json @@ -797,66 +797,7 @@ "isOptional": false }, { - "type": "Guid", - "name": "client", - "isOptional": false - }, - { - "type": "CancellationToken", - "name": "cancellationToken", - "isOptional": true - } - ] - }, - { - "returnType": "List", - "namespace": "Volo.Abp.OpenIddict.Tokens", - "name": "FindAsync", - "summary": null, - "isAsync": true, - "isPublic": true, - "isPrivate": false, - "isStatic": false, - "parameters": [ - { - "type": "String", - "name": "subject", - "isOptional": false - }, - { - "type": "Guid", - "name": "client", - "isOptional": false - }, - { - "type": "String", - "name": "status", - "isOptional": false - }, - { - "type": "CancellationToken", - "name": "cancellationToken", - "isOptional": true - } - ] - }, - { - "returnType": "List", - "namespace": "Volo.Abp.OpenIddict.Tokens", - "name": "FindAsync", - "summary": null, - "isAsync": true, - "isPublic": true, - "isPrivate": false, - "isStatic": false, - "parameters": [ - { - "type": "String", - "name": "subject", - "isOptional": false - }, - { - "type": "Guid", + "type": "Nullable", "name": "client", "isOptional": false }, @@ -1036,6 +977,43 @@ } ] }, + { + "returnType": "ValueTask", + "namespace": "Volo.Abp.OpenIddict.Tokens", + "name": "RevokeAsync", + "summary": null, + "isAsync": false, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ + { + "type": "String", + "name": "subject", + "isOptional": false + }, + { + "type": "Nullable", + "name": "applicationId", + "isOptional": false + }, + { + "type": "String", + "name": "status", + "isOptional": false + }, + { + "type": "String", + "name": "type", + "isOptional": false + }, + { + "type": "CancellationToken", + "name": "cancellationToken", + "isOptional": true + } + ] + }, { "returnType": "ValueTask", "namespace": "Volo.Abp.OpenIddict.Tokens", @@ -1054,7 +1032,51 @@ { "type": "CancellationToken", "name": "cancellationToken", + "isOptional": true + } + ] + }, + { + "returnType": "ValueTask", + "namespace": "Volo.Abp.OpenIddict.Tokens", + "name": "RevokeByApplicationIdAsync", + "summary": null, + "isAsync": false, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ + { + "type": "Guid", + "name": "applicationId", + "isOptional": false + }, + { + "type": "CancellationToken", + "name": "cancellationToken", + "isOptional": true + } + ] + }, + { + "returnType": "ValueTask", + "namespace": "Volo.Abp.OpenIddict.Tokens", + "name": "RevokeBySubjectAsync", + "summary": null, + "isAsync": false, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ + { + "type": "String", + "name": "subject", "isOptional": false + }, + { + "type": "CancellationToken", + "name": "cancellationToken", + "isOptional": true } ] } @@ -1344,10 +1366,20 @@ "isOptional": false }, { - "type": "Guid", + "type": "Nullable", "name": "client", "isOptional": false }, + { + "type": "String", + "name": "status", + "isOptional": false + }, + { + "type": "String", + "name": "type", + "isOptional": false + }, { "type": "CancellationToken", "name": "cancellationToken", @@ -1358,26 +1390,16 @@ { "returnType": "List", "namespace": "Volo.Abp.OpenIddict.Authorizations", - "name": "FindAsync", + "name": "FindByApplicationIdAsync", "summary": null, "isAsync": true, "isPublic": true, "isPrivate": false, "isStatic": false, "parameters": [ - { - "type": "String", - "name": "subject", - "isOptional": false - }, { "type": "Guid", - "name": "client", - "isOptional": false - }, - { - "type": "String", - "name": "status", + "name": "applicationId", "isOptional": false }, { @@ -1388,33 +1410,40 @@ ] }, { - "returnType": "List", + "returnType": "OpenIddictAuthorization", "namespace": "Volo.Abp.OpenIddict.Authorizations", - "name": "FindAsync", + "name": "FindByIdAsync", "summary": null, "isAsync": true, "isPublic": true, "isPrivate": false, "isStatic": false, "parameters": [ - { - "type": "String", - "name": "subject", - "isOptional": false - }, { "type": "Guid", - "name": "client", + "name": "id", "isOptional": false }, { - "type": "String", - "name": "status", - "isOptional": false - }, + "type": "CancellationToken", + "name": "cancellationToken", + "isOptional": true + } + ] + }, + { + "returnType": "List", + "namespace": "Volo.Abp.OpenIddict.Authorizations", + "name": "FindBySubjectAsync", + "summary": null, + "isAsync": true, + "isPublic": true, + "isPrivate": false, + "isStatic": false, + "parameters": [ { "type": "String", - "name": "type", + "name": "subject", "isOptional": false }, { @@ -1427,7 +1456,7 @@ { "returnType": "List", "namespace": "Volo.Abp.OpenIddict.Authorizations", - "name": "FindByApplicationIdAsync", + "name": "ListAsync", "summary": null, "isAsync": true, "isPublic": true, @@ -1435,8 +1464,13 @@ "isStatic": false, "parameters": [ { - "type": "Guid", - "name": "applicationId", + "type": "Nullable", + "name": "count", + "isOptional": false + }, + { + "type": "Nullable", + "name": "offset", "isOptional": false }, { @@ -1447,9 +1481,9 @@ ] }, { - "returnType": "OpenIddictAuthorization", + "returnType": "Int64", "namespace": "Volo.Abp.OpenIddict.Authorizations", - "name": "FindByIdAsync", + "name": "PruneAsync", "summary": null, "isAsync": true, "isPublic": true, @@ -1457,8 +1491,8 @@ "isStatic": false, "parameters": [ { - "type": "Guid", - "name": "id", + "type": "DateTime", + "name": "date", "isOptional": false }, { @@ -1469,11 +1503,11 @@ ] }, { - "returnType": "List", + "returnType": "ValueTask", "namespace": "Volo.Abp.OpenIddict.Authorizations", - "name": "FindBySubjectAsync", + "name": "RevokeAsync", "summary": null, - "isAsync": true, + "isAsync": false, "isPublic": true, "isPrivate": false, "isStatic": false, @@ -1483,6 +1517,21 @@ "name": "subject", "isOptional": false }, + { + "type": "Nullable", + "name": "applicationId", + "isOptional": false + }, + { + "type": "String", + "name": "status", + "isOptional": false + }, + { + "type": "String", + "name": "type", + "isOptional": false + }, { "type": "CancellationToken", "name": "cancellationToken", @@ -1491,23 +1540,18 @@ ] }, { - "returnType": "List", + "returnType": "ValueTask", "namespace": "Volo.Abp.OpenIddict.Authorizations", - "name": "ListAsync", + "name": "RevokeByApplicationIdAsync", "summary": null, - "isAsync": true, + "isAsync": false, "isPublic": true, "isPrivate": false, "isStatic": false, "parameters": [ { - "type": "Nullable", - "name": "count", - "isOptional": false - }, - { - "type": "Nullable", - "name": "offset", + "type": "Guid", + "name": "applicationId", "isOptional": false }, { @@ -1518,18 +1562,18 @@ ] }, { - "returnType": "Int64", + "returnType": "ValueTask", "namespace": "Volo.Abp.OpenIddict.Authorizations", - "name": "PruneAsync", + "name": "RevokeBySubjectAsync", "summary": null, - "isAsync": true, + "isAsync": false, "isPublic": true, "isPrivate": false, "isStatic": false, "parameters": [ { - "type": "DateTime", - "name": "date", + "type": "String", + "name": "subject", "isOptional": false }, { diff --git a/modules/openiddict/test/Volo.Abp.OpenIddict.MongoDB.Tests/Volo.Abp.OpenIddict.MongoDB.Tests.csproj b/modules/openiddict/test/Volo.Abp.OpenIddict.MongoDB.Tests/Volo.Abp.OpenIddict.MongoDB.Tests.csproj index d5f8903293..60a422fd38 100644 --- a/modules/openiddict/test/Volo.Abp.OpenIddict.MongoDB.Tests/Volo.Abp.OpenIddict.MongoDB.Tests.csproj +++ b/modules/openiddict/test/Volo.Abp.OpenIddict.MongoDB.Tests/Volo.Abp.OpenIddict.MongoDB.Tests.csproj @@ -10,9 +10,9 @@ - - - + + + diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs index ef056918c8..aa9bfe0029 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs @@ -43,25 +43,24 @@ public class PermissionAppService : ApplicationService, IPermissionAppService }; var multiTenancySide = CurrentTenant.GetMultiTenancySide(); + var permissionGroups = new List(); foreach (var group in await PermissionDefinitionManager.GetGroupsAsync()) { var groupDto = CreatePermissionGroupDto(group); - - var neededCheckPermissions = new List(); - var permissions = group.GetPermissionsWithChildren() .Where(x => x.IsEnabled) .Where(x => !x.Providers.Any() || x.Providers.Contains(providerName)) .Where(x => x.MultiTenancySide.HasFlag(multiTenancySide)); - + + var neededCheckPermissions = new List(); foreach (var permission in permissions) { - if(permission.Parent != null && !neededCheckPermissions.Contains(permission.Parent)) + if (permission.Parent != null && !neededCheckPermissions.Contains(permission.Parent)) { continue; } - + if (await SimpleStateCheckerManager.IsEnabledAsync(permission)) { neededCheckPermissions.Add(permission); @@ -73,42 +72,46 @@ public class PermissionAppService : ApplicationService, IPermissionAppService continue; } - var grantInfoDtos = neededCheckPermissions - .Select(CreatePermissionGrantInfoDto) - .ToList(); + groupDto.Permissions.AddRange(neededCheckPermissions.Select(CreatePermissionGrantInfoDto)); + permissionGroups.Add(groupDto); + } - var multipleGrantInfo = await PermissionManager.GetAsync(neededCheckPermissions.Select(x => x.Name).ToArray(), providerName, providerKey); + var multipleGrantInfo = await PermissionManager.GetAsync( + permissionGroups.SelectMany(group => group.Permissions).Select(permission => permission.Name).ToArray(), + providerName, + providerKey); - foreach (var grantInfo in multipleGrantInfo.Result) + foreach (var permissionGroup in permissionGroups) + { + foreach (var permission in permissionGroup.Permissions) { - var grantInfoDto = grantInfoDtos.First(x => x.Name == grantInfo.Name); - - grantInfoDto.IsGranted = grantInfo.IsGranted; - - foreach (var provider in grantInfo.Providers) + var grantInfo = multipleGrantInfo.Result.FirstOrDefault(x => x.Name == permission.Name); + if (grantInfo == null) { - grantInfoDto.GrantedProviders.Add(new ProviderInfoDto - { - ProviderName = provider.Name, - ProviderKey = provider.Key, - }); + continue; } - groupDto.Permissions.Add(grantInfoDto); + permission.IsGranted = grantInfo.IsGranted; + permission.GrantedProviders = grantInfo.Providers.Select(x => new ProviderInfoDto + { + ProviderName = x.Name, + ProviderKey = x.Key, + }).ToList(); } - if (groupDto.Permissions.Any()) + if (permissionGroup.Permissions.Any()) { - result.Groups.Add(groupDto); + result.Groups.Add(permissionGroup); } } return result; } - private PermissionGrantInfoDto CreatePermissionGrantInfoDto(PermissionDefinition permission) + protected virtual PermissionGrantInfoDto CreatePermissionGrantInfoDto(PermissionDefinition permission) { - return new PermissionGrantInfoDto { + return new PermissionGrantInfoDto + { Name = permission.Name, DisplayName = permission.DisplayName?.Localize(StringLocalizerFactory), ParentName = permission.Parent?.Name, @@ -117,10 +120,10 @@ public class PermissionAppService : ApplicationService, IPermissionAppService }; } - private PermissionGroupDto CreatePermissionGroupDto(PermissionGroupDefinition group) + protected virtual PermissionGroupDto CreatePermissionGroupDto(PermissionGroupDefinition group) { var localizableDisplayName = group.DisplayName as LocalizableString; - + return new PermissionGroupDto { Name = group.Name, diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor index 87c2c0db91..7992764c00 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor @@ -37,22 +37,25 @@ @if (_groups != null && _groups.Any()) {
- + @foreach (var group in _groups) { @if (group.Permissions.Any(x => x.IsGranted)) { - - @group.DisplayName ( @(group.Permissions.Count(x => x.IsGranted)) ) - + @group.DisplayName (@(group.Permissions.Count(x => x.IsGranted))) } else { - - @group.DisplayName ( @(group.Permissions.Count(x => x.IsGranted)) ) - + + @group.DisplayName (@(group.Permissions.Count(x => x.IsGranted))) + } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs index d2c2e092d5..cd3be870a1 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs @@ -58,7 +58,7 @@ public partial class PermissionManagementModal var result = await PermissionAppService.GetAsync(_providerName, _providerKey); _entityDisplayName = entityDisplayName ?? result.EntityDisplayName; - _allGroups = result.Groups; + _allGroups = result.Groups.OrderBy(x => x.DisplayName).ToList(); _groups = _allGroups.ToList(); NormalizePermissionGroup(); diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.css b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.css index f6870a7a16..a0b6579faf 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.css +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.css @@ -15,7 +15,7 @@ fieldset legend { } ::deep .lpx-scroll-pills-container ul li { - border: 1px solid #e8eef3; + border: 1px solid var(--lpx-border-color); margin-bottom: 10px; border-radius: 10px; } @@ -26,6 +26,15 @@ fieldset legend { background-color: #6c5dd3 !important; } -::deep .lpx-theme-dark .lpx-scroll-pills-container ul li { - border: 1px solid #23262a; -} \ No newline at end of file +::deep .nav-pills .nav-link { + background: none; + border: 0; + border-radius: var(--bs-nav-pills-border-radius); +} + +::deep .nav-pills .nav-link:disabled { + color: var(--bs-nav-link-disabled-color); + background-color: transparent; + border-color: transparent; +} + diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ar.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ar.json index 52de9ea37a..30f4b647e8 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ar.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ar.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "منح كافة الأذونات", "SelectAllInThisTab": "تحديد الكل", "SaveWithoutAnyPermissionsWarningMessage": "هل أنت متأكد أنك تريد الحفظ بدون أي أذونات؟", - "PermissionGroup": "مجموعة الأذونات" + "PermissionGroup": "مجموعة الأذونات", + "Filter": "تصفية" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/cs.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/cs.json index c0e38b9320..814b8e423c 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/cs.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/cs.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Dát veškerá oprávnění", "SelectAllInThisTab": "Vybrat vše", "SaveWithoutAnyPermissionsWarningMessage": "Opravdu chcete ukládat bez jakýchkoli oprávnění?", - "PermissionGroup": "Skupina oprávnění" + "PermissionGroup": "Skupina oprávnění", + "Filter": "Filtr" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/de.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/de.json index a2bb5fe72a..7a40c5d4d4 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/de.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/de.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Alle Berechtigungen erteilen", "SelectAllInThisTab": "Alle auswählen", "SaveWithoutAnyPermissionsWarningMessage": "Sind Sie sicher, dass Sie ohne Berechtigungen speichern möchten?", - "PermissionGroup": "Berechtigungsgruppe" + "PermissionGroup": "Berechtigungsgruppe", + "Filter": "Filtern" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/el.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/el.json index bc2c96db04..3449b7fab6 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/el.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/el.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Χορήγηση όλων των δικαιώματων", "SelectAllInThisTab": "Επιλογή όλων", "SaveWithoutAnyPermissionsWarningMessage": "Είστε βέβαιοι ότι θέλετε να αποθηκεύσετε χωρίς δικαιώματα;", - "PermissionGroup": "Ομάδα δικαιωμάτων" + "PermissionGroup": "Ομάδα δικαιωμάτων", + "Filter": "Φίλτρο" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en-GB.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en-GB.json index 62762d00a8..be6cbcd1f5 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en-GB.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en-GB.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Grant all permissions", "SelectAllInThisTab": "Select all", "SaveWithoutAnyPermissionsWarningMessage": "Are you sure you want to save without any permissions?", - "PermissionGroup": "Permission Group" + "PermissionGroup": "Permission Group", + "Filter": "Filter" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json index 45aeb905e2..b8299d4e5b 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Grant all permissions", "SelectAllInThisTab": "Select all", "SaveWithoutAnyPermissionsWarningMessage": "Are you sure you want to save without any permissions?", - "PermissionGroup": "Permission Group" + "PermissionGroup": "Permission Group", + "Filter": "Filter" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/es.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/es.json index 610d86a2ff..622883b259 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/es.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/es.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Conceder todos los permisos", "SelectAllInThisTab": "Seleccionar todo", "SaveWithoutAnyPermissionsWarningMessage": "¿Estás seguro de que quieres guardar sin ningún permiso?", - "PermissionGroup": "Grupo de permisos" + "PermissionGroup": "Grupo de permisos", + "Filter": "Filtrar" } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fa.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fa.json index 615ef79412..7ea9a6c4f3 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fa.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fa.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "اعطای همه مجوزها", "SelectAllInThisTab": "انتخاب همه", "SaveWithoutAnyPermissionsWarningMessage": "آیا مطمئن هستید که می خواهید بدون هیچ دسترسی ذخیره کنید؟", - "PermissionGroup": "گروه دسترسی" + "PermissionGroup": "گروه دسترسی", + "Filter": "فیلتر" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fi.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fi.json index 2c24727ad5..f9b828ade4 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fi.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fi.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Myönnä kaikki käyttöoikeudet", "SelectAllInThisTab": "Valitse kaikki", "SaveWithoutAnyPermissionsWarningMessage": "Haluatko varmasti tallentaa ilman käyttöoikeuksia?", - "PermissionGroup": "Käyttöoikeus" + "PermissionGroup": "Käyttöoikeus", + "Filter": "Suodatus" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fr.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fr.json index b2676631a2..79f4e68377 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fr.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fr.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Accorder toutes les autorisations", "SelectAllInThisTab": "Sélectionner tous les", "SaveWithoutAnyPermissionsWarningMessage": "Êtes-vous sûr de vouloir enregistrer sans aucune autorisation ?", - "PermissionGroup": "Groupe d'autorisations" + "PermissionGroup": "Groupe d'autorisations", + "Filter": "Filtrer" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hi.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hi.json index fa271bc752..cdc030e8ef 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hi.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hi.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "सभी अनुमतियां प्रदान करें", "SelectAllInThisTab": "सभी का चयन करे", "SaveWithoutAnyPermissionsWarningMessage": "क्या आप वाकई बिना किसी अनुमति के सहेजना चाहते हैं?", - "PermissionGroup": "अनुमति समूह" + "PermissionGroup": "अनुमति समूह", + "Filter": "फ़िल्टर" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hr.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hr.json index 45034a5bb6..531d0a35d2 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hr.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hr.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Dodijelite sva dopuštenja", "SelectAllInThisTab": "Odaberi sve", "SaveWithoutAnyPermissionsWarningMessage": "Jeste li sigurni da želite spremiti bez ikakvih dopuštenja?", - "PermissionGroup": "Grupa dozvola" + "PermissionGroup": "Grupa dozvola", + "Filter": "Filtriraj" } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hu.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hu.json index fdb727bf2b..3177ef8fa7 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hu.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hu.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Adjon meg minden engedélyt", "SelectAllInThisTab": "Mindet kiválaszt", "SaveWithoutAnyPermissionsWarningMessage": "Biztos, hogy engedélyek nélkül akar menteni?", - "PermissionGroup": "Engedélycsoport" + "PermissionGroup": "Engedélycsoport", + "Filter": "Szűrő" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/is.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/is.json index 322e6b5580..7db32eb99e 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/is.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/is.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Veita allar heimildir", "SelectAllInThisTab": "Velja allt", "SaveWithoutAnyPermissionsWarningMessage": "Ertu viss um að þú viljir vista án nokkurra heimilda?", - "PermissionGroup": "Heimildahópur" + "PermissionGroup": "Heimildahópur", + "Filter": "Sía" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/it.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/it.json index 1312568b53..e473c7b310 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/it.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/it.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Concedi tutte le autorizzazioni", "SelectAllInThisTab": "Seleziona tutto", "SaveWithoutAnyPermissionsWarningMessage": "Sei sicuro di voler salvare senza alcuna autorizzazione?", - "PermissionGroup": "Gruppo di autorizzazioni" + "PermissionGroup": "Gruppo di autorizzazioni", + "Filter": "Filtro" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/nl.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/nl.json index 0dacfe8341..4b928c4d03 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/nl.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/nl.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Verleen alle rechten", "SelectAllInThisTab": "Selecteer alles", "SaveWithoutAnyPermissionsWarningMessage": "Weet u zeker dat u zonder rechten wilt opslaan?", - "PermissionGroup": "Rechtengroep" + "PermissionGroup": "Rechtengroep", + "Filter": "Filter" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl-PL.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl-PL.json index aa0bf32851..ee483651eb 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl-PL.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl-PL.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Udziel wszystkich uprawnień", "SelectAllInThisTab": "Zaznacz wszystkie", "SaveWithoutAnyPermissionsWarningMessage": "Czy na pewno chcesz zapisać bez żadnych uprawnień?", - "PermissionGroup": "Grupa uprawnień" + "PermissionGroup": "Grupa uprawnień", + "Filter": "Filtr" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pt-BR.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pt-BR.json index 14f45ec46a..28231b227f 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pt-BR.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pt-BR.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Conceder todas as permissões", "SelectAllInThisTab": "Selecionar todos", "SaveWithoutAnyPermissionsWarningMessage": "Tem certeza que deseja salvar sem nenhuma permissão?", - "PermissionGroup": "Grupo de permissão" + "PermissionGroup": "Grupo de permissão", + "Filter": "Filtrar" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ro-RO.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ro-RO.json index 6466540438..af7db26acb 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ro-RO.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ro-RO.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Acordă toate permisiunile", "SelectAllInThisTab": "Selectează toate", "SaveWithoutAnyPermissionsWarningMessage": "Sigur doriți să salvați fără nicio permisiune?", - "PermissionGroup": "Grup de permisiuni" + "PermissionGroup": "Grup de permisiuni", + "Filter": "Filtru" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ru.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ru.json index d46b4e289e..6041357b0e 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ru.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ru.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Предоставить все разрешения", "SelectAllInThisTab": "Выбрать все", "SaveWithoutAnyPermissionsWarningMessage": "Вы уверены, что хотите сохранить без каких-либо разрешений?", - "PermissionGroup": "Группа разрешений" + "PermissionGroup": "Группа разрешений", + "Filter": "Фильтр" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sk.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sk.json index 722582d28f..c079b8eba1 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sk.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sk.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Udeliť všetky oprávnenia", "SelectAllInThisTab": "Vybrať všetky", "SaveWithoutAnyPermissionsWarningMessage": "Naozaj chcete ukladať bez akýchkoľvek povolení?", - "PermissionGroup": "Skupina oprávnení" + "PermissionGroup": "Skupina oprávnení", + "Filter": "Filtrovať" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sl.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sl.json index 36640ffdbd..9c906f7387 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sl.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sl.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Dodeli vsa dovoljenja", "SelectAllInThisTab": "Izberi vse", "SaveWithoutAnyPermissionsWarningMessage": "Ali ste prepričani, da želite shraniti brez kakršnih koli dovoljenj?", - "PermissionGroup": "Skupina dovoljenj" + "PermissionGroup": "Skupina dovoljenj", + "Filter": "Filtriraj" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sv.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sv.json index 3671bf1422..d46fca4dc4 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sv.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sv.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Ge alla behörigheter", "SelectAllInThisTab": "Välj alla", "SaveWithoutAnyPermissionsWarningMessage": "Är du säker på att du vill spara utan några behörigheter?", - "PermissionGroup": "Behörighetsgrupp" + "PermissionGroup": "Behörighetsgrupp", + "Filter": "Filtrera" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json index 3edfc2942f..960cd8cf02 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Tüm izinleri ver", "SelectAllInThisTab": "Hepsini seç", "SaveWithoutAnyPermissionsWarningMessage": "Hiçbir izin olmadan kaydetmek istediğinize emin misiniz?", - "PermissionGroup": "İzin Grubu" + "PermissionGroup": "İzin Grubu", + "Filter": "Filtre" } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/vi.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/vi.json index d927f6a4a9..5e9a9d8565 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/vi.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/vi.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "Cấp tất cả các quyền", "SelectAllInThisTab": "Chọn tất cả", "SaveWithoutAnyPermissionsWarningMessage": "Bạn có chắc chắn muốn lưu mà không có bất kỳ quyền nào không?", - "PermissionGroup": "Nhóm quyền" + "PermissionGroup": "Nhóm quyền", + "Filter": "Lọc" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hans.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hans.json index 3ae69c4d58..df844ca2bf 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hans.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hans.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "授予所有权限", "SelectAllInThisTab": "全选", "SaveWithoutAnyPermissionsWarningMessage": "您确定要在没有任何权限的情况下保存吗?", - "PermissionGroup": "权限组" + "PermissionGroup": "权限组", + "Filter": "过滤" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hant.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hant.json index b2d46a42a5..72af56c960 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hant.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hant.json @@ -7,6 +7,7 @@ "SelectAllInAllTabs": "授予所有權限", "SelectAllInThisTab": "全選", "SaveWithoutAnyPermissionsWarningMessage": "您確定要在沒有任何權限的情況下保存嗎?", - "PermissionGroup": "權限組" + "PermissionGroup": "權限組", + "Filter": "過濾" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeeder.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeeder.cs index 1e8a4aab7b..de2b824412 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeeder.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeeder.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; using Volo.Abp.Guids; using Volo.Abp.MultiTenancy; @@ -33,14 +34,17 @@ public class PermissionDataSeeder : IPermissionDataSeeder, ITransientDependency { using (CurrentTenant.Change(tenantId)) { - var names = grantedPermissions.ToArray(); - var existsPermissionGrants = (await PermissionGrantRepository.GetListAsync(names, providerName, providerKey)).Select(x => x.Name).ToList(); - var permissions = names.Except(existsPermissionGrants).Select(permissionName => new PermissionGrant(GuidGenerator.Create(), permissionName, providerName, providerKey, tenantId)).ToList(); - if (!permissions.Any()) + using (PermissionGrantRepository.DisableTracking()) { - return; + var names = grantedPermissions.ToArray(); + var existsPermissionGrants = (await PermissionGrantRepository.GetListAsync(names, providerName, providerKey)).Select(x => x.Name).ToList(); + var permissions = names.Except(existsPermissionGrants).Select(permissionName => new PermissionGrant(GuidGenerator.Create(), permissionName, providerName, providerKey, tenantId)).ToList(); + if (!permissions.Any()) + { + return; + } + await PermissionGrantRepository.InsertManyAsync(permissions); } - await PermissionGrantRepository.InsertManyAsync(permissions); } } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementProvider.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementProvider.cs index 0a80ed53e5..b6dfc2e518 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementProvider.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementProvider.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; using Volo.Abp.Guids; using Volo.Abp.MultiTenancy; @@ -34,21 +35,24 @@ public abstract class PermissionManagementProvider : IPermissionManagementProvid public virtual async Task CheckAsync(string[] names, string providerName, string providerKey) { - var multiplePermissionValueProviderGrantInfo = new MultiplePermissionValueProviderGrantInfo(names); - if (providerName != Name) + using (PermissionGrantRepository.DisableTracking()) { - return multiplePermissionValueProviderGrantInfo; - } + var multiplePermissionValueProviderGrantInfo = new MultiplePermissionValueProviderGrantInfo(names); + if (providerName != Name) + { + return multiplePermissionValueProviderGrantInfo; + } - var permissionGrants = await PermissionGrantRepository.GetListAsync(names, providerName, providerKey); + var permissionGrants = await PermissionGrantRepository.GetListAsync(names, providerName, providerKey); - foreach (var permissionName in names) - { - var isGrant = permissionGrants.Any(x => x.Name == permissionName); - multiplePermissionValueProviderGrantInfo.Result[permissionName] = new PermissionValueProviderGrantInfo(isGrant, providerKey); - } + foreach (var permissionName in names) + { + var isGrant = permissionGrants.Any(x => x.Name == permissionName); + multiplePermissionValueProviderGrantInfo.Result[permissionName] = new PermissionValueProviderGrantInfo(isGrant, providerKey); + } - return multiplePermissionValueProviderGrantInfo; + return multiplePermissionValueProviderGrantInfo; + } } public virtual Task SetAsync(string name, string providerKey, bool isGranted) diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs index 6eed394a31..85ffd77924 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Authorization.Permissions; using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; namespace Volo.Abp.PermissionManagement; @@ -67,36 +68,39 @@ public class PermissionStore : IPermissionStore, ITransientDependency string currentName, PermissionGrantCacheItem currentCacheItem) { - var permissions = await PermissionDefinitionManager.GetPermissionsAsync(); + using (PermissionGrantRepository.DisableTracking()) + { + var permissions = await PermissionDefinitionManager.GetPermissionsAsync(); - Logger.LogDebug($"Getting all granted permissions from the repository for this provider name,key: {providerName},{providerKey}"); + Logger.LogDebug($"Getting all granted permissions from the repository for this provider name,key: {providerName},{providerKey}"); - var grantedPermissionsHashSet = new HashSet( - (await PermissionGrantRepository.GetListAsync(providerName, providerKey)).Select(p => p.Name) - ); + var grantedPermissionsHashSet = new HashSet( + (await PermissionGrantRepository.GetListAsync(providerName, providerKey)).Select(p => p.Name) + ); - Logger.LogDebug($"Setting the cache items. Count: {permissions.Count}"); + Logger.LogDebug($"Setting the cache items. Count: {permissions.Count}"); - var cacheItems = new List>(); + var cacheItems = new List>(); - foreach (var permission in permissions) - { - var isGranted = grantedPermissionsHashSet.Contains(permission.Name); + foreach (var permission in permissions) + { + var isGranted = grantedPermissionsHashSet.Contains(permission.Name); - cacheItems.Add(new KeyValuePair( - CalculateCacheKey(permission.Name, providerName, providerKey), - new PermissionGrantCacheItem(isGranted)) - ); + cacheItems.Add(new KeyValuePair( + CalculateCacheKey(permission.Name, providerName, providerKey), + new PermissionGrantCacheItem(isGranted)) + ); - if (permission.Name == currentName) - { - currentCacheItem.IsGranted = isGranted; + if (permission.Name == currentName) + { + currentCacheItem.IsGranted = isGranted; + } } - } - await Cache.SetManyAsync(cacheItems); + await Cache.SetManyAsync(cacheItems); - Logger.LogDebug($"Finished setting the cache items. Count: {permissions.Count}"); + Logger.LogDebug($"Finished setting the cache items. Count: {permissions.Count}"); + } } public virtual async Task IsGrantedAsync(string[] names, string providerName, string providerKey) @@ -169,34 +173,37 @@ public class PermissionStore : IPermissionStore, ITransientDependency string providerKey, List notCacheKeys) { - var permissions = (await PermissionDefinitionManager.GetPermissionsAsync()) - .Where(x => notCacheKeys.Any(k => GetPermissionNameFormCacheKeyOrNull(k) == x.Name)).ToList(); + using (PermissionGrantRepository.DisableTracking()) + { + var permissions = (await PermissionDefinitionManager.GetPermissionsAsync()) + .Where(x => notCacheKeys.Any(k => GetPermissionNameFormCacheKeyOrNull(k) == x.Name)).ToList(); - Logger.LogDebug($"Getting not cache granted permissions from the repository for this provider name,key: {providerName},{providerKey}"); + Logger.LogDebug($"Getting not cache granted permissions from the repository for this provider name,key: {providerName},{providerKey}"); - var grantedPermissionsHashSet = new HashSet( - (await PermissionGrantRepository.GetListAsync(notCacheKeys.Select(GetPermissionNameFormCacheKeyOrNull).ToArray(), providerName, providerKey)).Select(p => p.Name) - ); + var grantedPermissionsHashSet = new HashSet( + (await PermissionGrantRepository.GetListAsync(notCacheKeys.Select(GetPermissionNameFormCacheKeyOrNull).ToArray(), providerName, providerKey)).Select(p => p.Name) + ); - Logger.LogDebug($"Setting the cache items. Count: {permissions.Count}"); + Logger.LogDebug($"Setting the cache items. Count: {permissions.Count}"); - var cacheItems = new List>(); + var cacheItems = new List>(); - foreach (var permission in permissions) - { - var isGranted = grantedPermissionsHashSet.Contains(permission.Name); + foreach (var permission in permissions) + { + var isGranted = grantedPermissionsHashSet.Contains(permission.Name); - cacheItems.Add(new KeyValuePair( - CalculateCacheKey(permission.Name, providerName, providerKey), - new PermissionGrantCacheItem(isGranted)) - ); - } + cacheItems.Add(new KeyValuePair( + CalculateCacheKey(permission.Name, providerName, providerKey), + new PermissionGrantCacheItem(isGranted)) + ); + } - await Cache.SetManyAsync(cacheItems); + await Cache.SetManyAsync(cacheItems); - Logger.LogDebug($"Finished setting the cache items. Count: {permissions.Count}"); + Logger.LogDebug($"Finished setting the cache items. Count: {permissions.Count}"); - return cacheItems; + return cacheItems; + } } protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs index 4fe6e63e3f..d1dff35f35 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs @@ -141,15 +141,15 @@ public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependenc throw; } - await unitOfWork.CompleteAsync(); - } - - if (newOrChangedPermissions.Any()) - { - await DistributedEventBus.PublishAsync(new DynamicPermissionDefinitionsChangedEto + if (newOrChangedPermissions.Any()) { - Permissions = newOrChangedPermissions.Distinct().ToList() - }); + await DistributedEventBus.PublishAsync(new DynamicPermissionDefinitionsChangedEto + { + Permissions = newOrChangedPermissions.Distinct().ToList() + }); + } + + await unitOfWork.CompleteAsync(); } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml index 155eebfd5a..a9caf5c348 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml @@ -23,7 +23,7 @@
- +
@@ -34,7 +34,7 @@
- Permission Group + @L["PermissionGroup"].Value diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo.Abp.PermissionManagement.MongoDB.Tests.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo.Abp.PermissionManagement.MongoDB.Tests.csproj index 0f9e0169e8..f42d7c0385 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo.Abp.PermissionManagement.MongoDB.Tests.csproj +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo.Abp.PermissionManagement.MongoDB.Tests.csproj @@ -20,9 +20,9 @@ - - - + + + diff --git a/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/package.json b/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/package.json index e60c436043..c5aa54a269 100644 --- a/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/package.json +++ b/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/package.json @@ -3,6 +3,6 @@ "name": "demo-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2" } } diff --git a/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/yarn.lock b/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/yarn.lock index 38a43d8ed5..7a9396e283 100644 --- a/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/yarn.lock +++ b/modules/setting-management/app/Volo.Abp.SettingManagement.DemoApp/yarn.lock @@ -2,194 +2,185 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.1.0.tgz#e9e0321037c34b43145dfc7827c422fbf79f59ab" - integrity sha512-vLkyrdFXw0/MPyU2hzqGJJk7trmIlVMdflNIe/rF77UIRmJYHWaTmPt9i5PLN/O4s0jnE9kWlaaJ/VnnFWeGsQ== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~9.1.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.1.0.tgz#35a73580b9b67a33708f2cf897243b50c9ad7100" - integrity sha512-rggTYL9poOFICsOseDX22JNhD0KWH8Oxs0YH91TZjvm/F67cPemW99eBlJc/YdJ7bN6icRiebN8m81y9MSanyg== - dependencies: - "@abp/aspnetcore.mvc.ui" "~9.1.0" - "@abp/bootstrap" "~9.1.0" - "@abp/bootstrap-datepicker" "~9.1.0" - "@abp/bootstrap-daterangepicker" "~9.1.0" - "@abp/datatables.net-bs5" "~9.1.0" - "@abp/font-awesome" "~9.1.0" - "@abp/jquery-form" "~9.1.0" - "@abp/jquery-validation-unobtrusive" "~9.1.0" - "@abp/lodash" "~9.1.0" - "@abp/luxon" "~9.1.0" - "@abp/malihu-custom-scrollbar-plugin" "~9.1.0" - "@abp/moment" "~9.1.0" - "@abp/select2" "~9.1.0" - "@abp/sweetalert2" "~9.1.0" - "@abp/timeago" "~9.1.0" - "@abp/toastr" "~9.1.0" - -"@abp/aspnetcore.mvc.ui@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.1.0.tgz#38d058ab148a36ad4d63870e8a501ca339e4a70e" - integrity sha512-lUw1ij87KJp+Rj4cz8oSQkdRuoMq76l/wIAGwtXH+EOKusVpSJTOTioAV/tPKeKZ15ubE4bO5jWcQGxZkXgH3w== +"@abp/aspnetcore.mvc.ui.theme.basic@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.2.0-rc.2.tgz#e32c8bd046d7377ae3f6a703eac9069e10bcd600" + integrity sha512-oj4lGrzllgGrq7rI7gz1Xvq5TU+xT+isGVf4T8hF3INE/J2nIyK45HnkjDq5wpdM3P+HkdM1+RtWNrLSVu0Klg== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.2.0-rc.2.tgz#77562013b604d0193e361efa6801235179dc7b4f" + integrity sha512-5KcMGg6o6W5uwsujrjUARlZFgWmZeYBNCf1vkf3btakkwTzmPDbypUx6VrgBJbt4AUnYlMQbSHvbfK0d3I1e6w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~9.2.0-rc.2" + "@abp/bootstrap" "~9.2.0-rc.2" + "@abp/bootstrap-datepicker" "~9.2.0-rc.2" + "@abp/bootstrap-daterangepicker" "~9.2.0-rc.2" + "@abp/datatables.net-bs5" "~9.2.0-rc.2" + "@abp/font-awesome" "~9.2.0-rc.2" + "@abp/jquery-form" "~9.2.0-rc.2" + "@abp/jquery-validation-unobtrusive" "~9.2.0-rc.2" + "@abp/lodash" "~9.2.0-rc.2" + "@abp/luxon" "~9.2.0-rc.2" + "@abp/malihu-custom-scrollbar-plugin" "~9.2.0-rc.2" + "@abp/moment" "~9.2.0-rc.2" + "@abp/select2" "~9.2.0-rc.2" + "@abp/sweetalert2" "~9.2.0-rc.2" + "@abp/timeago" "~9.2.0-rc.2" + +"@abp/aspnetcore.mvc.ui@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.2.0-rc.2.tgz#e1c0e4b0280d5fe37659da8dc696150f0d461336" + integrity sha512-WG4WWGS1mSb2QWAVOA2nrvoi9/3oueEaKEG18xbPgWiLz5GCq8gsopKqUgW/fmBaw+1Jh9tsuCTH7eED/2tUxA== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.1.0.tgz#96a1875a2b92f03b8f90f7842ebf8849cf8a4d04" - integrity sha512-3FQm1SK8qmzoP7LOvXkZpq1AzrdPHKRdQJjoqK9N+lQxQjNfOxEFMxeDSg6WJKO619Cnve++jAyMidojXlCiwQ== +"@abp/bootstrap-datepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.2.0-rc.2.tgz#429c2d44a22f9190013aea0efa420854b7e83a4f" + integrity sha512-bxMpaxTgnF5/VELbKvtZVw0tI0bowzUQFVSBQJqO4o7wJmxyHNzJuaom8Is+tCsoMrpo8t9EGMGfpWHV1IsrjQ== dependencies: bootstrap-datepicker "^1.10.0" -"@abp/bootstrap-daterangepicker@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.1.0.tgz#1993c539837eb31de174cba9f0bccd441a98f209" - integrity sha512-jS1jTqP6KCXQYX9xZpDAI07C94nwzXrNJD3H0pJXsXoOva3TTBQDD8khf6RDb7tiZrkrNJqmMyUiCfVTDbO7zg== +"@abp/bootstrap-daterangepicker@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.2.0-rc.2.tgz#85bb869d5d43eb6c97f50943ea2c390cb4d37d54" + integrity sha512-rLLsY6GCY16IOwRkmx7vZ25sVHOM9dPdTlcZiuUuEi5+QrmgGiqZmIwqYiu4WFSPPTJtufsyMaHzXxqiZRpXVg== dependencies: bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.1.0.tgz#a06c1cd4c11554c29973db70c6a383efebb10b4e" - integrity sha512-r6onkBnpqoWrdYLDVZTiqgQBvH5UZerYRrPB3Ky+RkXJhgwn7AM3LAVrB42sIvFZRNbwOlvuAL+a0+5yDOH67Q== +"@abp/bootstrap@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.2.0-rc.2.tgz#ba2ab98d930501e7f004f99e7d7e7d4e6d3c0998" + integrity sha512-u2mtHHbmXNgngExl4hM5uo/E3k453n0im2Ci02+4dnhZoFicLZlBdtJusZAw5QR1XTLPybW6RXofWBvkZcgiwg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" bootstrap "^5.3.3" -"@abp/core@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.1.0.tgz#af78fc096932d4ec45216b5e910c87a72e479b1f" - integrity sha512-6P0KGh5+IqlhQnUOyzaGaKrKzOAm1G0gg8Ay3DXyW2ksY6Zr+lyi5cxNsSi4ix1dicqfydIKvLfV+DF3mQf/dw== +"@abp/core@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.2.0-rc.2.tgz#0eab20842f8c0801d1dca036d3d66a67e155e019" + integrity sha512-8bC1MAV4iSFLJvRvM5mvrI9+N5FPAjaG79hr3p0xJtNixFOFpNELDf4p0zwy97zY8juL+AIEyJAkg+KIIK90DQ== dependencies: - "@abp/utils" "~9.1.0" + "@abp/utils" "~9.2.0-rc.2" -"@abp/datatables.net-bs5@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.1.0.tgz#3b2e7d68e947db3d3432ab5e8bff4da89a85d05d" - integrity sha512-zBrzOrfvTPJcc6mvHZDPVdXbWesKU0lKlrb7vuBZxWjrFNwVC0d/KNJPcYVvyQ3zf9rrQB5Xg0MDgu0k5Tt7tQ== +"@abp/datatables.net-bs5@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.2.0-rc.2.tgz#8c07544cafd2d2257f45150d3265c25eafc0ec0f" + integrity sha512-l3VOtc0fleUsx+ZUMr49+nPBWRBUVw4Z26ogT7O7uRR8doeerNU1GfA4yFJCUp02WIwt8cmZ+apJHADykn9Vng== dependencies: - "@abp/datatables.net" "~9.1.0" + "@abp/datatables.net" "~9.2.0-rc.2" datatables.net-bs5 "^2.1.8" -"@abp/datatables.net@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.1.0.tgz#12d6e1412aac4a2e0a61437dcdfbfbe1add7fed7" - integrity sha512-lYRvEZUkntaI+dS9Xkz23Z54eDeZ1R4Wzhqcg6VIsz3iO5kYAa5GiQjcOiBssNGJSLuaet6R5Pqh1ThEUjfvvA== +"@abp/datatables.net@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.2.0-rc.2.tgz#2880ccb61750faa1689aec15f9e49d92ab1e94a1" + integrity sha512-nljq0p8mxoBvMZngtViInyQJqX/MI7EyQeCIYNICF2LO42RRZfpKaRy5zX+4Uc3z/wz209CfK8QfRNH/d1HtBQ== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" datatables.net "^2.1.8" -"@abp/font-awesome@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.1.0.tgz#af21ff2d7c332deb1df626404b53a78d790ba842" - integrity sha512-HcJzDCACGejOIvhxsorCdaesyDHnupR/sl5YFCsGikSqATyOSKQy4pw8wKsb1z+FCT5JYCTR7ROJ3lB1lTEgZw== +"@abp/font-awesome@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.2.0-rc.2.tgz#51eb750f5697668db538ad68cc6b9279517da537" + integrity sha512-X9JfF4n4MDZLszwZHIHuxSdGGxImEzV91ROKqNeclErCx3bOXyq/l9C+haZl/3TLzTAOSDMLgHGrT3MUCVPF5w== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" "@fortawesome/fontawesome-free" "^6.6.0" -"@abp/jquery-form@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.1.0.tgz#bdb96436c54b2a09ac4259db97e1e577410e1fc3" - integrity sha512-xjHXWstVY6B3MPB4/8g0EJeHlVvlV010OMZKFmfCGLFi6aHAEKqqWh1rMItAiVuSmCCzL0m/208ZkpoCI++jGQ== +"@abp/jquery-form@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.2.0-rc.2.tgz#58ce31a32bdfb355e5d4e3f97cb4761f594aa8e1" + integrity sha512-xN2xjK1RWKygwaqzTaWmpMlU0kGWGa2nKaudM6g9k/B6b1Pfdr8ykI4DaSuPb4BkRmurGkgy4r9o7IUoqv0T8Q== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-form "^4.3.0" -"@abp/jquery-validation-unobtrusive@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.1.0.tgz#798551e7c14ba286a819a48804107a53e974c1ee" - integrity sha512-TggKckfbLtB7Ig2kNt2kH//GaJUns8OZbpdKdh7vCWIAGedw2FLNCABCpoGnyNp0s/dVOtdyxRl9eFRN3+a4tw== +"@abp/jquery-validation-unobtrusive@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.2.0-rc.2.tgz#4e624e22cbf149d1c78da46aabaf6fc18d3b65ff" + integrity sha512-adKJxnWnhCrIYWoVdo41zgO5p4SjyypCxoHeQ7mkohry2WN6WuQG5nmHqhpdevw966DENvmVzFfz7vnO72vtrg== dependencies: - "@abp/jquery-validation" "~9.1.0" + "@abp/jquery-validation" "~9.2.0-rc.2" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.1.0.tgz#b771652377fbfb3c92ce03b7b7c069a3bb2ae70b" - integrity sha512-bmPSOHcxjitB8NkiLv0SxxuSS3FlCq9SiTUGvsT8nOStDsm+KpyYSzZQq8LSCyqWWj54tlOARTfcD4tMK7/K3Q== +"@abp/jquery-validation@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.2.0-rc.2.tgz#b6a8124d5056ddad515733bffc78703f6bd422be" + integrity sha512-4j1fg1qImif2vFZQFJ7z1olEUnjc3BByKS+7joNQ+96/NqOQdOQjRjB2n490od7ExCinKnvYx3JFttpYjHU3Xw== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" jquery-validation "^1.21.0" -"@abp/jquery@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.1.0.tgz#952b61ea3e4a99e6551f667297dbb637d83f3c50" - integrity sha512-Ulw8ClAbqmxceNYCb76bR6dq7e9b7Hs6mjlEy05zdbsSNuZW3UoycsJ+BquXi+dUNBo6OppqEmhVeiIjAKTaFg== +"@abp/jquery@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.2.0-rc.2.tgz#335643787ab282d5d2fee4f360c2b62cce3db962" + integrity sha512-baKNDuiQMzkV0LFzBwEqS0JYtXIUe5KH+MVizGR6Zv10B8l/gIrUzWI/nnqDtbts6gmY9OBDXkCTcgMQBwKqjw== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" jquery "~3.7.1" -"@abp/lodash@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.1.0.tgz#62c784bd99c9e1ccfa82905e843375c594ef8584" - integrity sha512-cCE3ZwwWcPrh+ccKx4AS1USL1ttbrXBqci/l+ROjXhFzrs6GYnhI0+30xRZgPdPRm3U3nGPrSaDAU7UuHkRScg== +"@abp/lodash@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.2.0-rc.2.tgz#521bccafcc09780c751932688b6ec45c368c2854" + integrity sha512-T6eYsDovUl4AYD9n2N4WZq9MeC4/ryke5ctvdhzuArqM7WpPvohq3VKGugrP/MLo9oD3MZ5DVQvMJ0AVg+IXNQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" lodash "^4.17.21" -"@abp/luxon@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.1.0.tgz#b246f3627bbc7ce41b7878fce1679da473b0e0ae" - integrity sha512-AouS/qEK+i69hwr4XtUOCepONLuR+1gdHFb50RpOEbUoDfTYmYpxPBdiICmZPeJ3CExErITPZxzzngCnKPPOUw== +"@abp/luxon@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.2.0-rc.2.tgz#da366d335f9575b104ea2206e076af35cbbfcfba" + integrity sha512-mr8ipYrJDDJPoImIwORK7Z6d1C07APl95L17ufRbig4sjdxjNEDR5uUXVviVafF1No2bVkYiS0xGKQmojA2twg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" luxon "^3.5.0" -"@abp/malihu-custom-scrollbar-plugin@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.1.0.tgz#288ae1916a78100f2c728dfd05a560ee99fafae4" - integrity sha512-McdMRDtbTZBjp//3GMVjRjkc16HNYDEdPc2ar1xrY817e0tEln6/TBkItAcfOvjso0TsLcvlNL1+1bjbMjj8OA== +"@abp/malihu-custom-scrollbar-plugin@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.2.0-rc.2.tgz#cb79018adf08276041eb5c5dcf75130c8995686b" + integrity sha512-jdHXx7kPduaEfFXDvf5s+QVl10RqshNbnZkkqPx0mMkhz2H/qxwzF1e0mcLFL38nEgs+0gxIkcA8/MKq85tyqQ== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.1.0.tgz#f3c0ae02209e496913cda53df752dabc5be648bd" - integrity sha512-WxR1l09vwxRhqyvAVP4m4IBOFjIC4pzLOmelloqES+gL/klw+T/AmY21Z/z+OV0p7aJ3V3Q1aQStuMCDCyGFnQ== +"@abp/moment@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.2.0-rc.2.tgz#f4339138668ca72be1d700624b91789344557bff" + integrity sha512-J4ZaCDEcXLGmJ+8zqgaMK9Lutnjga28LSbuiFaGLlZ1QgYZ5HDScE128o3vX9qr60tc6IIY0cDkHi4bBHo7ozw== dependencies: moment "^2.30.1" -"@abp/select2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.1.0.tgz#9c2c6751f8506fb375c9b7c42a008c168a8f4c75" - integrity sha512-n6HqsH8aFLMrqL6LJ0gsHFlaq6T1QSt/x7HkkZyhDC7CwhXajxtsJEstDnDF9naHSXX/lerPbrHEgzn2QRMBqg== +"@abp/select2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.2.0-rc.2.tgz#ba6e839644b7ecdd198fe5286101302a3dc79ce3" + integrity sha512-AQMXb5ATgbN7grpKU8lB2xFFfirCe45lIvd9krW5SwUHCYZP7pRPbwQXAew+BqlIIYJJF2GA7/Nf//e/71bKtg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" select2 "^4.0.13" -"@abp/sweetalert2@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.1.0.tgz#660f9b1db60cc04bde6cadbcf3c55320d20c2f3e" - integrity sha512-z3JMeAwva2vR8qZSRJ/BisQDdAEqjDJ/6oIGu/JYXQEchO1Lnd9UMV376nc3crLJdnyRRO7Qs6zx4FfVOCH0kw== +"@abp/sweetalert2@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.2.0-rc.2.tgz#075634dc3ec996607757694d636d276ba2350e06" + integrity sha512-ZgHgqM8Q+V9ECQyAyepM5KpshKpQP463xRHwgY45oR7/CfCcO/vS56YQgJNg2zE6+lRQZaRQ9OL7lph8975vVg== dependencies: - "@abp/core" "~9.1.0" + "@abp/core" "~9.2.0-rc.2" sweetalert2 "^11.14.1" -"@abp/timeago@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.1.0.tgz#496e40153241bae5d418d79cc6c65c9db6306447" - integrity sha512-a/UAYQ2GrhYFGx4p0hqr3x+teBqnjnOiWJEPq9//RTlz+EZ+OUyHbJrcUtqZRvaGJtOAsfmljD3jYnOXUlU7DA== +"@abp/timeago@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.2.0-rc.2.tgz#25324732966438bd6f505bdd932511ba2cc8c22b" + integrity sha512-WfWxxiycg77vVN2hPHqUOu552la+zxr0EZnHMqnEnnBe+gFYe9C0CpdYuyBq2eQXRXHoVzsB+4YRVNk5sbSGng== dependencies: - "@abp/jquery" "~9.1.0" + "@abp/jquery" "~9.2.0-rc.2" timeago "^1.6.7" -"@abp/toastr@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.1.0.tgz#501cbe211164b2e7416a2d4878cb34aed3550b7c" - integrity sha512-BkTDuU0p9Qa4+HT9CYwiPQjVTrbTSNBgQ18h69S3lIadLJwdC6pXeLbL9bhe+5fiuhq2f+0Oqo3X75LNEgzHkw== - dependencies: - "@abp/jquery" "~9.1.0" - toastr "^2.1.4" - -"@abp/utils@~9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.1.0.tgz#a4fd37d943df18b5c11f5d4869efd5d8882b7959" - integrity sha512-+UZo0ocTEZlRO41SzzHNdinjdFUDVq8wYZSj5BA1bqCVeWYhfjI2+rl9DuogCN8UEuCACA4K/qb4XcE/untQ3g== +"@abp/utils@~9.2.0-rc.2": + version "9.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.2.0-rc.2.tgz#669885ad1e3ac7b8e182d7c392ea1b633df65aac" + integrity sha512-rfQxhJy04Qf50P3ciasKbDlJ+9JidD60xW77TjcoUJvljJE+A8iwTX8Wwqz7+YcC7rks2ILM4y3Ay7/47NOICQ== dependencies: just-compare "^2.3.0" @@ -263,7 +254,7 @@ jquery-validation@>=1.19, jquery-validation@^1.21.0: resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.21.0.tgz#78fc05ab76020912a246af3661b3f54a438bca93" integrity sha512-xNot0rlUIgu7duMcQ5qb6MGkGL/Z1PQaRJQoZAURW9+a/2PGOUxY36o/WyNeP2T9R6jvWB8Z9lUVvvQWI/Zs5w== -jquery@>=1.10, jquery@>=1.12.0, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2: +jquery@>=1.10, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2: version "3.6.4" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.4.tgz#ba065c188142100be4833699852bf7c24dc0252f" integrity sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ== @@ -321,10 +312,3 @@ timeago@^1.6.7: integrity sha512-FikcjN98+ij0siKH4VO4dZ358PR3oDDq4Vdl1+sN9gWz1/+JXGr3uZbUShYH/hL7bMhcTpPbplJU5Tej4b4jbQ== dependencies: jquery ">=1.5.0 <4.0" - -toastr@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/toastr/-/toastr-2.1.4.tgz#8b43be64fb9d0c414871446f2db8e8ca4e95f181" - integrity sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA== - dependencies: - jquery ">=1.12.0" diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Application/Volo/Abp/SettingManagement/TimeZoneSettingsAppService.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Application/Volo/Abp/SettingManagement/TimeZoneSettingsAppService.cs index ee8580ac22..4aa5700d8e 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Application/Volo/Abp/SettingManagement/TimeZoneSettingsAppService.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Application/Volo/Abp/SettingManagement/TimeZoneSettingsAppService.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; @@ -12,6 +13,8 @@ public class TimeZoneSettingsAppService : SettingManagementAppServiceBase, ITime protected ISettingManager SettingManager { get; } protected ITimezoneProvider TimezoneProvider { get; } + private const string UnspecifiedTimeZone = "Unspecified"; + public TimeZoneSettingsAppService(ISettingManager settingManager, ITimezoneProvider timezoneProvider) { SettingManager = settingManager; @@ -20,21 +23,36 @@ public class TimeZoneSettingsAppService : SettingManagementAppServiceBase, ITime public virtual async Task GetAsync() { - if (CurrentTenant.GetMultiTenancySide() == MultiTenancySides.Host) + var timezone = CurrentTenant.GetMultiTenancySide() == MultiTenancySides.Host + ? await SettingManager.GetOrNullGlobalAsync(TimingSettingNames.TimeZone) + : await SettingManager.GetOrNullForCurrentTenantAsync(TimingSettingNames.TimeZone); + + if (timezone.IsNullOrWhiteSpace()) { - return await SettingManager.GetOrNullGlobalAsync(TimingSettingNames.TimeZone); + timezone = UnspecifiedTimeZone; } - return await SettingManager.GetOrNullForCurrentTenantAsync(TimingSettingNames.TimeZone); + return timezone; } public virtual Task> GetTimezonesAsync() { - return Task.FromResult(TimeZoneHelper.GetTimezones(TimezoneProvider.GetWindowsTimezones())); + var timezones = TimeZoneHelper.GetTimezones(TimezoneProvider.GetIanaTimezones()); + timezones.Insert(0, new NameValue + { + Name = L["DefaultTimeZone"], + Value = UnspecifiedTimeZone + }); + return Task.FromResult(timezones); } public virtual async Task UpdateAsync(string timezone) { + if (timezone.Equals(UnspecifiedTimeZone, StringComparison.OrdinalIgnoreCase)) + { + timezone = null; + } + if (CurrentTenant.GetMultiTenancySide() == MultiTenancySides.Host) { await SettingManager.SetGlobalAsync(TimingSettingNames.TimeZone, timezone); diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/ar.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/ar.json index 1a0a2e218a..ec35f5286f 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/ar.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/ar.json @@ -20,7 +20,8 @@ "Menu:Emailing": "إرسال بالبريد الإلكتروني", "Menu:TimeZone": "وحدة زمنية", "DisplayName:Timezone": "وحدة زمنية", - "TimezoneHelpText": "يُستخدم هذا الإعداد على مستوى التطبيق أو على أساس المستأجر.", + "TimezoneHelpText": "تتيح لك هذه الميزة تعيين منطقة زمنية افتراضية لخادمك، بينما يمكن للمستخدمين اختيار منطقتهم الزمنية الخاصة. إذا اختلفت المنطقة الزمنية للمستخدم عن المنطقة الزمنية للخادم، فسيتم تعديل جميع الأوقات وفقًا لذلك. على سبيل المثال، مع تعيين الخادم على أوروبا/لندن(00:00) ومستخدم في أوروبا/باريس(+01:00)، سيتم تعديل الأوقات بساعة واحدة لذلك المستخدم. سيؤدي اختيار 'المنطقة الزمنية الافتراضية' إلى استخدام المنطقة الزمنية للخادم أو المتصفح تلقائيًا.", + "DefaultTimeZone": "الوحدة الزمنية الافتراضية", "SmtpHost": "مضيف", "SmtpPort": "ميناء", "SmtpUserName": "اسم المستخدم", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/cs.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/cs.json index 983c6718b9..cd9d4d78d1 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/cs.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/cs.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Zasílání e-mailem", "Menu:TimeZone": "Časové Pásmo", "DisplayName:Timezone": "Časové pásmo", - "TimezoneHelpText": "Toto nastavení se používá pro celou aplikaci nebo pro klienty.", + "TimezoneHelpText": "Tato funkce vám umožňuje nastavit výchozí časové pásmo pro váš server, zatímco uživatelé si mohou vybrat své vlastní. Pokud se časové pásmo uživatele liší od časového pásma serveru, všechny časy budou odpovídajícím způsobem upraveny. Například, když je server nastaven na Evropa/Londýn(00:00) a uživatel je v Evropa/Paříž(+01:00), časy budou pro tohoto uživatele upraveny o 1 hodinu. Výběrem 'Výchozí časové pásmo' se automaticky použije časové pásmo serveru nebo prohlížeče.", + "DefaultTimeZone": "Výchozí časové pásmo", "SmtpHost": "Hostitel", "SmtpPort": "Přístav", "SmtpUserName": "Uživatelské jméno", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/de-DE.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/de-DE.json index 3f44913224..6e17d1c0dc 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/de-DE.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/de-DE.json @@ -20,7 +20,8 @@ "Menu:Emailing": "E-Mail", "Menu:TimeZone": "Zeitzone", "DisplayName:Timezone": "Zeitzone", - "TimezoneHelpText": "Diese Einstellung wird für die Anwendung oder den Mandanten verwendet.", + "TimezoneHelpText": "Diese Einstellung wird für die Anwendung oder den Mandanten verwendet. Die Standardzeitzone wird versuchen, die Zeitzone des Browsers oder des Servers zu verwenden.", + "DefaultTimeZone": "Standardzeitzone", "SmtpHost": "Host", "SmtpPort": "Port", "SmtpUserName": "Benutzername", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/de.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/de.json index c5efcb1718..7f8debc508 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/de.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/de.json @@ -20,7 +20,9 @@ "Menu:Emailing": "E-Mail senden", "Menu:TimeZone": "Zeitzone", "DisplayName:Timezone": "Zeitzone", - "TimezoneHelpText": "Diese Einstellung wird anwendungsweit oder mandantenbasiert verwendet.", + "TimezoneHelpText": "Diese Funktion ermöglicht es Ihnen, eine Standardzeitzone für Ihren Server festzulegen, während Benutzer ihre eigene auswählen können. Wenn sich die Zeitzone eines Benutzers von der des Servers unterscheidet, werden alle Zeiten entsprechend angepasst. Zum Beispiel: Wenn der Server auf Europa/London(00:00) eingestellt ist und ein Benutzer in Europa/Paris(+01:00) ist, werden die Zeiten für diesen Benutzer um 1 Stunde angepasst. Die Auswahl von 'Standardzeitzone' verwendet automatisch die Zeitzone des Servers oder Browsers.", + "DefaultTimeZone": "Standardzeitzone", + "DefaultTimeZoneInfo": "Die Standardzeitzone wird versuchen, die Zeitzone des Browsers oder des Servers zu verwenden.", "SmtpHost": "Gastgeber", "SmtpPort": "Hafen", "SmtpUserName": "Nutzername", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/en.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/en.json index e7d221b4ef..84933fe44b 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/en.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/en.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Emailing", "Menu:TimeZone": "Time Zone", "DisplayName:Timezone": "Time zone", - "TimezoneHelpText": "This setting is used for application-wide or tenant-based.", + "TimezoneHelpText": "This feature allows you to set a default time zone for your server, while users can select their own. If a user's time zone differs from the server's, all times will adjust accordingly. For example, with the server set to Europe/London(00:00) and a user in Europe/Paris(+01:00), times will be adjusted by 1 hour for that user. Selecting 'Default Timezone' will automatically use the server's or browser's time zone.", + "DefaultTimeZone": "Default time zone", "SmtpHost": "Host", "SmtpPort": "Port", "SmtpUserName": "User name", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/es.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/es.json index 7d6bcd8236..bfc0ab6a3b 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/es.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/es.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Configuración", "Menu:TimeZone": "Zona Horaria", "DisplayName:Timezone": "Zona horaria", - "TimezoneHelpText": "Esta configuración se utiliza para toda la aplicación o basada en inquilinos.", + "TimezoneHelpText": "Esta función le permite establecer una zona horaria predeterminada para su servidor, mientras que los usuarios pueden seleccionar la suya propia. Si la zona horaria de un usuario difiere de la del servidor, todas las horas se ajustarán en consecuencia. Por ejemplo, con el servidor configurado en Europa/Londres(00:00) y un usuario en Europa/París(+01:00), las horas se ajustarán 1 hora para ese usuario. Al seleccionar 'Zona horaria predeterminada' se utilizará automáticamente la zona horaria del servidor o del navegador.", + "DefaultTimeZone": "Zona horaria predeterminada", "SmtpHost": "Host", "SmtpPort": "Puerto", "SmtpUserName": "Nombre de usuario", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/fi.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/fi.json index 312ecf608c..f9b52baf2b 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/fi.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/fi.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Sähköpostiviestit", "Menu:TimeZone": "Aikavyöhyke", "DisplayName:Timezone": "Aikavyöhyke", - "TimezoneHelpText": "Tätä asetusta käytetään sovelluksenlaajuisesti tai vuokraajakohtaisesti.", + "TimezoneHelpText": "Tämä ominaisuus antaa sinun asettaa oletusaikavyöhykkeen palvelimellesi, kun taas käyttäjät voivat valita oman aikavyöhykkeensä. Jos käyttäjän aikavyöhyke poikkeaa palvelimen aikavyöhykkeestä, kaikki ajat mukautetaan vastaavasti. Esimerkiksi, kun palvelin on asetettu Eurooppa/Lontoo(00:00) ja käyttäjä on Eurooppa/Pariisi(+01:00), ajat mukautetaan tunnilla kyseiselle käyttäjälle. 'Oletusaikavyöhyke' -valinta käyttää automaattisesti palvelimen tai selaimen aikavyöhykettä.", + "DefaultTimeZone": "Oletusaikavyöhyke", "SmtpHost": "Isäntä", "SmtpPort": "Portti", "SmtpUserName": "Käyttäjänimi", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/fr.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/fr.json index 3ec1a598c2..758e9a090c 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/fr.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/fr.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Envoi par e-mail", "Menu:TimeZone": "Fuseau Horaire", "DisplayName:Timezone": "Fuseau horaire", - "TimezoneHelpText": "Ce paramètre est utilisé à l’échelle de l’application ou basé sur le client.", + "TimezoneHelpText": "Cette fonctionnalité vous permet de définir un fuseau horaire par défaut pour votre serveur, tandis que les utilisateurs peuvent sélectionner le leur. Si le fuseau horaire d'un utilisateur diffère de celui du serveur, tous les horaires seront ajustés en conséquence. Par exemple, avec le serveur réglé sur Europe/Londres(00:00) et un utilisateur en Europe/Paris(+01:00), les horaires seront ajustés d'une heure pour cet utilisateur. La sélection de 'Fuseau horaire par défaut' utilisera automatiquement le fuseau horaire du serveur ou du navigateur.", + "DefaultTimeZone": "Fuseau horaire par défaut", "SmtpHost": "Hôte", "SmtpPort": "Port", "SmtpUserName": "Nom d'utilisateur", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/hi.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/hi.json index 786616f901..8dacb200e4 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/hi.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/hi.json @@ -20,7 +20,8 @@ "Menu:Emailing": "ईमेल से भेजना", "Menu:TimeZone": "समय क्षेत्र", "DisplayName:Timezone": "समय क्षेत्र", - "TimezoneHelpText": "इस सेटिंग का उपयोग एप्लिकेशन-व्यापी या किरायेदार-आधारित के लिए किया जाता है।", + "TimezoneHelpText": "यह सुविधा आपको अपने सर्वर के लिए एक डिफ़ॉल्ट समय क्षेत्र सेट करने की अनुमति देती है, जबकि उपयोगकर्ता अपना स्वयं का चुन सकते हैं। यदि किसी उपयोगकर्ता का समय क्षेत्र सर्वर के समय क्षेत्र से भिन्न है, तो सभी समय तदनुसार समायोजित किए जाएंगे। उदाहरण के लिए, सर्वर को यूरोप/लंदन(00:00) पर सेट किए जाने पर और एक उपयोगकर्ता यूरोप/पेरिस(+01:00) में होने पर, उस उपयोगकर्ता के लिए समय 1 घंटे के लिए समायोजित किया जाएगा। 'डिफ़ॉल्ट समय क्षेत्र' का चयन स्वचालित रूप से सर्वर या ब्राउज़र के समय क्षेत्र का उपयोग करेगा।", + "DefaultTimeZone": "डिफ़ॉल्ट समय क्षेत्र", "SmtpHost": "मेज़बान", "SmtpPort": "बंदरगाह", "SmtpUserName": "उपयोगकर्ता नाम", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/hr.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/hr.json index acd0c132fa..133233a10f 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/hr.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/hr.json @@ -7,7 +7,7 @@ "Permission:Emailing": "Slanje e-poštom", "Permission:EmailingTest": "Test slanja e-pošte", "Permission:TimeZone": "Vremenska zona", - "SendTestEmail": "Po�alji probnu e-poštu", + "SendTestEmail": "Pošalji probnu e-poštu", "SenderEmailAddress": "Adresa e-pošte pošiljatelja", "TargetEmailAddress": "Ciljana adresa e-pošte", "Subject": "Predmet", @@ -20,7 +20,9 @@ "Menu:Emailing": "Slanje e-poštom", "Menu:TimeZone": "Vremenska Zona", "DisplayName:Timezone": "Vremenska zona", - "TimezoneHelpText": "Ova se postavka koristi za cijelu aplikaciju ili zakupce.", + "TimezoneHelpText": "Ova značajka omogućuje vam postavljanje zadanog vremenskog pojasa za vaš poslužitelj, dok korisnici mogu odabrati svoj vlastiti. Ako se vremenski pojas korisnika razlikuje od vremenskog pojasa poslužitelja, sva vremena će se prilagoditi u skladu s tim. Na primjer, kada je poslužitelj postavljen na Europa/London(00:00) i korisnik je u Europa/Pariz(+01:00), vremena će se prilagoditi za 1 sat za tog korisnika. Odabirom 'Zadani vremenski pojas' automatski će se koristiti vremenski pojas poslužitelja ili preglednika.", + "DefaultTimeZone": "Zadana vremenska zona", + "DefaultTimeZoneInfo": "Zadana vremenska zona pokušat će koristiti vremensku zonu preglednika ili poslužitelja.", "SmtpHost": "Domaćin", "SmtpPort": "Port", "SmtpUserName": "Korisničko ime", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/hu.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/hu.json index c47a813371..4aed2b5688 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/hu.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/hu.json @@ -20,7 +20,8 @@ "Menu:Emailing": "E-mailezés", "Menu:TimeZone": "Időzóna", "DisplayName:Timezone": "Időzóna", - "TimezoneHelpText": "Ez a beállítás az egész alkalmazásra vagy a bérlőre vonatkozik.", + "TimezoneHelpText": "Ez a funkció lehetővé teszi, hogy alapértelmezett időzónát állítson be a szerveréhez, miközben a felhasználók sajátot választhatnak. Ha egy felhasználó időzónája eltér a szerver időzónájától, az összes idő ennek megfelelően módosul. Például, ha a szerver Európa/London(00:00) időzónára van állítva, és egy felhasználó Európa/Párizs(+01:00) időzónában van, az időket 1 órával módosítják ennél a felhasználónál. Az 'Alapértelmezett időzóna' kiválasztása automatikusan a szerver vagy a böngésző időzónáját használja.", + "DefaultTimeZone": "Alapértelmezett időzóna", "SmtpHost": "Házigazda", "SmtpPort": "Kikötő", "SmtpUserName": "Felhasználónév", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/is.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/is.json index f39b10f795..2106b156bc 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/is.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/is.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Senda tölvupóst", "Menu:TimeZone": "Tímabelti", "DisplayName:Timezone": "Tímabelti", - "TimezoneHelpText": "Þessi stilling er notuð fyrir allt forrit eða leigjanda.", + "TimezoneHelpText": "Þessi eiginleiki gerir þér kleift að setja sjálfgefna tímabelti fyrir þjóninn þinn, á meðan notendur geta valið sitt eigið. Ef tímabelti notanda er frábrugðið tímabelti þjónsins, verða allir tímar aðlagaðir í samræmi við það. Til dæmis, þegar þjónninn er stilltur á Evrópa/London(00:00) og notandi er í Evrópa/París(+01:00), verða tímar aðlagaðir um 1 klukkutíma fyrir þann notanda. Með því að velja 'Sjálfgefið tímabelti' verður sjálfkrafa notað tímabelti þjónsins eða vafrans.", + "DefaultTimeZone": "Sjálfgefitt tímabelti", "SmtpHost": "Smtp Host", "SmtpPort": "Port", "SmtpUserName": "Notanda nafn", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/it.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/it.json index 3de6657509..487078c449 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/it.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/it.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Invio di e-mail", "Menu:TimeZone": "Fuso Orario", "DisplayName:Timezone": "Fuso orario", - "TimezoneHelpText": "Questa impostazione viene utilizzata per l'intera applicazione o in base al tenant.", + "TimezoneHelpText": "Questa funzione ti permette di impostare un fuso orario predefinito per il tuo server, mentre gli utenti possono selezionare il proprio. Se il fuso orario di un utente differisce da quello del server, tutti gli orari verranno adeguati di conseguenza. Ad esempio, con il server impostato su Europa/Londra(00:00) e un utente in Europa/Parigi(+01:00), gli orari verranno adeguati di 1 ora per quell'utente. Selezionando 'Fuso orario predefinito' verrà utilizzato automaticamente il fuso orario del server o del browser.", + "DefaultTimeZone": "Fuso orario predefinito", "SmtpHost": "Host", "SmtpPort": "Porta", "SmtpUserName": "Nome utente", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/nl.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/nl.json index a188146d0e..06f07346e5 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/nl.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/nl.json @@ -20,7 +20,8 @@ "Menu:Emailing": "E-mail", "Menu:TimeZone": "Tijdzone", "DisplayName:Timezone": "Tijdzone", - "TimezoneHelpText": "Deze instelling wordt gebruikt voor de hele toepassing of op tenantbasis.", + "TimezoneHelpText": "Deze functie stelt u in staat om een standaardtijdzone in te stellen voor uw server, terwijl gebruikers hun eigen tijdzone kunnen kiezen. Als de tijdzone van een gebruiker verschilt van die van de server, worden alle tijden dienovereenkomstig aangepast. Bijvoorbeeld, met de server ingesteld op Europa/Londen(00:00) en een gebruiker in Europa/Parijs(+01:00), worden de tijden met 1 uur aangepast voor die gebruiker. Door 'Standaardtijdzone' te selecteren wordt automatisch de tijdzone van de server of browser gebruikt.", + "DefaultTimeZone": "Standaardtijdzone", "SmtpHost": "Host", "SmtpPort": "Poort", "SmtpUserName": "Gebruikersnaam", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pl-PL.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pl-PL.json index a4bbd7238e..35038ee043 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pl-PL.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pl-PL.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Wysyłanie e-maili", "Menu:TimeZone": "Strefa Czasowa", "DisplayName:Timezone": "Strefa czasowa", - "TimezoneHelpText": "To ustawienie jest używane w przypadku całej aplikacji lub opartej na dzierżawie.", + "TimezoneHelpText": "Ta funkcja pozwala ustawić domyślną strefę czasową dla serwera, podczas gdy użytkownicy mogą wybrać własną. Jeśli strefa czasowa użytkownika różni się od strefy czasowej serwera, wszystkie czasy zostaną odpowiednio dostosowane. Na przykład, gdy serwer jest ustawiony na Europa/Londyn(00:00), a użytkownik jest w Europie/Paryżu(+01:00), czasy zostaną dostosowane o 1 godzinę dla tego użytkownika. Wybranie opcji 'Domyślna strefa czasowa' automatycznie wykorzysta strefę czasową serwera lub przeglądarki.", + "DefaultTimeZone": "Domyślna strefa czasowa", "SmtpHost": "Gospodarz", "SmtpPort": "Port", "SmtpUserName": "Nazwa użytkownika", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pt-BR.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pt-BR.json index c6c951c025..9a8cf0317b 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pt-BR.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pt-BR.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Enviando por e-mail", "Menu:TimeZone": "Fuso Horário", "DisplayName:Timezone": "Fuso horário", - "TimezoneHelpText": "Essa configuração é usada para todo o aplicativo ou com base em locatário.", + "TimezoneHelpText": "Este recurso permite que você defina um fuso horário padrão para seu servidor, enquanto os usuários podem selecionar o seu próprio. Se o fuso horário de um usuário for diferente do fuso horário do servidor, todos os horários serão ajustados de acordo. Por exemplo, com o servidor definido como Europa/Londres(00:00) e um usuário na Europa/Paris(+01:00), os horários serão ajustados em 1 hora para esse usuário. Selecionar 'Fuso horário padrão' usará automaticamente o fuso horário do servidor ou do navegador.", + "DefaultTimeZone": "Fuso horário padrão", "SmtpHost": "Hospedeiro", "SmtpPort": "Porta", "SmtpUserName": "Nome do usuário", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/ro-RO.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/ro-RO.json index 7e1eac80be..8029a43734 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/ro-RO.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/ro-RO.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Emailing", "Menu:TimeZone": "Fus Orar", "DisplayName:Timezone": "Fus orar", - "TimezoneHelpText": "Această setare este utilizată pentru aplicație sau pentru chiriași.", + "TimezoneHelpText": "Această funcționalitate vă permite să setați un fus orar implicit pentru serverul dvs., în timp ce utilizatorii pot selecta propriul lor. Dacă fusul orar al unui utilizator diferă de cel al serverului, toate orele vor fi ajustate în consecință. De exemplu, cu serverul setat la Europa/Londra(00:00) și un utilizator în Europa/Paris(+01:00), orele vor fi ajustate cu 1 oră pentru acel utilizator. Selectarea 'Fus orar implicit' va folosi automat fusul orar al serverului sau browserului.", + "DefaultTimeZone": "Fus orar implicit", "SmtpHost": "Host", "SmtpPort": "Port", "SmtpUserName": "Nume de utilizator", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/ru.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/ru.json index 9fde67ff61..0ab1535a3b 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/ru.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/ru.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Отправка по электронной почте", "Menu:TimeZone": "Часовой пояс", "DisplayName:Timezone": "Часовой пояс", - "TimezoneHelpText": "Этот параметр используется для всего приложения или на уровне клиента.", + "TimezoneHelpText": "Эта функция позволяет вам установить часовой пояс по умолчанию для вашего сервера, в то время как пользователи могут выбрать свой собственный. Если часовой пояс пользователя отличается от часового пояса сервера, все время будет соответствующим образом скорректировано. Например, если сервер установлен на Европа/Лондон(00:00), а пользователь находится в Европа/Париж(+01:00), время будет скорректировано на 1 час для этого пользователя. При выборе 'Часовой пояс по умолчанию' будет автоматически использоваться часовой пояс сервера или браузера.", + "DefaultTimeZone": "Часовой пояс по умолчанию", "SmtpHost": "Хозяин", "SmtpPort": "Порт", "SmtpUserName": "Имя пользователя", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/sk.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/sk.json index e06c4d9d0a..1c68a43ae5 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/sk.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/sk.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Posielanie emailov", "Menu:TimeZone": "Časové Pásmo", "DisplayName:Timezone": "Časové pásmo", - "TimezoneHelpText": "Toto nastavenie sa používa pre celú aplikáciu alebo pre nájomníkov.", + "TimezoneHelpText": "Táto funkcia vám umožňuje nastaviť predvolené časové pásmo pre váš server, zatiaľ čo používatelia si môžu vybrať svoje vlastné. Ak sa časové pásmo používateľa líši od časového pásma servera, všetky časy sa príslušne upravia. Napríklad, keď je server nastavený na Európa/Londýn(00:00) a používateľ je v Európa/Paríž(+01:00), časy sa pre tohto používateľa upravia o 1 hodinu. Výberom 'Predvolené časové pásmo' sa automaticky použije časové pásmo servera alebo prehliadača.", + "DefaultTimeZone": "Predvolené časové pásmo", "SmtpHost": "Host", "SmtpPort": "Port", "SmtpUserName": "Meno používateľa", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/sl.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/sl.json index 509ce74e08..4805b28994 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/sl.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/sl.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Pošiljanje po e-pošti", "Menu:TimeZone": "Časovni Pas", "DisplayName:Timezone": "Časovni pas", - "TimezoneHelpText": "Ta nastavitev se uporablja za celotno aplikacijo ali za najemnika.", + "TimezoneHelpText": "Ta funkcija vam omogoča nastavitev privzetega časovnega pasu za vaš strežnik, medtem ko lahko uporabniki izberejo svojega. Če se časovni pas uporabnika razlikuje od časovnega pasu strežnika, se vsi časi ustrezno prilagodijo. Na primer, ko je strežnik nastavljen na Evropa/London(00:00) in je uporabnik v Evropa/Pariz(+01:00), se časi za tega uporabnika prilagodijo za 1 uro. Z izbiro 'Privzeti časovni pas' se samodejno uporabi časovni pas strežnika ali brskalnika.", + "DefaultTimeZone": "Privzeto časovno pas", "SmtpHost": "Gostitelj", "SmtpPort": "pristanišče", "SmtpUserName": "Uporabniško ime", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/sv.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/sv.json index ad74a9c147..ebbdad2b32 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/sv.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/sv.json @@ -19,7 +19,8 @@ "Menu:Emailing": "E-post", "Menu:TimeZone": "Tidszon", "DisplayName:Timezone": "Tidszon", - "TimezoneHelpText": "Denna inställning används för applikationsomfattande eller hyresgästbaserad.", + "TimezoneHelpText": "Denna inställning används för applikationsomfattande eller hyresgästbaserad. Standardtidszonen kommer att försöka använda webbläsarens eller serverns tidszon.", + "DefaultTimeZone": "Standardtidszon", "SmtpHost": "Värd", "SmtpPort": "Port", "SmtpUserName": "Användarens namn", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/tr.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/tr.json index aab8928026..3b7fc2ac5f 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/tr.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/tr.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Email", "Menu:TimeZone": "Zaman Dilimi", "DisplayName:Timezone": "Zaman dilimi", - "TimezoneHelpText": "Bu ayar uygulama genelinde veya müşteri tabanlı olarak kullanılır.", + "TimezoneHelpText": "Bu özellik, kullanıcılar kendi saat dilimlerini seçebilirken, sunucunuz için varsayılan bir saat dilimi ayarlamanıza olanak tanır. Bir kullanıcının saat dilimi sunucunun saat diliminden farklıysa, tüm saatler buna göre ayarlanacaktır. Örneğin, sunucu Avrupa/Londra(00:00) olarak ayarlandığında ve bir kullanıcı Avrupa/Paris(+01:00)'te olduğunda, o kullanıcı için saatler 1 saat ayarlanacaktır. 'Varsayılan Saat Dilimi' seçimi otomatik olarak sunucunun veya tarayıcının saat dilimini kullanacaktır.", + "DefaultTimeZone": "Varsayılan saat dilimi", "SmtpHost": "Sunucu", "SmtpPort": "Port", "SmtpUserName": "Kullanıcı adı", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/vi.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/vi.json index 94acb63a71..a9cc466fdb 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/vi.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/vi.json @@ -20,7 +20,8 @@ "Menu:Emailing": "Gửi email", "Menu:TimeZone": "Múi Giờ", "DisplayName:Timezone": "Múi giờ", - "TimezoneHelpText": "Cài đặt này được sử dụng cho toàn bộ ứng dụng hoặc dựa trên đối tượng thuê.", + "TimezoneHelpText": "Tính năng này cho phép bạn đặt múi giờ mặc định cho máy chủ của mình, trong khi người dùng có thể chọn múi giờ của riêng họ. Nếu múi giờ của người dùng khác với múi giờ của máy chủ, tất cả thời gian sẽ được điều chỉnh tương ứng. Ví dụ: với máy chủ được đặt ở Châu Âu/Luân Đôn(00:00) và người dùng ở Châu Âu/Paris(+01:00), thời gian sẽ được điều chỉnh 1 giờ cho người dùng đó. Chọn 'Múi giờ mặc định' sẽ tự động sử dụng múi giờ của máy chủ hoặc trình duyệt.", + "DefaultTimeZone": "Múi giờ mặc định", "SmtpHost": "Tổ chức", "SmtpPort": "Hải cảng", "SmtpUserName": "Tên tài khoản", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/zh-Hans.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/zh-Hans.json index 339f14e292..3aad0b9e8a 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/zh-Hans.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/zh-Hans.json @@ -20,7 +20,8 @@ "Menu:Emailing": "邮件", "Menu:TimeZone": "时区", "DisplayName:Timezone": "时区", - "TimezoneHelpText": "此设置用于应用程序范围或基于租户。", + "TimezoneHelpText": "此功能允许您为服务器设置默认时区,同时用户可以选择自己的时区。如果用户的时区与服务器的时区不同,所有时间将相应调整。例如,如果服务器设置为欧洲/伦敦(00:00),而用户在欧洲/巴黎(+01:00),则该用户的时间将调整1小时。选择'默认时区'将自动使用服务器或浏览器的时区。", + "DefaultTimeZone": "默认时区", "SmtpHost": "主机", "SmtpPort": "端口", "SmtpUserName": "用户名", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/zh-Hant.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/zh-Hant.json index e3a06511c2..77c586efe4 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/zh-Hant.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/zh-Hant.json @@ -20,7 +20,8 @@ "Menu:Emailing": "信箱", "Menu:TimeZone": "時區", "DisplayName:Timezone": "時區", - "TimezoneHelpText": "此設置用於應用程序範圍或基於租戶。", + "TimezoneHelpText": "此功能允許您為伺服器設置預設時區,同時用戶可以選擇自己的時區。如果用戶的時區與伺服器的時區不同,所有時間將相應調整。例如,如果伺服器設置為歐洲/倫敦(00:00),而用戶在歐洲/巴黎(+01:00),則該用戶的時間將調整1小時。選擇'預設時區'將自動使用伺服器或瀏覽器的時區。", + "DefaultTimeZone": "預設時區", "SmtpHost": "主機", "SmtpPort": "Port", "SmtpUserName": "帳號", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManager.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManager.cs index ddf451154d..b3fd120f38 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManager.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManager.cs @@ -136,7 +136,7 @@ public class SettingManager : ISettingManager, ISingletonDependency if (providers.Count > 1 && !forceToSet && setting.IsInherited && value != null) { var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null); - if (fallbackValue == value) + if (string.Equals(fallbackValue, value, StringComparison.OrdinalIgnoreCase)) { //Clear the value if it's same as it's fallback value value = null; diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Installer/AngularInstallationInfo.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Installer/AngularInstallationInfo.json new file mode 100644 index 0000000000..987f8f72ea --- /dev/null +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Installer/AngularInstallationInfo.json @@ -0,0 +1,47 @@ +{ + "packages":[ + { + "name": "@abp/ng.setting-management", + "appRoutingModuleConfiguration":{ + "routes":[ + "{ path: 'setting-management', loadChildren: () => import('@abp/ng.setting-management').then(m => m.SettingManagementModule.forLazy()),}" + ] + }, + "appModuleConfiguration":{ + "imports":[ + { + "names":[ + "provideSettingManagementConfig" + ], + "namespace": "@abp/ng.setting-management/config" + } + ], + "ngModuleImports":[ + ], + "providerNames":[ + "provideSettingManagementConfig()" + ] + }, + "tsJsonPathRecordConfigurations":[ + { + "name": "@abp/ng.setting-management", + "paths": [ + "angular/projects/setting-management/src/public-api.ts" + ] + }, + { + "name": "@abp/ng.setting-management/config", + "paths": [ + "angular/projects/setting-management/config/src/public-api.ts" + ] + }, + { + "name": "@abp/ng.setting-management/proxy", + "paths": [ + "angular/projects/setting-management/proxy/src/public-api.ts" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Installer/Volo.Abp.SettingManagement.Installer.csproj b/modules/setting-management/src/Volo.Abp.SettingManagement.Installer/Volo.Abp.SettingManagement.Installer.csproj index c0c5281afe..7a4b257929 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Installer/Volo.Abp.SettingManagement.Installer.csproj +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Installer/Volo.Abp.SettingManagement.Installer.csproj @@ -25,6 +25,8 @@ true content\ + + diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Components/TimeZoneSettingGroup/Default.cshtml b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Components/TimeZoneSettingGroup/Default.cshtml index c146118cb3..11d9af9637 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Components/TimeZoneSettingGroup/Default.cshtml +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Components/TimeZoneSettingGroup/Default.cshtml @@ -1,18 +1,18 @@ @using Microsoft.AspNetCore.Mvc.Localization @using Volo.Abp.SettingManagement.Localization @inject IHtmlLocalizer L -@model Volo.Abp.SettingManagement.Web.Pages.SettingManagement.Components.TimeZoneSettingGroup.TimeZoneSettingGroupViewComponent.UpdateTimezoneSettingsViewModel -
- + +
+
@L["TimezoneHelpText"].Value
- + @L["Save"]
diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Components/TimeZoneSettingGroup/Default.js b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Components/TimeZoneSettingGroup/Default.js index d6e720dbe3..aebbe66352 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Components/TimeZoneSettingGroup/Default.js +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Components/TimeZoneSettingGroup/Default.js @@ -3,13 +3,28 @@ var l = abp.localization.getResource('AbpSettingManagement'); + var select2 = null; + volo.abp.settingManagement.timeZoneSettings.getTimezones().then(function (result) { + var data = $.map(result, function (obj) { + obj.id = obj.value; + obj.text = obj.name; + return obj; + }); + + select2 = $("#timezone-select").select2({ + data: data + }); + + volo.abp.settingManagement.timeZoneSettings.get().then(function (result) { + select2.val(result).trigger("change"); + }); + }); + $("#TimeZoneSettingsForm").on('submit', function (event) { event.preventDefault(); - - volo.abp.settingManagement.timeZoneSettings.update($("#Timezone").val()).then(function (result) { + volo.abp.settingManagement.timeZoneSettings.update($("#timezone-select").val()).then(function (result) { $(document).trigger("AbpSettingSaved"); }); - }); }); })(jQuery); diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Components/TimeZoneSettingGroup/TimeZoneSettingGroupViewComponent.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Components/TimeZoneSettingGroup/TimeZoneSettingGroupViewComponent.cs index eaf24ab2f2..9c22152a6f 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Components/TimeZoneSettingGroup/TimeZoneSettingGroupViewComponent.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/Components/TimeZoneSettingGroup/TimeZoneSettingGroupViewComponent.cs @@ -1,42 +1,18 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; +using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Rendering; using Volo.Abp.AspNetCore.Mvc; -using Volo.Abp.Auditing; -using Volo.Abp.Timing.Localization.Resources.AbpTiming; namespace Volo.Abp.SettingManagement.Web.Pages.SettingManagement.Components.TimeZoneSettingGroup; public class TimeZoneSettingGroupViewComponent : AbpViewComponent { - protected ITimeZoneSettingsAppService TimeZoneSettingsAppService { get; } - public TimeZoneSettingGroupViewComponent(ITimeZoneSettingsAppService timeZoneSettingsAppService) { ObjectMapperContext = typeof(AbpSettingManagementWebModule); - TimeZoneSettingsAppService = timeZoneSettingsAppService; } public virtual async Task InvokeAsync() { - var timezone = await TimeZoneSettingsAppService.GetAsync(); - var timezones = await TimeZoneSettingsAppService.GetTimezonesAsync(); - var model = new UpdateTimezoneSettingsViewModel() - { - Timezone = timezone, - TimeZoneItems = new List() - }; - model.TimeZoneItems.AddRange(timezones.Select(x => new SelectListItem(x.Name, x.Value)).ToList()); - return View("~/Pages/SettingManagement/Components/TimeZoneSettingGroup/Default.cshtml", model); - } - - public class UpdateTimezoneSettingsViewModel - { - public string Timezone { get; set; } - - public List TimeZoneItems { get; set; } + return View("~/Pages/SettingManagement/Components/TimeZoneSettingGroup/Default.cshtml"); } } diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo.Abp.SettingManagement.MongoDB.Tests.csproj b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo.Abp.SettingManagement.MongoDB.Tests.csproj index 9c2c2f36a9..9bbb27387d 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo.Abp.SettingManagement.MongoDB.Tests.csproj +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo.Abp.SettingManagement.MongoDB.Tests.csproj @@ -19,9 +19,9 @@ - - - + + + diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Installer/AngularInstallationInfo.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Installer/AngularInstallationInfo.json new file mode 100644 index 0000000000..700380b9ca --- /dev/null +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Installer/AngularInstallationInfo.json @@ -0,0 +1,47 @@ +{ + "packages":[ + { + "name": "@abp/ng.tenant-management", + "appRoutingModuleConfiguration":{ + "routes":[ + "{ path: 'tenant-management', loadChildren: () => import('@abp/ng.tenant-management').then(m => m.TenantManagementModule.forLazy()),}" + ] + }, + "appModuleConfiguration":{ + "imports":[ + { + "names":[ + "provideTenantManagementConfig" + ], + "namespace": "@abp/ng.tenant-management/config" + } + ], + "ngModuleImports":[ + ], + "providerNames":[ + "provideTenantManagementConfig()" + ] + }, + "tsJsonPathRecordConfigurations":[ + { + "name": "@abp/ng.tenant-management", + "paths": [ + "angular/projects/tenant-management/src/public-api.ts" + ] + }, + { + "name": "@abp/ng.tenant-management/config", + "paths": [ + "angular/projects/tenant-management/config/src/public-api.ts" + ] + }, + { + "name": "@abp/ng.tenant-management/proxy", + "paths": [ + "angular/projects/tenant-management/proxy/src/public-api.ts" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Installer/Volo.Abp.TenantManagement.Installer.csproj b/modules/tenant-management/src/Volo.Abp.TenantManagement.Installer/Volo.Abp.TenantManagement.Installer.csproj index 1375f5bfe7..af992ef8de 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Installer/Volo.Abp.TenantManagement.Installer.csproj +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Installer/Volo.Abp.TenantManagement.Installer.csproj @@ -25,6 +25,8 @@ true content\
+ + diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo.Abp.TenantManagement.MongoDB.Tests.csproj b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo.Abp.TenantManagement.MongoDB.Tests.csproj index 2913a0ed53..4ad25e0218 100644 --- a/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo.Abp.TenantManagement.MongoDB.Tests.csproj +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo.Abp.TenantManagement.MongoDB.Tests.csproj @@ -19,9 +19,9 @@ - - - + + + diff --git a/modules/virtual-file-explorer/app/DemoApp.abppkg.analyze.json b/modules/virtual-file-explorer/app/DemoApp.abppkg.analyze.json new file mode 100644 index 0000000000..6adec15576 --- /dev/null +++ b/modules/virtual-file-explorer/app/DemoApp.abppkg.analyze.json @@ -0,0 +1,158 @@ +{ + "name": "DemoApp", + "hash": "", + "contents": [ + { + "namespace": "DemoApp", + "dependsOnModules": [ + { + "declaringAssemblyName": "Volo.Abp.AspNetCore.Mvc", + "namespace": "Volo.Abp.AspNetCore.Mvc", + "name": "AbpAspNetCoreMvcModule" + }, + { + "declaringAssemblyName": "Volo.Abp.Autofac", + "namespace": "Volo.Abp.Autofac", + "name": "AbpAutofacModule" + }, + { + "declaringAssemblyName": "Volo.Abp.AutoMapper", + "namespace": "Volo.Abp.AutoMapper", + "name": "AbpAutoMapperModule" + }, + { + "declaringAssemblyName": "Volo.Abp.Swashbuckle", + "namespace": "Volo.Abp.Swashbuckle", + "name": "AbpSwashbuckleModule" + }, + { + "declaringAssemblyName": "Volo.Abp.AspNetCore.Serilog", + "namespace": "Volo.Abp.AspNetCore.Serilog", + "name": "AbpAspNetCoreSerilogModule" + }, + { + "declaringAssemblyName": "Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic", + "namespace": "Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic", + "name": "AbpAspNetCoreMvcUiBasicThemeModule" + }, + { + "declaringAssemblyName": "Volo.Abp.VirtualFileExplorer.Web", + "namespace": "Volo.Abp.VirtualFileExplorer.Web", + "name": "AbpVirtualFileExplorerWebModule" + }, + { + "declaringAssemblyName": "Volo.Abp.Account.Web", + "namespace": "Volo.Abp.Account.Web", + "name": "AbpAccountWebModule" + }, + { + "declaringAssemblyName": "Volo.Abp.Account.HttpApi", + "namespace": "Volo.Abp.Account", + "name": "AbpAccountHttpApiModule" + }, + { + "declaringAssemblyName": "Volo.Abp.Account.Application", + "namespace": "Volo.Abp.Account", + "name": "AbpAccountApplicationModule" + }, + { + "declaringAssemblyName": "Volo.Abp.PermissionManagement.Domain.Identity", + "namespace": "Volo.Abp.PermissionManagement.Identity", + "name": "AbpPermissionManagementDomainIdentityModule" + }, + { + "declaringAssemblyName": "Volo.Abp.Identity.Web", + "namespace": "Volo.Abp.Identity.Web", + "name": "AbpIdentityWebModule" + }, + { + "declaringAssemblyName": "Volo.Abp.Identity.HttpApi", + "namespace": "Volo.Abp.Identity", + "name": "AbpIdentityHttpApiModule" + }, + { + "declaringAssemblyName": "Volo.Abp.Identity.Application", + "namespace": "Volo.Abp.Identity", + "name": "AbpIdentityApplicationModule" + }, + { + "declaringAssemblyName": "Volo.Abp.Identity.EntityFrameworkCore", + "namespace": "Volo.Abp.Identity.EntityFrameworkCore", + "name": "AbpIdentityEntityFrameworkCoreModule" + }, + { + "declaringAssemblyName": "Volo.Abp.PermissionManagement.Web", + "namespace": "Volo.Abp.PermissionManagement.Web", + "name": "AbpPermissionManagementWebModule" + }, + { + "declaringAssemblyName": "Volo.Abp.PermissionManagement.Application", + "namespace": "Volo.Abp.PermissionManagement", + "name": "AbpPermissionManagementApplicationModule" + }, + { + "declaringAssemblyName": "Volo.Abp.PermissionManagement.HttpApi", + "namespace": "Volo.Abp.PermissionManagement.HttpApi", + "name": "AbpPermissionManagementHttpApiModule" + }, + { + "declaringAssemblyName": "Volo.Abp.PermissionManagement.EntityFrameworkCore", + "namespace": "Volo.Abp.PermissionManagement.EntityFrameworkCore", + "name": "AbpPermissionManagementEntityFrameworkCoreModule" + }, + { + "declaringAssemblyName": "Volo.Abp.EntityFrameworkCore.SqlServer", + "namespace": "Volo.Abp.EntityFrameworkCore.SqlServer", + "name": "AbpEntityFrameworkCoreSqlServerModule" + } + ], + "implementingInterfaces": [ + { + "name": "IAbpModule", + "namespace": "Volo.Abp.Modularity", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.Modularity.IAbpModule" + }, + { + "name": "IOnPreApplicationInitialization", + "namespace": "Volo.Abp.Modularity", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.Modularity.IOnPreApplicationInitialization" + }, + { + "name": "IOnApplicationInitialization", + "namespace": "Volo.Abp", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.IOnApplicationInitialization" + }, + { + "name": "IOnPostApplicationInitialization", + "namespace": "Volo.Abp.Modularity", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.Modularity.IOnPostApplicationInitialization" + }, + { + "name": "IOnApplicationShutdown", + "namespace": "Volo.Abp", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.IOnApplicationShutdown" + }, + { + "name": "IPreConfigureServices", + "namespace": "Volo.Abp.Modularity", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.Modularity.IPreConfigureServices" + }, + { + "name": "IPostConfigureServices", + "namespace": "Volo.Abp.Modularity", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.Modularity.IPostConfigureServices" + } + ], + "contentType": "abpModule", + "name": "DemoAppModule", + "summary": null + } + ] +} \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/package.json b/modules/virtual-file-explorer/app/package.json index 999c375067..9bd234644d 100644 --- a/modules/virtual-file-explorer/app/package.json +++ b/modules/virtual-file-explorer/app/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0", - "@abp/virtual-file-explorer": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2", + "@abp/virtual-file-explorer": "~9.2.0-rc.2" } } diff --git a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Contracts/Volo.Abp.VirtualFileExplorer.Contracts.abppkg.analyze.json b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Contracts/Volo.Abp.VirtualFileExplorer.Contracts.abppkg.analyze.json index 0f37f00591..65361601a2 100644 --- a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Contracts/Volo.Abp.VirtualFileExplorer.Contracts.abppkg.analyze.json +++ b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Contracts/Volo.Abp.VirtualFileExplorer.Contracts.abppkg.analyze.json @@ -1,7 +1,57 @@ { - "name": "Volo.Abp.VirtualFileExplorer.Contracts", - "hash": "", - "contents": [ - - ] - } \ No newline at end of file + "name": "Volo.Abp.VirtualFileExplorer.Contracts", + "hash": "", + "contents": [ + { + "namespace": "Volo.Abp.VirtualFileExplorer", + "dependsOnModules": [], + "implementingInterfaces": [ + { + "name": "IAbpModule", + "namespace": "Volo.Abp.Modularity", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.Modularity.IAbpModule" + }, + { + "name": "IOnPreApplicationInitialization", + "namespace": "Volo.Abp.Modularity", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.Modularity.IOnPreApplicationInitialization" + }, + { + "name": "IOnApplicationInitialization", + "namespace": "Volo.Abp", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.IOnApplicationInitialization" + }, + { + "name": "IOnPostApplicationInitialization", + "namespace": "Volo.Abp.Modularity", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.Modularity.IOnPostApplicationInitialization" + }, + { + "name": "IOnApplicationShutdown", + "namespace": "Volo.Abp", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.IOnApplicationShutdown" + }, + { + "name": "IPreConfigureServices", + "namespace": "Volo.Abp.Modularity", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.Modularity.IPreConfigureServices" + }, + { + "name": "IPostConfigureServices", + "namespace": "Volo.Abp.Modularity", + "declaringAssemblyName": "Volo.Abp.Core", + "fullName": "Volo.Abp.Modularity.IPostConfigureServices" + } + ], + "contentType": "abpModule", + "name": "AbpVirtualFileExplorerContractsModule", + "summary": null + } + ] +} \ No newline at end of file diff --git a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.abppkg.analyze.json b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.abppkg.analyze.json index 8665e359e5..2b6f122188 100644 --- a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.abppkg.analyze.json +++ b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.abppkg.analyze.json @@ -14,6 +14,11 @@ "declaringAssemblyName": "Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", "namespace": "Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", "name": "AbpAspNetCoreMvcUiThemeSharedModule" + }, + { + "declaringAssemblyName": "Volo.Abp.VirtualFileExplorer.Contracts", + "namespace": "Volo.Abp.VirtualFileExplorer", + "name": "AbpVirtualFileExplorerContractsModule" } ], "implementingInterfaces": [ diff --git a/npm/lerna.json b/npm/lerna.json index db2bd7024a..8cf6527c13 100644 --- a/npm/lerna.json +++ b/npm/lerna.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "packages": [ "packs/*" ], diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index 5fba83180d..e17d6c6f18 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -44,8 +44,8 @@ }, "private": true, "devDependencies": { - "@abp/ng.theme.lepton-x": "~4.1.0", - "@abp/utils": "~9.1.0", + "@abp/ng.theme.lepton-x": "~4.2.0-rc.2", + "@abp/utils": "~9.2.0-rc.2", "@angular-devkit/build-angular": "~19.1.0", "@angular-devkit/core": "~19.1.0", "@angular-devkit/schematics": "~19.1.0", diff --git a/npm/ng-packs/packages/account-core/package.json b/npm/ng-packs/packages/account-core/package.json index ba4538c0fe..de48838320 100644 --- a/npm/ng-packs/packages/account-core/package.json +++ b/npm/ng-packs/packages/account-core/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.account.core", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.core": "~9.1.0", - "@abp/ng.theme.shared": "~9.1.0", + "@abp/ng.core": "~9.2.0-rc.2", + "@abp/ng.theme.shared": "~9.2.0-rc.2", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/account/config/src/providers/route.provider.ts b/npm/ng-packs/packages/account/config/src/providers/route.provider.ts index b152237503..15baeae9f8 100644 --- a/npm/ng-packs/packages/account/config/src/providers/route.provider.ts +++ b/npm/ng-packs/packages/account/config/src/providers/route.provider.ts @@ -24,12 +24,14 @@ export function configureRoutes() { path: '/account/login', name: eAccountRouteNames.Login, parentName: eAccountRouteNames.Account, + layout: eLayoutType.account, order: 1, }, { path: '/account/register', name: eAccountRouteNames.Register, parentName: eAccountRouteNames.Account, + layout: eLayoutType.account, order: 2, }, { @@ -45,12 +47,14 @@ export function configureRoutes() { path: '/account/forgot-password', parentName: eAccountRouteNames.Account, name: eAccountRouteNames.ForgotPassword, + layout: eLayoutType.account, invisible: true, }, { path: '/account/reset-password', parentName: eAccountRouteNames.Account, name: eAccountRouteNames.ResetPassword, + layout: eLayoutType.account, invisible: true, }, ]); diff --git a/npm/ng-packs/packages/account/package.json b/npm/ng-packs/packages/account/package.json index 99a5dccfd5..5d382cc031 100644 --- a/npm/ng-packs/packages/account/package.json +++ b/npm/ng-packs/packages/account/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.account", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.account.core": "~9.1.0", - "@abp/ng.theme.shared": "~9.1.0", + "@abp/ng.account.core": "~9.2.0-rc.2", + "@abp/ng.theme.shared": "~9.2.0-rc.2", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form-prop.component.html b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form-prop.component.html index b5da3a266f..b0d8f42bc7 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form-prop.component.html +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form-prop.component.html @@ -22,7 +22,7 @@ /> } @case ('hidden') { - + } @case ('checkbox') {
@@ -62,24 +62,11 @@ } @case ('multiselect') { - + [abpDisabled]="disabled"/> } @case ('typeahead') { @@ -100,7 +87,7 @@ [class.is-invalid]="typeahead.classList.contains('is-invalid')" class="form-control" /> - +
} @case ('date') { @@ -122,7 +109,7 @@ } @case ('dateTime') { - + } @case ('textarea') { diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form-prop.component.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form-prop.component.ts index d6e9dba268..401bd15c3d 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form-prop.component.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-form/extensible-form-prop.component.ts @@ -49,12 +49,14 @@ import { NgxValidateCoreModule } from '@ngx-validate/core'; import { ExtensibleFormPropService } from '../../services/extensible-form-prop.service'; import { CommonModule } from '@angular/common'; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ExtensibleFormMultiselectComponent } from '../multi-select/extensible-form-multiselect.component'; @Component({ selector: 'abp-extensible-form-prop', templateUrl: './extensible-form-prop.component.html', imports: [ ExtensibleDateTimePickerComponent, + ExtensibleFormMultiselectComponent, NgbDatepickerModule, NgbTimepickerModule, ReactiveFormsModule, diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html index 471aad0f15..cca0f284f9 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html @@ -28,7 +28,7 @@ @@ -49,18 +49,33 @@ @if (!row['_' + prop.name].component) { -
+ } @else { +
+ [ngClass]="entityPropTypeClasses[prop.type]" + [class.pointer]="prop.action" + > + } } @else { ' @@ -156,12 +154,6 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn switch (prop.type) { case ePropType.Boolean: return this.getIcon(value); - case ePropType.Date: - return this.getDate(value, getShortDateFormat(this.config)); - case ePropType.Time: - return this.getDate(value, getShortTimeFormat(this.config)); - case ePropType.DateTime: - return this.getDate(value, getShortDateShortTimeFormat(this.config)); case ePropType.Enum: return this.getEnum(value, prop.enumList || []); default: @@ -196,6 +188,10 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn provide: PROP_DATA_STREAM, useValue: value, }, + { + provide: ROW_RECORD, + useValue: record, + }, ], parent: this.#injector, }); @@ -231,8 +227,8 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn ngAfterViewInit(): void { this.list?.requestStatus$?.pipe(filter(status => status === 'loading')).subscribe(() => { - this.data = []; - this.cdr.markForCheck(); - }); + this.data = []; + this.cdr.markForCheck(); + }); } } diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/index.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/index.ts index 17f1c380d1..149043089b 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/index.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/index.ts @@ -5,3 +5,4 @@ export * from './extensible-table/extensible-table.component'; export * from './date-time-picker/extensible-date-time-picker.component'; export * from './grid-actions/grid-actions.component'; export * from './page-toolbar/page-toolbar.component'; +export * from './multi-select/extensible-form-multiselect.component'; diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/multi-select/extensible-form-multiselect.component.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/multi-select/extensible-form-multiselect.component.ts new file mode 100644 index 0000000000..d55d55614d --- /dev/null +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/multi-select/extensible-form-multiselect.component.ts @@ -0,0 +1,77 @@ +import { Component, ChangeDetectionStrategy, forwardRef, input } from '@angular/core'; +import { NG_VALUE_ACCESSOR, ControlValueAccessor, ReactiveFormsModule } from '@angular/forms'; +import { CommonModule } from '@angular/common'; +import { ABP, LocalizationModule } from '@abp/ng.core'; +import { FormProp } from '../../models/form-props'; + +const EXTENSIBLE_FORM_MULTI_SELECT_CONTROL_VALUE_ACCESSOR = { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ExtensibleFormMultiselectComponent), + multi: true, +}; + +@Component({ + selector: 'abp-extensible-form-multi-select', + template: ` +
+ @for (option of options(); track option.value) { +
+ + @if (prop().isExtra) { + {{ '::' + option.key | abpLocalization }} + } @else { + {{ option.key }} + } +
+ } +
+ `, + providers: [EXTENSIBLE_FORM_MULTI_SELECT_CONTROL_VALUE_ACCESSOR], + imports: [LocalizationModule, CommonModule, ReactiveFormsModule], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ExtensibleFormMultiselectComponent implements ControlValueAccessor { + prop = input.required(); + options = input.required[]>(); + + selectedValues: any[] = []; + disabled = false; + + private onChange: (value: any) => void = () => {}; + private onTouched: () => void = () => {}; + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + isChecked(value: any): boolean { + return this.selectedValues.includes(value); + } + + onCheckboxChange(value: any, checked: boolean): void { + this.selectedValues = checked + ? [...this.selectedValues, value] + : this.selectedValues.filter(item => item !== value); + + this.onChange(this.selectedValues); + this.onTouched(); + } + + writeValue(value: any[]): void { + this.selectedValues = Array.isArray(value) ? [...value] : []; + } + + registerOnChange(fn: any): void { + this.onChange = fn; + } + + registerOnTouched(fn: any): void { + this.onTouched = fn; + } +} diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/multi-select/index.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/multi-select/index.ts new file mode 100644 index 0000000000..3a85d672ab --- /dev/null +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/multi-select/index.ts @@ -0,0 +1 @@ +export * from './extensible-form-multiselect.component'; diff --git a/npm/ng-packs/packages/components/extensible/src/lib/extensible.module.ts b/npm/ng-packs/packages/components/extensible/src/lib/extensible.module.ts index 99bc25b6f4..26741c2832 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/extensible.module.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/extensible.module.ts @@ -16,6 +16,7 @@ import { GridActionsComponent, PageToolbarComponent, ExtensibleDateTimePickerComponent, + ExtensibleFormMultiselectComponent, } from './components'; import { PropDataDirective } from './directives/prop-data.directive'; import { CreateInjectorPipe } from './pipes/create-injector.pipe'; @@ -31,7 +32,9 @@ const importWithExport = [ CreateInjectorPipe, ExtensibleFormComponent, ExtensibleTableComponent, + ExtensibleFormMultiselectComponent, ]; + @NgModule({ declarations: [], imports: [ diff --git a/npm/ng-packs/packages/components/extensible/src/lib/tokens/extensions.token.ts b/npm/ng-packs/packages/components/extensible/src/lib/tokens/extensions.token.ts index 0a2df45a00..bafaf29908 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/tokens/extensions.token.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/tokens/extensions.token.ts @@ -18,6 +18,9 @@ export const PROP_DATA_STREAM = new InjectionToken>('PROP_DATA_S type EntityPropTypeClassMap = { [key in ePropType]: string; }; + +export const ROW_RECORD = new InjectionToken('ROW_RECORD'); + export type EntityPropTypeClass = Partial; export const ENTITY_PROP_TYPE_CLASSES = new InjectionToken( 'ENTITY_PROP_TYPE_CLASSES', diff --git a/npm/ng-packs/packages/components/extensible/src/public-api.ts b/npm/ng-packs/packages/components/extensible/src/public-api.ts index 35e4696d49..e5c7883738 100644 --- a/npm/ng-packs/packages/components/extensible/src/public-api.ts +++ b/npm/ng-packs/packages/components/extensible/src/public-api.ts @@ -4,6 +4,7 @@ export * from './lib/components/extensible-form/extensible-form.component'; export * from './lib/components/extensible-table/extensible-table.component'; export * from './lib/components/grid-actions/grid-actions.component'; export * from './lib/components/page-toolbar/page-toolbar.component'; +export * from './lib/components/multi-select'; export * from './lib/constants/extra-properties'; export * from './lib/directives/prop-data.directive'; export * from './lib/enums/props.enum'; diff --git a/npm/ng-packs/packages/components/package.json b/npm/ng-packs/packages/components/package.json index bbf92e8243..820e93ac26 100644 --- a/npm/ng-packs/packages/components/package.json +++ b/npm/ng-packs/packages/components/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.components", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "peerDependencies": { - "@abp/ng.core": ">=9.1.0", - "@abp/ng.theme.shared": ">=9.1.0" + "@abp/ng.core": ">=9.2.0-rc.2", + "@abp/ng.theme.shared": ">=9.2.0-rc.2" }, "dependencies": { "chart.js": "^3.5.1", diff --git a/npm/ng-packs/packages/core/ng-package.json b/npm/ng-packs/packages/core/ng-package.json index b73dba2fb7..9f9100d2fa 100644 --- a/npm/ng-packs/packages/core/ng-package.json +++ b/npm/ng-packs/packages/core/ng-package.json @@ -9,6 +9,7 @@ "angular-oauth2-oidc", "just-compare", "just-clone", - "ts-toolbelt" + "ts-toolbelt", + "luxon" ] } diff --git a/npm/ng-packs/packages/core/package.json b/npm/ng-packs/packages/core/package.json index 10a52ad895..7a10820ead 100644 --- a/npm/ng-packs/packages/core/package.json +++ b/npm/ng-packs/packages/core/package.json @@ -1,17 +1,18 @@ { "name": "@abp/ng.core", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/utils": "~9.1.0", + "@abp/utils": "~9.2.0-rc.2", "just-clone": "^6.0.0", "just-compare": "^2.0.0", "ts-toolbelt": "^9.0.0", - "tslib": "^2.0.0" + "tslib": "^2.0.0", + "luxon": "^3.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts index 83881a5246..4505253aba 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -3,6 +3,7 @@ import { provideHttpClient, withInterceptorsFromDi, withXsrfConfiguration, + HTTP_INTERCEPTORS, } from '@angular/common/http'; import { ModuleWithProviders, NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @@ -30,6 +31,7 @@ import { ShortTimePipe } from './pipes/short-time.pipe'; import { ShortDatePipe } from './pipes/short-date.pipe'; import { SafeHtmlPipe } from './pipes/safe-html.pipe'; import { provideAbpCoreChild, provideAbpCore, withOptions } from './providers'; +import { UtcToLocalPipe } from './pipes'; const standaloneDirectives = [ AutofocusDirective, @@ -72,6 +74,7 @@ const standaloneDirectives = [ ReactiveFormsModule, RouterModule, LocalizationModule, + UtcToLocalPipe, ...standaloneDirectives, ], declarations: [ diff --git a/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts b/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts index 342c73b572..82ff84d8a5 100644 --- a/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts +++ b/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts @@ -7,7 +7,7 @@ import { } from '@angular/router'; import { HttpErrorResponse } from '@angular/common/http'; import { Observable, of } from 'rxjs'; -import { tap } from 'rxjs/operators'; +import { filter, take, tap } from 'rxjs/operators'; import { AuthService, IAbpGuard } from '../abstracts'; import { findRoute, getRoutePath } from '../utils/route-utils'; import { RoutesService, PermissionService, HttpErrorReporterService } from '../services'; @@ -32,9 +32,13 @@ export class PermissionGuard implements IAbpGuard { requiredPolicy = routeFound?.requiredPolicy; } - if (!requiredPolicy) return of(true); + if (!requiredPolicy) { + return of(true); + } return this.permissionService.getGrantedPolicy$(requiredPolicy).pipe( + filter(Boolean), + take(1), tap(access => { if (!access && this.authService.isAuthenticated) { this.httpErrorReporter.reportError({ status: 403 } as HttpErrorResponse); @@ -61,9 +65,13 @@ export const permissionGuard: CanActivateFn = ( requiredPolicy = routeFound?.requiredPolicy; } - if (!requiredPolicy) return of(true); + if (!requiredPolicy) { + return of(true); + } return permissionService.getGrantedPolicy$(requiredPolicy).pipe( + filter(Boolean), + take(1), tap(access => { if (!access && authService.isAuthenticated) { httpErrorReporter.reportError({ status: 403 } as HttpErrorResponse); diff --git a/npm/ng-packs/packages/core/src/lib/interceptors/index.ts b/npm/ng-packs/packages/core/src/lib/interceptors/index.ts index d7479a7684..ddcc17e23d 100644 --- a/npm/ng-packs/packages/core/src/lib/interceptors/index.ts +++ b/npm/ng-packs/packages/core/src/lib/interceptors/index.ts @@ -1 +1,2 @@ export * from './api.interceptor'; +export * from './timezone.interceptor'; diff --git a/npm/ng-packs/packages/core/src/lib/interceptors/timezone.interceptor.ts b/npm/ng-packs/packages/core/src/lib/interceptors/timezone.interceptor.ts new file mode 100644 index 0000000000..7e3a887361 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/interceptors/timezone.interceptor.ts @@ -0,0 +1,26 @@ +import { inject, Injectable } from '@angular/core'; +import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; +import { TimezoneService } from '../services'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root', +}) +export class TimezoneInterceptor implements HttpInterceptor { + protected readonly timezoneService = inject(TimezoneService); + + intercept(req: HttpRequest, next: HttpHandler): Observable> { + if (!this.timezoneService.isUtcClockEnabled) { + return next.handle(req); + } + const timezone = this.timezoneService.timezone; + if (timezone) { + req = req.clone({ + setHeaders: { + __timezone: timezone, + }, + }); + } + return next.handle(req); + } +} diff --git a/npm/ng-packs/packages/core/src/lib/localization.module.ts b/npm/ng-packs/packages/core/src/lib/localization.module.ts index 315f27b1ff..9335e944fa 100644 --- a/npm/ng-packs/packages/core/src/lib/localization.module.ts +++ b/npm/ng-packs/packages/core/src/lib/localization.module.ts @@ -1,8 +1,10 @@ import { NgModule } from '@angular/core'; import { LocalizationPipe } from './pipes/localization.pipe'; +import { LazyLocalizationPipe } from './pipes'; @NgModule({ - exports: [LocalizationPipe], + imports: [LazyLocalizationPipe], + exports: [LocalizationPipe, LazyLocalizationPipe], declarations: [LocalizationPipe], }) export class LocalizationModule {} diff --git a/npm/ng-packs/packages/core/src/lib/pipes/index.ts b/npm/ng-packs/packages/core/src/lib/pipes/index.ts index 1a981ca041..cbfc8f77e9 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/index.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/index.ts @@ -5,3 +5,5 @@ export * from './to-injector.pipe'; export * from './short-date.pipe'; export * from './short-time.pipe'; export * from './short-date-time.pipe'; +export * from './utc-to-local.pipe'; +export * from './lazy-localization.pipe'; diff --git a/npm/ng-packs/packages/core/src/lib/pipes/lazy-localization.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/lazy-localization.pipe.ts new file mode 100644 index 0000000000..b715b25ad8 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/pipes/lazy-localization.pipe.ts @@ -0,0 +1,41 @@ +import { inject, Injectable, Pipe, PipeTransform } from '@angular/core'; +import { + Observable, + of, + filter, + take, + switchMap, + map, + startWith, + distinctUntilChanged, +} from 'rxjs'; +import { ConfigStateService, LocalizationService } from '../services'; + +@Injectable() +@Pipe({ + name: 'abpLazyLocalization', +}) +export class LazyLocalizationPipe implements PipeTransform { + private localizationService = inject(LocalizationService); + private configStateService = inject(ConfigStateService); + + transform(key: string, ...params: (string | string[])[]): Observable { + if (!key) { + return of(''); + } + + const flatParams = params.reduce( + (acc, val) => (Array.isArray(val) ? acc.concat(val) : [...acc, val]), + [], + ); + + return this.configStateService.getAll$().pipe( + filter(config => !!config.localization), + take(1), + switchMap(() => this.localizationService.get(key, ...flatParams)), + map(translation => (translation && translation !== key ? translation : '')), + startWith(''), + distinctUntilChanged(), + ); + } +} diff --git a/npm/ng-packs/packages/core/src/lib/pipes/utc-to-local.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/utc-to-local.pipe.ts new file mode 100644 index 0000000000..1f06aba74f --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/pipes/utc-to-local.pipe.ts @@ -0,0 +1,50 @@ +import { Pipe, PipeTransform, Injectable, inject, LOCALE_ID } from '@angular/core'; +import { ConfigStateService, LocalizationService, TimeService, TimezoneService } from '../services'; +import { getShortDateFormat, getShortDateShortTimeFormat, getShortTimeFormat } from '../utils'; + +@Injectable() +@Pipe({ + name: 'abpUtcToLocal', +}) +export class UtcToLocalPipe implements PipeTransform { + protected readonly timezoneService = inject(TimezoneService); + protected readonly timeService = inject(TimeService); + protected readonly configState = inject(ConfigStateService); + protected readonly localizationService = inject(LocalizationService); + protected readonly locale = inject(LOCALE_ID); + + transform( + value: string | Date | null | undefined, + type: 'date' | 'datetime' | 'time', + ): string | Date { + if (!value) return ''; + + const date = new Date(value); + if (isNaN(date.getTime())) return ''; + + const format = this.getFormat(type); + + try { + if (this.timezoneService.isUtcClockEnabled) { + const timeZone = this.timezoneService.timezone; + return this.timeService.formatDateWithStandardOffset(date, format, timeZone); + } else { + return this.timeService.formatWithoutTimeZone(date, format); + } + } catch (err) { + return value; + } + } + + private getFormat(propType: 'date' | 'datetime' | 'time'): string { + switch (propType) { + case 'date': + return getShortDateFormat(this.configState); + case 'time': + return getShortTimeFormat(this.configState); + case 'datetime': + default: + return getShortDateShortTimeFormat(this.configState); + } + } +} diff --git a/npm/ng-packs/packages/core/src/lib/providers/core-module-config.provider.ts b/npm/ng-packs/packages/core/src/lib/providers/core-module-config.provider.ts index 2b44117acb..fafb51ff6d 100644 --- a/npm/ng-packs/packages/core/src/lib/providers/core-module-config.provider.ts +++ b/npm/ng-packs/packages/core/src/lib/providers/core-module-config.provider.ts @@ -1,6 +1,7 @@ import { makeEnvironmentProviders, Provider, inject, provideAppInitializer } from '@angular/core'; import { TitleStrategy } from '@angular/router'; import { + HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi, withXsrfConfiguration, @@ -24,6 +25,7 @@ import { DEFAULT_DYNAMIC_LAYOUTS } from '../constants'; import { LocalizationService, LocalStorageListenerService, AbpTitleStrategy } from '../services'; import { DefaultQueueManager, getInitialData, localeInitializer } from '../utils'; import { CookieLanguageProvider, IncludeLocalizationResourcesProvider, LocaleProvider } from './'; +import { TimezoneInterceptor } from '../interceptors'; export enum CoreFeatureKind { Options, @@ -128,6 +130,11 @@ export function provideAbpCore(...features: CoreFeature[]) { provide: TitleStrategy, useExisting: AbpTitleStrategy, }, + { + provide: HTTP_INTERCEPTORS, + useClass: TimezoneInterceptor, + multi: true, + }, ]; for (const feature of features) { diff --git a/npm/ng-packs/packages/core/src/lib/services/index.ts b/npm/ng-packs/packages/core/src/lib/services/index.ts index c47bef3106..6b006be74e 100644 --- a/npm/ng-packs/packages/core/src/lib/services/index.ts +++ b/npm/ng-packs/packages/core/src/lib/services/index.ts @@ -23,3 +23,5 @@ export * from './window.service'; export * from './internet-connection-service'; export * from './local-storage-listener.service'; export * from './title-strategy.service'; +export * from './timezone.service'; +export * from './time.service'; diff --git a/npm/ng-packs/packages/core/src/lib/services/time.service.ts b/npm/ng-packs/packages/core/src/lib/services/time.service.ts new file mode 100644 index 0000000000..4161d55d07 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/services/time.service.ts @@ -0,0 +1,107 @@ +import { inject, Injectable, LOCALE_ID } from '@angular/core'; +import { DateTime } from 'luxon'; + +@Injectable({ + providedIn: 'root', +}) +export class TimeService { + private locale = inject(LOCALE_ID); + + /** + * Returns the current date and time in the specified timezone. + * + * @param zone - An IANA timezone name (e.g., 'Europe/Istanbul', 'UTC'); defaults to the system's local timezone. + * @returns A Luxon DateTime instance representing the current time in the given timezone. + */ + now(zone = 'local'): DateTime { + return DateTime.now().setZone(zone); + } + + /** + * Converts the input date to the specified timezone, applying any timezone and daylight saving time (DST) adjustments. + * + * This method: + * 1. Parses the input value into a Luxon DateTime object. + * 2. Applies the specified IANA timezone, including any DST shifts based on the given date. + * + * @param value - The ISO string or Date object to convert. + * @param zone - An IANA timezone name (e.g., 'America/New_York'). + * @returns A Luxon DateTime instance adjusted to the specified timezone and DST rules. + */ + toZone(value: string | Date, zone: string): DateTime { + return DateTime.fromISO(value instanceof Date ? value.toISOString() : value, { + zone, + }); + } + + /** + * Formats the input date by applying timezone and daylight saving time (DST) adjustments. + * + * This method: + * 1. Converts the input date to the specified timezone. + * 2. Formats the result using the given format and locale, reflecting any timezone or DST shifts. + * + * @param value - The ISO string or Date object to format. + * @param format - The format string (default: 'ff'). + * @param zone - Optional IANA timezone name (e.g., 'America/New_York'); defaults to the system's local timezone. + * @returns A formatted date string adjusted for the given timezone and DST rules. + */ + format(value: string | Date, format = 'ff', zone = 'local'): string { + return this.toZone(value, zone).setLocale(this.locale).toFormat(format); + } + + /** + * Formats a date using the standard time offset (ignoring daylight saving time) for the specified timezone. + * + * This method: + * 1. Converts the input date to UTC. + * 2. Calculates the standard UTC offset for the given timezone (based on January 1st to avoid DST). + * 3. Applies the standard offset manually to the UTC time. + * 4. Formats the result using the specified format and locale, without applying additional timezone shifts. + * + * @param value - The ISO string or Date object to format. + * @param format - The Luxon format string (default: 'ff'). + * @param zone - Optional IANA timezone name (e.g., 'America/New_York'); if omitted, system local timezone is used. + * @returns A formatted date string adjusted by standard time (non-DST). + */ + formatDateWithStandardOffset(value: string | Date, format = 'ff', zone?: string): string { + const utcDate = + typeof value === 'string' + ? DateTime.fromISO(value, { zone: 'UTC' }) + : DateTime.fromJSDate(value, { zone: 'UTC' }); + + if (!utcDate.isValid) return ''; + + const targetZone = zone ?? DateTime.local().zoneName; + + const januaryDate = DateTime.fromObject( + { year: utcDate.year, month: 1, day: 1 }, + { zone: targetZone }, + ); + const standardOffset = januaryDate.offset; + const dateWithStandardOffset = utcDate.plus({ minutes: standardOffset }); + + return dateWithStandardOffset.setZone('UTC').setLocale(this.locale).toFormat(format); + } + + /** + * Formats the input date using its original clock time, without converting based on timezone or DST + * + * This method: + * 1. Converts the input date to ISO string. + * 2. Calculates the date time in UTC, keeping the local time. + * 3. Formats the result using the specified format and locale, without shifting timezones. + * + * @param value - The ISO string or Date object to format. + * @param format - The format string (default: 'ff'). + * @returns A formatted date string without applying timezone. + */ + formatWithoutTimeZone(value: string | Date, format = 'ff'): string { + const isoString = value instanceof Date ? value.toISOString() : value; + + const dateTime = DateTime.fromISO(isoString) + .setZone('utc', { keepLocalTime: true }) + .setLocale(this.locale); + return dateTime.toFormat(format); + } +} diff --git a/npm/ng-packs/packages/core/src/lib/services/timezone.service.ts b/npm/ng-packs/packages/core/src/lib/services/timezone.service.ts new file mode 100644 index 0000000000..2d6c103dcd --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/services/timezone.service.ts @@ -0,0 +1,63 @@ +import { inject, Injectable } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import { ConfigStateService } from './config-state.service'; + +@Injectable({ + providedIn: 'root', +}) +export class TimezoneService { + protected readonly configState = inject(ConfigStateService); + protected readonly document = inject(DOCUMENT); + private readonly cookieKey = '__timezone'; + private timeZoneNameFromSettings: string | null | undefined; + public isUtcClockEnabled: boolean | undefined; + + constructor() { + this.configState.getOne$('setting').subscribe(settings => { + this.timeZoneNameFromSettings = settings?.values?.['Abp.Timing.TimeZone']; + }); + this.configState.getOne$('clock').subscribe(clock => { + this.isUtcClockEnabled = clock?.kind === 'Utc'; + }); + } + + /** + * Returns the effective timezone to be used across the application. + * + * This value is determined based on the clock kind setting in the configuration: + * - If clock kind is not equal to Utc, the browser's local timezone is returned. + * - If clock kind is equal to Utc, the configured timezone (`timeZoneNameFromSettings`) is returned if available; + * otherwise, the browser's timezone is used as a fallback. + * + * @returns The IANA timezone name (e.g., 'Europe/Istanbul', 'America/New_York'). + */ + get timezone(): string { + if (!this.isUtcClockEnabled) { + return this.getBrowserTimezone(); + } + return this.timeZoneNameFromSettings || this.getBrowserTimezone(); + } + + /** + * Retrieves the browser's local timezone based on the user's system settings. + * + * @returns The IANA timezone name (e.g., 'Europe/Istanbul', 'America/New_York'). + */ + getBrowserTimezone(): string { + return Intl.DateTimeFormat().resolvedOptions().timeZone; + } + + /** + * Sets the application's timezone in a cookie to persist the user's selected timezone. + * + * This method sets the cookie only if the clock kind setting is set to UTC. + * The cookie is stored using the key defined by `this.cookieKey` and applied to the root path (`/`). + * + * @param timezone - The IANA timezone name to be stored (e.g., 'Europe/Istanbul'). + */ + setTimezone(timezone: string): void { + if (this.isUtcClockEnabled) { + this.document.cookie = `${this.cookieKey}=${timezone}; path=/`; + } + } +} diff --git a/npm/ng-packs/packages/feature-management/package.json b/npm/ng-packs/packages/feature-management/package.json index d89cb20de0..ff8060c3b4 100644 --- a/npm/ng-packs/packages/feature-management/package.json +++ b/npm/ng-packs/packages/feature-management/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.feature-management", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "~9.1.0", + "@abp/ng.theme.shared": "~9.2.0-rc.2", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts index e26609bb25..fc71ad4a12 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts @@ -13,7 +13,7 @@ import { ToasterService, } from '@abp/ng.theme.shared'; import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; -import { NgTemplateOutlet } from '@angular/common'; +import { CommonModule, NgTemplateOutlet } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; import { finalize } from 'rxjs/operators'; @@ -31,6 +31,7 @@ enum ValueTypes { templateUrl: './feature-management.component.html', exportAs: 'abpFeatureManagement', imports: [ + CommonModule, ThemeSharedModule, LocalizationModule, FormsModule, diff --git a/npm/ng-packs/packages/generators/package.json b/npm/ng-packs/packages/generators/package.json index d6a9ac9b57..897c05548b 100644 --- a/npm/ng-packs/packages/generators/package.json +++ b/npm/ng-packs/packages/generators/package.json @@ -1,6 +1,6 @@ { "name": "@abp/nx.generators", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "generators": "./generators.json", "type": "commonjs", diff --git a/npm/ng-packs/packages/identity/package.json b/npm/ng-packs/packages/identity/package.json index 2c1b4942fb..90848c3a83 100644 --- a/npm/ng-packs/packages/identity/package.json +++ b/npm/ng-packs/packages/identity/package.json @@ -1,15 +1,15 @@ { "name": "@abp/ng.identity", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.components": "~9.1.0", - "@abp/ng.permission-management": "~9.1.0", - "@abp/ng.theme.shared": "~9.1.0", + "@abp/ng.components": "~9.2.0-rc.2", + "@abp/ng.permission-management": "~9.2.0-rc.2", + "@abp/ng.theme.shared": "~9.2.0-rc.2", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/oauth/package.json b/npm/ng-packs/packages/oauth/package.json index 12713c2357..000915aef6 100644 --- a/npm/ng-packs/packages/oauth/package.json +++ b/npm/ng-packs/packages/oauth/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.oauth", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.core": "~9.1.0", - "@abp/utils": "~9.1.0", + "@abp/ng.core": "~9.2.0-rc.2", + "@abp/utils": "~9.2.0-rc.2", "angular-oauth2-oidc": "^17.0.0", "just-clone": "^6.0.0", "just-compare": "^2.0.0", diff --git a/npm/ng-packs/packages/oauth/src/lib/services/remember-me.service.ts b/npm/ng-packs/packages/oauth/src/lib/services/remember-me.service.ts index ebec253cd5..8ddc1194ff 100644 --- a/npm/ng-packs/packages/oauth/src/lib/services/remember-me.service.ts +++ b/npm/ng-packs/packages/oauth/src/lib/services/remember-me.service.ts @@ -22,7 +22,11 @@ export class RememberMeService { getFromToken(accessToken: string) { const tokenBody = accessToken.split('.')[1].replace(/-/g, '+').replace(/_/g, '/'); - const parsedToken = JSON.parse(atob(tokenBody)); - return Boolean(parsedToken[this.#rememberMe]); + try { + const parsedToken = JSON.parse(atob(tokenBody)); + return Boolean(parsedToken[this.#rememberMe]); + } catch { + return false; + } } } diff --git a/npm/ng-packs/packages/permission-management/package.json b/npm/ng-packs/packages/permission-management/package.json index 7164d201b0..5757310487 100644 --- a/npm/ng-packs/packages/permission-management/package.json +++ b/npm/ng-packs/packages/permission-management/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.permission-management", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "~9.1.0", + "@abp/ng.theme.shared": "~9.2.0-rc.2", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/schematics/package.json b/npm/ng-packs/packages/schematics/package.json index e24ccf8d65..f8918746a6 100644 --- a/npm/ng-packs/packages/schematics/package.json +++ b/npm/ng-packs/packages/schematics/package.json @@ -1,6 +1,6 @@ { "name": "@abp/ng.schematics", - "version": "9.1.0", + "version": "9.2.0-rc.2", "author": "", "schematics": "./collection.json", "dependencies": { diff --git a/npm/ng-packs/packages/schematics/src/commands/change-theme/index.ts b/npm/ng-packs/packages/schematics/src/commands/change-theme/index.ts index de0c75c2ef..9e27d31c9f 100644 --- a/npm/ng-packs/packages/schematics/src/commands/change-theme/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/change-theme/index.ts @@ -5,21 +5,20 @@ import * as ts from 'typescript'; import { allStyles, importMap, styleMap } from './style-map'; import { ChangeThemeOptions } from './model'; import { - Change, - createDefaultPath, - InsertChange, + addRootImport, + addRootProvider, + getAppModulePath, isLibrary, + isStandaloneApp, updateWorkspace, WorkspaceDefinition, + getAppConfigPath, + cleanEmptyExprFromModule, + cleanEmptyExprFromProviders, } from '../../utils'; import { ThemeOptionsEnum } from './theme-options.enum'; -import { - addImportToModule, - addProviderToModule, - findNodes, - getDecoratorMetadata, - getMetadataField, -} from '../../utils/angular/ast-utils'; +import { findNodes, getDecoratorMetadata, getMetadataField } from '../../utils/angular/ast-utils'; +import { getMainFilePath } from '../../utils/angular/standalone/util'; export default function (_options: ChangeThemeOptions): Rule { return async () => { @@ -68,46 +67,60 @@ function updateProjectStyle( function updateAppModule(selectedProject: string, targetThemeName: ThemeOptionsEnum): Rule { return async (host: Tree) => { - const appModulePath = (await createDefaultPath(host, selectedProject)) + '/app.module.ts'; + const mainFilePath = await getMainFilePath(host, selectedProject); + const isStandalone = isStandaloneApp(host, mainFilePath); + const appModulePath = isStandalone + ? getAppConfigPath(host, mainFilePath) + : getAppModulePath(host, mainFilePath); return chain([ removeImportPath(appModulePath, targetThemeName), - removeImportFromNgModuleMetadata(appModulePath, targetThemeName), - removeProviderFromNgModuleMetadata(appModulePath, targetThemeName), - insertImports(appModulePath, targetThemeName), - insertProviders(appModulePath, targetThemeName), + ...(!isStandalone ? [removeImportFromNgModuleMetadata(appModulePath, targetThemeName)] : []), + isStandalone + ? removeImportsFromStandaloneProviders(appModulePath, targetThemeName) + : removeProviderFromNgModuleMetadata(appModulePath, targetThemeName), + insertImports(selectedProject, targetThemeName), + insertProviders(selectedProject, targetThemeName), + adjustProvideAbpThemeShared(appModulePath, targetThemeName), + formatFile(appModulePath), + cleanEmptyExpressions(appModulePath, isStandalone), ]); }; } -export function removeImportPath(appModulePath: string, selectedTheme: ThemeOptionsEnum): Rule { +export function removeImportPath(filePath: string, selectedTheme: ThemeOptionsEnum): Rule { return (host: Tree) => { - const recorder = host.beginUpdate(appModulePath); - const source = createSourceFile(host, appModulePath); + const buffer = host.read(filePath); + if (!buffer) return host; + + const sourceText = buffer.toString('utf-8'); + const source = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true); + const recorder = host.beginUpdate(filePath); + const impMap = getImportPaths(selectedTheme, true); const nodes = findNodes(source, ts.isImportDeclaration); - const filteredNodes = nodes.filter(node => - impMap.some(({ path, importName }) => { - const sourceModule = node.getFullText(); - const moduleName = importName.split('.')[0]; + const filteredNodes = nodes.filter(node => { + const importPath = (node.moduleSpecifier as ts.StringLiteral).text; + const namedBindings = node.importClause?.namedBindings; - if (path && sourceModule.match(path)) { - return true; - } + return impMap.some(({ path, importName }) => { + const symbol = importName.split('.')[0]; + const matchesPath = !!path && importPath === path; - return !!(moduleName && sourceModule.match(moduleName)); - }), - ); + const matchesSymbol = + !!namedBindings && + ts.isNamedImports(namedBindings) && + namedBindings.elements.some(e => e.name.text === symbol); - if (filteredNodes?.length < 1) { - return; - } + return matchesPath || matchesSymbol; + }); + }); - filteredNodes.map(importPath => - recorder.remove(importPath.getStart(), importPath.getWidth() + 1), - ); + for (const node of filteredNodes) { + recorder.remove(node.getStart(), node.getWidth()); + } host.commitUpdate(recorder); return host; @@ -153,100 +166,188 @@ export function removeImportFromNgModuleMetadata( }; } -export function removeProviderFromNgModuleMetadata( - appModulePath: string, +export function removeImportsFromStandaloneProviders( + mainPath: string, selectedTheme: ThemeOptionsEnum, ): Rule { return (host: Tree) => { - const recorder = host.beginUpdate(appModulePath); - const source = createSourceFile(host, appModulePath); + const buffer = host.read(mainPath); + if (!buffer) return host; + + const sourceText = buffer.toString('utf-8'); + const source = ts.createSourceFile(mainPath, sourceText, ts.ScriptTarget.Latest, true); + const recorder = host.beginUpdate(mainPath); + const impMap = getImportPaths(selectedTheme, true); + const callExpressions = findNodes(source, ts.isCallExpression); - const node = getDecoratorMetadata(source, 'NgModule', '@angular/core')[0] || {}; - if (!node) { - throw new SchematicsException('The app module does not found'); - } + for (const expr of callExpressions) { + const exprText = expr.getText(); - const matchingProperties = getMetadataField(node as ts.ObjectLiteralExpression, 'providers'); - const assignment = matchingProperties[0] as ts.PropertyAssignment; - const assignmentInit = assignment.initializer as ts.ArrayLiteralExpression; + if (expr.expression.getText() === 'importProvidersFrom') { + const args = expr.arguments; - const elements = assignmentInit.elements; - if (!elements || elements.length < 1) { - throw new SchematicsException(`Elements could not found: ${elements}`); - } + let modules: readonly ts.Expression[] = []; - const filteredElements = elements.filter(f => - impMap.filter(f => !!f.provider).some(s => f.getText().match(s.provider!)), - ); + if (args.length === 1 && ts.isArrayLiteralExpression(args[0])) { + modules = (args[0] as ts.ArrayLiteralExpression).elements; + } else { + modules = args; + } - if (!filteredElements || filteredElements.length < 1) { - return; + const elementsToRemove = modules.filter(el => + impMap.some(({ importName }) => el.getText().includes(importName)), + ); + + if (elementsToRemove.length) { + for (const removeEl of elementsToRemove) { + const start = removeEl.getFullStart(); + const end = removeEl.getEnd(); + + const nextChar = sourceText.slice(end, end + 1); + const prevChar = sourceText.slice(start - 1, start); + + if (nextChar === ',') { + recorder.remove(start, end - start + 1); + } else if (prevChar === ',') { + recorder.remove(start - 1, end - start + 1); + } else { + recorder.remove(start, end - start); + } + } + } + + const remaining = modules.filter(el => !elementsToRemove.includes(el)); + if (remaining.length === 0) { + const start = expr.getFullStart(); + const end = expr.getEnd(); + const nextChar = sourceText.slice(end, end + 1); + const prevChar = sourceText.slice(start - 1, start); + + if (nextChar === ',') { + recorder.remove(start, end - start + 1); + } else if (prevChar === ',') { + recorder.remove(start - 1, end - start + 1); + } else { + recorder.remove(start, end - start); + } + } + } else { + const match = impMap.find(({ importName, provider }) => { + const moduleSymbol = importName?.split('.')[0]; + return ( + (moduleSymbol && exprText.includes(moduleSymbol)) || + (provider && exprText.includes(provider)) + ); + }); + + if (match) { + const start = expr.getFullStart(); + const end = expr.getEnd(); + const nextChar = sourceText.slice(end, end + 1); + const prevChar = sourceText.slice(start - 1, start); + + if (nextChar === ',') { + recorder.remove(start, end - start + 1); + } else if (prevChar === ',') { + recorder.remove(start - 1, end - start + 1); + } else { + recorder.remove(start, end - start); + } + } + } } - filteredElements.map(willRemoveModule => { - recorder.remove(willRemoveModule.getStart(), willRemoveModule.getWidth()); - }); host.commitUpdate(recorder); return host; }; } -export function insertImports(appModulePath: string, selectedTheme: ThemeOptionsEnum): Rule { +export function removeProviderFromNgModuleMetadata( + appModulePath: string, + selectedTheme: ThemeOptionsEnum, +): Rule { return (host: Tree) => { const recorder = host.beginUpdate(appModulePath); const source = createSourceFile(host, appModulePath); - const selected = importMap.get(selectedTheme); + const impMap = getImportPaths(selectedTheme, true); - if (!selected) { - return host; + const node = getDecoratorMetadata(source, 'NgModule', '@angular/core')[0]; + if (!node) { + throw new SchematicsException('The app module does not found'); } - const changes: Change[] = []; + const providersProperty = getMetadataField( + node as ts.ObjectLiteralExpression, + 'providers', + )[0] as ts.PropertyAssignment; - selected.map(({ importName, path }) => - changes.push(...addImportToModule(source, appModulePath, importName, path)), - ); + const providersArray = providersProperty.initializer as ts.ArrayLiteralExpression; + if (!providersArray.elements.length) return host; - if (changes.length > 0) { - for (const change of changes) { - if (change instanceof InsertChange) { - recorder.insertLeft(change.order, change.toAdd); + for (const element of providersArray.elements) { + const elementText = element.getText(); + + const match = impMap.find(({ provider }) => { + if (!provider) return false; + const providerName = provider.replace(/\(\s*\)$/, '').trim(); + return provider && elementText.includes(providerName); + }); + + if (match) { + const start = element.getFullStart(); + const end = element.getEnd(); + + const nextChar = source.text.slice(end, end + 1); + const prevChar = source.text.slice(start - 1, start); + + if (nextChar === ',') { + recorder.remove(start, end - start + 1); + } else if (prevChar === ',') { + recorder.remove(start - 1, end - start + 1); + } else { + recorder.remove(start, end - start); } } } + host.commitUpdate(recorder); return host; }; } -export function insertProviders(appModulePath: string, selectedTheme: ThemeOptionsEnum): Rule { - return (host: Tree) => { - const recorder = host.beginUpdate(appModulePath); - const source = createSourceFile(host, appModulePath); +export function insertImports(projectName: string, selectedTheme: ThemeOptionsEnum): Rule { + return addRootImport(projectName, code => { const selected = importMap.get(selectedTheme); + if (!selected?.length) return code.code``; - if (!selected) { - return host; - } - - const changes: Change[] = []; + const expressions: string[] = []; - selected.map(({ path, provider }) => { - if (provider) { - changes.push(...addProviderToModule(source, appModulePath, provider + '()', path)); + for (const { importName, path, expression } of selected) { + if (importName && path) { + code.external(importName, path); } - }); - - for (const change of changes) { - if (change instanceof InsertChange) { - recorder.insertLeft(change.order, change.toAdd); + if (expression) { + expressions.push(expression.trim()); } } + return code.code`${expressions}`; + }); +} +export function insertProviders(projectName: string, selectedTheme: ThemeOptionsEnum): Rule { + return addRootProvider(projectName, code => { + const selected = importMap.get(selectedTheme); + if (!selected || selected.length === 0) return code.code``; - host.commitUpdate(recorder); - return host; - }; + const providers = selected + .filter(s => !!s.provider) + .map(({ provider, path, importName }) => { + code.external(importName, path); + return `${provider}`; + }); + + return code.code`${providers}`; + }); } export function createSourceFile(host: Tree, appModulePath: string): ts.SourceFile { @@ -271,7 +372,7 @@ export function createSourceFile(host: Tree, appModulePath: string): ts.SourceFi * @param selectedTheme The selected theme * @param getAll If true, returns all import paths */ -export function getImportPaths(selectedTheme: ThemeOptionsEnum, getAll: boolean = false) { +export function getImportPaths(selectedTheme: ThemeOptionsEnum, getAll = false) { if (getAll) { return Array.from(importMap.values()).reduce((acc, val) => [...acc, ...val], []); } @@ -316,3 +417,120 @@ export const styleCompareFn = (item1: string | object, item2: string | object) = return o1.bundleName && o2.bundleName && o1.bundleName == o2.bundleName; }; + +export const formatFile = (filePath: string): Rule => { + return (tree: Tree) => { + const buffer = tree.read(filePath); + if (!buffer) return tree; + + const source = ts.createSourceFile(filePath, buffer.toString(), ts.ScriptTarget.Latest, true); + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + const formatted = printer.printFile(source); + + tree.overwrite(filePath, formatted); + return tree; + }; +}; + +export function cleanEmptyExpressions(modulePath: string, isStandalone: boolean): Rule { + return (host: Tree) => { + const buffer = host.read(modulePath); + if (!buffer) throw new SchematicsException(`Cannot read ${modulePath}`); + + const source = ts.createSourceFile( + modulePath, + buffer.toString('utf-8'), + ts.ScriptTarget.Latest, + true, + ); + const recorder = host.beginUpdate(modulePath); + + if (isStandalone) { + cleanEmptyExprFromProviders(source, recorder); + } else { + cleanEmptyExprFromModule(source, recorder); + } + host.commitUpdate(recorder); + return host; + }; +} + +export function adjustProvideAbpThemeShared( + appModulePath: string, + selectedTheme: ThemeOptionsEnum, +): Rule { + return (host: Tree) => { + const source = createSourceFile(host, appModulePath); + const recorder = host.beginUpdate(appModulePath); + const sourceText = source.getText(); + + const callExpressions = findProvideAbpThemeSharedCalls(source); + + for (const expr of callExpressions) { + const exprStart = expr.getStart(); + const exprEnd = expr.getEnd(); + const originalText = sourceText.substring(exprStart, exprEnd); + + let newText = originalText; + + const hasHttpErrorConfig = originalText.includes('withHttpErrorConfig'); + const hasValidationBluePrint = originalText.includes('withValidationBluePrint'); + + if (selectedTheme === ThemeOptionsEnum.LeptonX) { + if (!hasHttpErrorConfig) { + newText = newText.replace( + '(', + `( + withHttpErrorConfig({ + errorScreen: { + component: HttpErrorComponent, + forWhichErrors: [401, 403, 404, 500], + hideCloseIcon: true + } + }),`, + ); + } + } else { + if (hasHttpErrorConfig) { + newText = newText.replace(/withHttpErrorConfig\([^)]*\),?/, ''); + } + } + + if (!hasValidationBluePrint) { + newText = newText.replace( + '(', + `( + withValidationBluePrint({ + wrongPassword: 'Please choose 1q2w3E*' + }),`, + ); + } + + if (newText && newText !== originalText) { + recorder.remove(exprStart, exprEnd - exprStart); + recorder.insertLeft(exprStart, newText); + } + } + + host.commitUpdate(recorder); + return host; + }; +} + +function findProvideAbpThemeSharedCalls(source: ts.SourceFile): ts.CallExpression[] { + const result: ts.CallExpression[] = []; + + const visit = (node: ts.Node) => { + if (ts.isCallExpression(node)) { + const expressionText = node.expression.getText(); + if (expressionText.includes('provideAbpThemeShared')) { + result.push(node); + } + } + ts.forEachChild(node, visit); + }; + + visit(source); + + return result; +} diff --git a/npm/ng-packs/packages/schematics/src/commands/change-theme/style-map.ts b/npm/ng-packs/packages/schematics/src/commands/change-theme/style-map.ts index 7dc9bf8053..8cae3b4641 100644 --- a/npm/ng-packs/packages/schematics/src/commands/change-theme/style-map.ts +++ b/npm/ng-packs/packages/schematics/src/commands/change-theme/style-map.ts @@ -12,6 +12,7 @@ export type ImportDefinition = { path: string; importName: string; provider?: string; + expression?: string; }; export const styleMap = new Map(); @@ -260,7 +261,7 @@ styleMap.set(ThemeOptionsEnum.LeptonXLite, [ bundleName: 'bootstrap-icons', }, ]); -// the code written by Github co-pilot. thank go-pilot. You are the best sidekick. + export const allStyles = Array.from(styleMap.values()).reduce((acc, val) => [...acc, ...val], []); export const importMap = new Map(); @@ -269,40 +270,122 @@ importMap.set(ThemeOptionsEnum.Basic, [ { path: '@abp/ng.theme.basic', importName: 'ThemeBasicModule', - provider: 'provideThemeBasicConfig', + expression: 'ThemeBasicModule', + }, + { + path: '@abp/ng.theme.basic', + importName: 'provideThemeBasicConfig', + provider: 'provideThemeBasicConfig()', + }, + { + path: '@abp/ng.theme.shared', + importName: 'ThemeSharedModule', + expression: 'ThemeSharedModule', + }, + { + path: '@abp/ng.theme.shared', + importName: 'withValidationBluePrint', + }, + { + path: '@abp/ng.theme.shared', + importName: 'provideAbpThemeShared', + provider: 'provideAbpThemeShared()', }, ]); importMap.set(ThemeOptionsEnum.Lepton, [ { path: '@volo/abp.ng.theme.lepton', - importName: 'ThemeLeptonModule', - provider: 'provideThemeLepton', + importName: 'provideThemeLepton', + provider: 'provideThemeLepton()', + }, + { + path: '@abp/ng.theme.shared', + importName: 'ThemeSharedModule', + expression: 'ThemeSharedModule', + }, + { + path: '@abp/ng.theme.shared', + importName: 'withHttpErrorConfig', + }, + { + path: '@abp/ng.theme.shared', + importName: 'withValidationBluePrint', + }, + { + path: '@abp/ng.theme.shared', + importName: 'provideAbpThemeShared', + provider: 'provideAbpThemeShared()', }, ]); importMap.set(ThemeOptionsEnum.LeptonXLite, [ { path: '@abp/ng.theme.lepton-x', - importName: 'ThemeLeptonXModule.forRoot()', + importName: 'ThemeLeptonXModule', + expression: 'ThemeLeptonXModule.forRoot()', }, { path: '@abp/ng.theme.lepton-x/layouts', - importName: 'SideMenuLayoutModule.forRoot()', + importName: 'SideMenuLayoutModule', + expression: 'SideMenuLayoutModule.forRoot()', }, { path: '@abp/ng.theme.lepton-x/account', - importName: 'AccountLayoutModule.forRoot()', + importName: 'AccountLayoutModule', + expression: 'AccountLayoutModule.forRoot()', + }, + { + path: '@abp/ng.theme.shared', + importName: 'ThemeSharedModule', + expression: 'ThemeSharedModule', + }, + { + path: '@abp/ng.theme.shared', + importName: 'withHttpErrorConfig', + }, + { + path: '@abp/ng.theme.shared', + importName: 'withValidationBluePrint', + }, + { + path: '@abp/ng.theme.shared', + importName: 'provideAbpThemeShared', + provider: 'provideAbpThemeShared()', }, ]); importMap.set(ThemeOptionsEnum.LeptonX, [ { path: '@volosoft/abp.ng.theme.lepton-x', - importName: 'ThemeLeptonXModule.forRoot()', + importName: 'ThemeLeptonXModule', + expression: 'ThemeLeptonXModule.forRoot()', }, { path: '@volosoft/abp.ng.theme.lepton-x/layouts', - importName: 'SideMenuLayoutModule.forRoot()', + importName: 'SideMenuLayoutModule', + expression: 'SideMenuLayoutModule.forRoot()', + }, + { + path: '@abp/ng.theme.shared', + importName: 'ThemeSharedModule', + expression: 'ThemeSharedModule', + }, + { + path: '@volosoft/abp.ng.theme.lepton-x', + importName: 'HttpErrorComponent', + }, + { + path: '@abp/ng.theme.shared', + importName: 'withHttpErrorConfig', + }, + { + path: '@abp/ng.theme.shared', + importName: 'withValidationBluePrint', + }, + { + path: '@abp/ng.theme.shared', + importName: 'provideAbpThemeShared', + provider: 'provideAbpThemeShared()', }, ]); diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/.eslintrc.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/.eslintrc.json.template new file mode 100644 index 0000000000..de84cad019 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/.eslintrc.json.template @@ -0,0 +1,44 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": [ + "!**/*" + ], + "overrides": [ + { + "files": [ + "*.ts" + ], + "parserOptions": { + "project": [ + "projects/<%= kebab(libraryName) %>/tsconfig.lib.json", + "projects/<%= kebab(libraryName) %>/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "lib", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "lib", + "style": "kebab-case" + } + ] + } + }, + { + "files": [ + "*.html" + ], + "rules": {} + } + ] +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/ng-package.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/ng-package.json.template new file mode 100644 index 0000000000..6cf8e96f64 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/ng-package.json.template @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-entrypoint.schema.json", + "dest": "../../dist/<%= kebab(libraryName) %>/config", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/enums/index.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/enums/index.ts.template new file mode 100644 index 0000000000..3bda94b078 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/enums/index.ts.template @@ -0,0 +1 @@ +export * from './route-names'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/enums/route-names.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/enums/route-names.ts.template new file mode 100644 index 0000000000..3bbb75be52 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/enums/route-names.ts.template @@ -0,0 +1,3 @@ +export const enum e<%= pascal(libraryName) %>RouteNames { + <%= pascal(libraryName) %> = '<%= pascal(libraryName) %>', +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/providers/index.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/providers/index.ts.template new file mode 100644 index 0000000000..fe08efba8c --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/providers/index.ts.template @@ -0,0 +1 @@ +export * from './route.provider'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/providers/route.provider.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/providers/route.provider.ts.template new file mode 100644 index 0000000000..c890db7305 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/providers/route.provider.ts.template @@ -0,0 +1,30 @@ +import { eLayoutType, RoutesService } from '@abp/ng.core'; +import { e<%= pascal(libraryName) %>RouteNames } from '../enums/route-names'; +import { makeEnvironmentProviders, provideAppInitializer, inject, EnvironmentProviders } from '@angular/core'; + +export const <%= macro(libraryName) %>_ROUTE_PROVIDERS = [ + provideAppInitializer(() => { + configureRoutes(); + }), +]; + +export function configureRoutes() { + const routes = inject(RoutesService); + routes.add([ + { + path: '/<%= kebab(libraryName) %>', + name: e<%= pascal(libraryName) %>RouteNames.<%= pascal(libraryName) %>, + iconClass: 'fas fa-book', + layout: eLayoutType.application, + order: 3, + }, + ]); +} + +export const <%= macro(libraryName) %>_PROVIDERS: EnvironmentProviders[] = [ + ...<%= macro(libraryName) %>_ROUTE_PROVIDERS, +]; + +export function provide<%= pascal(libraryName) %>Config() { + return makeEnvironmentProviders(<%= macro(libraryName) %>_PROVIDERS); +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/public-api.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/public-api.ts.template new file mode 100644 index 0000000000..0003cebbef --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/config/src/public-api.ts.template @@ -0,0 +1,2 @@ +export * from './enums'; +export * from './providers'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/karma.conf.js.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/karma.conf.js.template new file mode 100644 index 0000000000..e181d4088d --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/karma.conf.js.template @@ -0,0 +1,44 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, '../../coverage/<%= kebab(libraryName) %>'), + subdir: '.', + reporters: [ + { type: 'html' }, + { type: 'text-summary' } + ] + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/ng-package.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/ng-package.json.template new file mode 100644 index 0000000000..fd8e55d798 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/ng-package.json.template @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/<%= kebab(libraryName) %>", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/package.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/package.json.template new file mode 100644 index 0000000000..c4d36a8576 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/package.json.template @@ -0,0 +1,11 @@ +{ + "name": "@<%= kebab(libraryName) %>/<%= kebab(libraryName) %>", + "version": "0.0.1", + "peerDependencies": { + "@abp/ng.core": "<%= abpVersion %>", + "@abp/ng.theme.shared": "<%= abpVersion %>" + }, + "dependencies": { + "tslib": "^2.1.0" + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/lib/__libraryName@kebab__.component.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/lib/__libraryName@kebab__.component.ts.template new file mode 100644 index 0000000000..3c8ada66b8 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/lib/__libraryName@kebab__.component.ts.template @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import {CoreModule} from "@abp/ng.core"; +import {ThemeSharedModule} from "@abp/ng.theme.shared"; + +@Component({ + standalone: true, + selector: '<%= camel(libraryName) %>-home', + template: `

Lazy Loaded Test Component

`, + imports: [CoreModule, ThemeSharedModule], +}) +export class <%= pascal(libraryName) %>Component {} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/lib/__libraryName@kebab__.routes.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/lib/__libraryName@kebab__.routes.ts.template new file mode 100644 index 0000000000..975fdaa261 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/lib/__libraryName@kebab__.routes.ts.template @@ -0,0 +1,9 @@ +import { Routes } from '@angular/router'; + +export const <%= macro(libraryName) %>_ROUTES: Routes = [ + { + path: '', + loadComponent: () => + import('./<%= kebab(libraryName) %>.component').then(m => m.<%= pascal(libraryName) %>Component), + }, +]; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/lib/index.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/lib/index.ts.template new file mode 100644 index 0000000000..b954dd6be0 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/lib/index.ts.template @@ -0,0 +1 @@ +export * from './<%= kebab(libraryName) %>.routes'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/public-api.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/public-api.ts.template new file mode 100644 index 0000000000..5fa8ee704c --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/public-api.ts.template @@ -0,0 +1,4 @@ +/* + * Public API Surface of my-project-name + */ +export * from './lib'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/test.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/test.ts.template new file mode 100644 index 0000000000..52e55168eb --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/src/test.ts.template @@ -0,0 +1,26 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context(path: string, deep?: boolean, filter?: RegExp): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/tsconfig.lib.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/tsconfig.lib.json.template new file mode 100644 index 0000000000..5b574d313d --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/tsconfig.lib.json.template @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2020", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/tsconfig.lib.prod.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/tsconfig.lib.prod.json.template new file mode 100644 index 0000000000..06de549e10 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/tsconfig.lib.prod.json.template @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/tsconfig.spec.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/tsconfig.spec.json.template new file mode 100644 index 0000000000..715dd0a5d2 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package-standalone/__libraryName@kebab__/tsconfig.spec.json.template @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint-standalone/__libraryName@kebab__/ng-package.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint-standalone/__libraryName@kebab__/ng-package.json.template new file mode 100644 index 0000000000..e09fb3fd03 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint-standalone/__libraryName@kebab__/ng-package.json.template @@ -0,0 +1,6 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-entrypoint.schema.json", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint-standalone/__libraryName@kebab__/src/lib/__target@kebab__-__libraryName@kebab__.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint-standalone/__libraryName@kebab__/src/lib/__target@kebab__-__libraryName@kebab__.ts.template new file mode 100644 index 0000000000..a6b4121de5 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint-standalone/__libraryName@kebab__/src/lib/__target@kebab__-__libraryName@kebab__.ts.template @@ -0,0 +1,7 @@ +import { Provider } from '@angular/core'; + +export function provide<%= pascal(target) %><%= pascal(libraryName) %>(): Provider[] { + return [ + // Add your providers here + ]; +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint-standalone/__libraryName@kebab__/src/lib/index.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint-standalone/__libraryName@kebab__/src/lib/index.ts.template new file mode 100644 index 0000000000..b8bd795229 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint-standalone/__libraryName@kebab__/src/lib/index.ts.template @@ -0,0 +1 @@ +export * from './<%= kebab(target) %>-<%= kebab(libraryName) %>'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint-standalone/__libraryName@kebab__/src/public-api.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint-standalone/__libraryName@kebab__/src/public-api.ts.template new file mode 100644 index 0000000000..11aece60c4 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint-standalone/__libraryName@kebab__/src/public-api.ts.template @@ -0,0 +1 @@ +export * from './lib/index'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts b/npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts index f353845880..49645c3e45 100644 --- a/npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts @@ -12,19 +12,24 @@ import * as ts from 'typescript'; import { join, normalize } from '@angular-devkit/core'; import { - addImportToModule, + addRootImport, + addRootProvider, addRouteDeclarationToModule, applyWithOverwrite, - camel, + findAppRoutesModulePath, + findAppRoutesPath, getFirstApplication, getWorkspace, + hasImportInNgModule, + hasProviderInStandaloneAppConfig, InsertChange, interpolate, isLibrary, + isStandaloneApp, JSONFile, kebab, + macro, pascal, - readWorkspaceSchema, resolveProject, updateWorkspace, } from '../../utils'; @@ -32,7 +37,8 @@ import { ProjectDefinition, WorkspaceDefinition } from '../../utils/angular/work import { addLibToWorkspaceFile } from '../../utils/angular-schematic/generate-lib'; import * as cases from '../../utils/text'; import { Exception } from '../../enums/exception'; -import { GenerateLibSchema } from './models/generate-lib-schema'; +import { GenerateLibSchema, GenerateLibTemplateType } from './models/generate-lib-schema'; +import { getMainFilePath } from '../../utils/angular/standalone/util'; export default function (schema: GenerateLibSchema) { return async (tree: Tree) => { @@ -67,11 +73,17 @@ function createLibrary(options: GenerateLibSchema): Rule { return async (tree: Tree) => { const target = await resolveProject(tree, options.packageName, null); if (!target || options.override) { - if (options.isModuleTemplate) { - return createLibFromModuleTemplate(tree, options); - } if (options.isSecondaryEntrypoint) { - return createLibSecondaryEntry(tree, options); + if (options.templateType === GenerateLibTemplateType.Standalone) { + return createLibSecondaryEntryWithStandaloneTemplate(tree, options); + } else { + return createLibSecondaryEntry(tree, options); + } + } + if (options.templateType === GenerateLibTemplateType.Module) { + return createLibFromModuleTemplate(tree, options); + } else { + return createLibFromModuleStandaloneTemplate(tree, options); } } else { throw new SchematicsException( @@ -109,14 +121,32 @@ async function createLibFromModuleTemplate(tree: Tree, options: GenerateLibSchem }), move(normalize(packagesDir)), ]), - addLibToWorkspaceIfNotExist(options.packageName, packagesDir), + addLibToWorkspaceIfNotExist(options, packagesDir), ]); } -export function addLibToWorkspaceIfNotExist(name: string, packagesDir: string): Rule { +async function createLibFromModuleStandaloneTemplate(tree: Tree, options: GenerateLibSchema) { + const packagesDir = await resolvePackagesDirFromAngularJson(tree); + const packageJson = JSON.parse(tree.read('./package.json')!.toString()); + const abpVersion = packageJson.dependencies['@abp/ng.core']; + + return chain([ + applyWithOverwrite(url('./files-package-standalone'), [ + applyTemplates({ + ...cases, + libraryName: options.packageName, + abpVersion, + }), + move(normalize(packagesDir)), + ]), + addLibToWorkspaceIfNotExist(options, packagesDir), + ]); +} + +export function addLibToWorkspaceIfNotExist(options: GenerateLibSchema, packagesDir: string): Rule { return async (tree: Tree) => { const workspace = await getWorkspace(tree); - const packageName = kebab(name); + const packageName = kebab(options.packageName); const isProjectExist = workspace.projects.has(packageName); const projectRoot = join(normalize(packagesDir), packageName); @@ -130,8 +160,8 @@ export function addLibToWorkspaceIfNotExist(name: string, packagesDir: string): : noop(), addLibToWorkspaceFile(projectRoot, packageName), updateTsConfig(packageName, pathImportLib), - importConfigModuleToDefaultProjectAppModule(workspace, packageName), - addRoutingToAppRoutingModule(workspace, packageName), + importConfigModuleToDefaultProjectAppModule(packageName, options), + addRoutingToAppRoutingModule(workspace, packageName, options), ]); }; } @@ -169,84 +199,211 @@ export async function createLibSecondaryEntry(tree: Tree, options: GenerateLibSc ]); } +export async function createLibSecondaryEntryWithStandaloneTemplate( + tree: Tree, + options: GenerateLibSchema, +) { + const targetLib = await resolveProject(tree, options.target); + const packageName = `${kebab(targetLib.name)}/${kebab(options.packageName)}`; + const importPath = `${targetLib.definition.root}/${kebab(options.packageName)}`; + return chain([ + applyWithOverwrite(url('./files-secondary-entrypoint-standalone'), [ + applyTemplates({ + ...cases, + libraryName: options.packageName, + target: targetLib.name, + }), + move(normalize(targetLib.definition.root)), + updateTsConfig(packageName, importPath), + ]), + ]); +} + export function importConfigModuleToDefaultProjectAppModule( - workspace: WorkspaceDefinition, packageName: string, + options: GenerateLibSchema, ) { - return (tree: Tree) => { - const projectName = readWorkspaceSchema(tree).defaultProject || getFirstApplication(tree).name!; - const project = workspace.projects.get(projectName); - const appModulePath = `${project?.sourceRoot}/app/app.module.ts`; - const appModule = tree.read(appModulePath); - if (!appModule) { - return; - } - const appModuleContent = appModule.toString(); - if (appModuleContent.includes(`${camel(packageName)}ConfigModule`)) { - return; - } + return async (tree: Tree) => { + const projectName = getFirstApplication(tree).name!; + const mainFilePath = await getMainFilePath(tree, projectName); + const isSourceStandalone = isStandaloneApp(tree, mainFilePath); + const rules: Rule[] = []; - const forRootStatement = `${pascal(packageName)}ConfigModule.forRoot()`; - const text = tree.read(appModulePath); - if (!text) { + const providerAlreadyExists = isSourceStandalone + ? await hasProviderInStandaloneAppConfig( + tree, + projectName, + `provide${pascal(packageName)}Config`, + ) + : await hasImportInNgModule( + tree, + projectName, + options.templateType === GenerateLibTemplateType.Standalone + ? `provide${pascal(packageName)}Config` + : `${pascal(packageName)}ConfigModule`, + options.templateType === GenerateLibTemplateType.Standalone ? 'providers' : 'imports', + ); + if (providerAlreadyExists) { return; } - const sourceText = text.toString(); - if (sourceText.includes(forRootStatement)) { - return; + + if (options.templateType === GenerateLibTemplateType.Standalone) { + rules.push( + addRootProvider(projectName, code => { + const configFn = code.external( + `provide${pascal(packageName)}Config`, + `${kebab(packageName)}/config`, + ); + return code.code`${configFn}()`; + }), + ); + } else { + rules.push( + addRootImport(projectName, code => { + const configFn = code.external( + `${pascal(packageName)}ConfigModule`, + `${kebab(packageName)}/config`, + ); + return code.code`${configFn}.forRoot()`; + }), + ); } - const source = ts.createSourceFile(appModulePath, sourceText, ts.ScriptTarget.Latest, true); - - const changes = addImportToModule( - source, - appModulePath, - forRootStatement, - `${kebab(packageName)}/config`, - ); - const recorder = tree.beginUpdate(appModulePath); - for (const change of changes) { + return chain(rules); + }; +} + +export function addRoutingToAppRoutingModule( + workspace: WorkspaceDefinition, + packageName: string, + options: GenerateLibSchema, +): Rule { + return async (tree: Tree) => { + const projectName = getFirstApplication(tree).name!; + const project = workspace.projects.get(projectName); + const mainFilePath = await getMainFilePath(tree, projectName); + const isSourceStandalone = isStandaloneApp(tree, mainFilePath); + + const pascalName = pascal(packageName); + const macroName = macro(packageName); + const routePath = `${kebab(packageName)}`; + const moduleName = `${pascalName}Module`; + + if (isSourceStandalone) { + const appRoutesPath = + findAppRoutesPath(tree, mainFilePath) || `${project?.sourceRoot}/app/app.routes.ts`; + const buffer = tree.read(appRoutesPath); + if (!buffer) { + throw new SchematicsException(`Cannot find routes file: ${appRoutesPath}`); + } + + const content = buffer.toString(); + const source = ts.createSourceFile(appRoutesPath, content, ts.ScriptTarget.Latest, true); + const routeExpr = + options.templateType === GenerateLibTemplateType.Standalone + ? `() => import('${routePath}').then(m => m.${macroName}_ROUTES)` + : `() => import('${routePath}').then(m => m.${moduleName}.forLazy())`; + const routeToAdd = `{ path: '${routePath}', loadChildren: ${routeExpr} }`; + const change = addRouteToRoutesArray(source, 'APP_ROUTES', routeToAdd); + if (change instanceof InsertChange) { + const recorder = tree.beginUpdate(appRoutesPath); recorder.insertLeft(change.pos, change.toAdd); + tree.commitUpdate(recorder); } - } - tree.commitUpdate(recorder); + } else { + const appRoutingModulePath = await findAppRoutesModulePath(tree, mainFilePath); + + if (!appRoutingModulePath) { + throw new SchematicsException(`Cannot find routing module: ${appRoutingModulePath}`); + } + + const appRoutingModule = tree.read(appRoutingModulePath); + if (!appRoutingModule) { + return; + } + const appRoutingModuleContent = appRoutingModule.toString(); + const routeExpr = + options.templateType === GenerateLibTemplateType.Standalone + ? `${macroName}_ROUTES` + : moduleName; + if (appRoutingModuleContent.includes(routeExpr)) { + return; + } + + const source = ts.createSourceFile( + appRoutingModulePath, + appRoutingModuleContent, + ts.ScriptTarget.Latest, + true, + ); + const importStatement = + options.templateType === GenerateLibTemplateType.Standalone + ? `() => import('${routePath}').then(m => m.${macroName}_ROUTES)` + : `() => import('${routePath}').then(m => m.${moduleName}.forLazy())`; + const routeDefinition = `{ path: '${routePath}', loadChildren: ${importStatement} }`; + const change = addRouteDeclarationToModule(source, routePath, routeDefinition); + const recorder = tree.beginUpdate(appRoutingModulePath); + if (change instanceof InsertChange) { + recorder.insertLeft(change.pos, change.toAdd); + } + tree.commitUpdate(recorder); + } return; }; } -export function addRoutingToAppRoutingModule(workspace: WorkspaceDefinition, packageName: string) { - return (tree: Tree) => { - const projectName = readWorkspaceSchema(tree).defaultProject || getFirstApplication(tree).name!; - const project = workspace.projects.get(projectName); - const appRoutingModulePath = `${project?.sourceRoot}/app/app-routing.module.ts`; - const appRoutingModule = tree.read(appRoutingModulePath); - if (!appRoutingModule) { - return; - } - const appRoutingModuleContent = appRoutingModule.toString(); - const moduleName = `${pascal(packageName)}Module`; - if (appRoutingModuleContent.includes(moduleName)) { - return; - } +export function addRouteToRoutesArray( + source: ts.SourceFile, + arrayName: string, + routeToAdd: string, +): InsertChange | null { + const routesVar = source.statements.find( + stmt => + ts.isVariableStatement(stmt) && + stmt.declarationList.declarations.some( + decl => + ts.isVariableDeclaration(decl) && + (decl.name.getText() === arrayName || decl.name.getText() === arrayName.toUpperCase()) && + decl.initializer !== undefined && + ts.isArrayLiteralExpression(decl.initializer), + ), + ); - const source = ts.createSourceFile( - appRoutingModulePath, - appRoutingModuleContent, - ts.ScriptTarget.Latest, - true, - ); - const importPath = `${kebab(packageName)}`; - const importStatement = `() => import('${importPath}').then(m => m.${moduleName}.forLazy())`; - const routeDefinition = `{ path: '${kebab(packageName)}', loadChildren: ${importStatement} }`; - const change = addRouteDeclarationToModule(source, `${kebab(packageName)}`, routeDefinition); - - const recorder = tree.beginUpdate(appRoutingModulePath); - if (change instanceof InsertChange) { - recorder.insertLeft(change.pos, change.toAdd); - } - tree.commitUpdate(recorder); + if (!routesVar || !ts.isVariableStatement(routesVar)) { + throw new Error(`Could not find routes array named "${arrayName}".`); + } - return; + const declaration = routesVar.declarationList.declarations.find( + decl => decl.name.getText() === arrayName, + ) as ts.VariableDeclaration; + + const arrayLiteral = declaration.initializer as ts.ArrayLiteralExpression; + + const getPathValue = (routeText: string): string | null => { + const match = routeText.match(/path:\s*['"`](.+?)['"`]/); + return match?.[1] ?? null; }; + + const newPath = getPathValue(routeToAdd); + + const alreadyExists = arrayLiteral.elements.some(el => { + const existingPath = getPathValue(el.getText()); + return existingPath === newPath; + }); + + if (alreadyExists) { + return null; + } + + const hasTrailingComma = arrayLiteral.elements.hasTrailingComma ?? false; + const insertPos = + hasTrailingComma || arrayLiteral.elements.length === 0 + ? arrayLiteral.getEnd() - 1 + : arrayLiteral.elements[arrayLiteral.elements.length - 1].getEnd(); + + const prefix = arrayLiteral.elements.length > 0 && !hasTrailingComma ? ',\n ' : ' '; + const toAdd = `${prefix}${routeToAdd}`; + + return new InsertChange(source.fileName, insertPos, toAdd); } diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/models/generate-lib-schema.ts b/npm/ng-packs/packages/schematics/src/commands/create-lib/models/generate-lib-schema.ts index f3c6e5b97f..aca9f08e49 100644 --- a/npm/ng-packs/packages/schematics/src/commands/create-lib/models/generate-lib-schema.ts +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/models/generate-lib-schema.ts @@ -1,3 +1,8 @@ +export enum GenerateLibTemplateType { + Standalone = 'standalone', + Module = 'module', +} + export interface GenerateLibSchema { /** * Angular package name will create @@ -8,8 +13,10 @@ export interface GenerateLibSchema { * İs the package a library or a library module */ isSecondaryEntrypoint: boolean; - - isModuleTemplate: boolean; + /** + * İs the package has standalone template + */ + templateType: GenerateLibTemplateType; override: boolean; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/schema.json b/npm/ng-packs/packages/schematics/src/commands/create-lib/schema.json index 19df1a5427..df9f64a470 100644 --- a/npm/ng-packs/packages/schematics/src/commands/create-lib/schema.json +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/schema.json @@ -22,21 +22,25 @@ }, "x-prompt": "Is secondary entrypoint?" }, - "isModuleTemplate": { - "description": "Is module template", - "type": "boolean", - "$default": { - "$source": "argv", - "index": 2 - }, - "x-prompt": "Is module template?" + "templateType": { + "type": "string", + "description": "Type of the template", + "enum": ["module", "standalone"], + "x-prompt": { + "message": "Select the type of template to generate:", + "type": "list", + "items": [ + { "value": "module", "label": "Module Template" }, + { "value": "standalone", "label": "Standalone Template" } + ] + } }, "override": { "description": "Override existing files", "type": "boolean", "$default": { "$source": "argv", - "index": 3 + "index": 4 }, "x-prompt": "Override existing files?" } diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/add-declaration-to-ng-module.ts b/npm/ng-packs/packages/schematics/src/utils/angular/add-declaration-to-ng-module.ts index f62e3922fd..6e5dc266b7 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/add-declaration-to-ng-module.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/add-declaration-to-ng-module.ts @@ -3,7 +3,7 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ import { Rule, Tree, strings } from '@angular-devkit/schematics'; @@ -19,6 +19,7 @@ export interface DeclarationToNgModuleOptions { flat?: boolean; export?: boolean; type: string; + typeSeparator?: '.' | '-'; skipImport?: boolean; standalone?: boolean; } @@ -30,6 +31,8 @@ export function addDeclarationToNgModule(options: DeclarationToNgModuleOptions): return host; } + const typeSeparator = options.typeSeparator ?? '.'; + const sourceText = host.readText(modulePath); const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); @@ -37,11 +40,11 @@ export function addDeclarationToNgModule(options: DeclarationToNgModuleOptions): `/${options.path}/` + (options.flat ? '' : strings.dasherize(options.name) + '/') + strings.dasherize(options.name) + - (options.type ? '.' : '') + - strings.dasherize(options.type); + (options.type ? typeSeparator + strings.dasherize(options.type) : ''); const importPath = buildRelativePath(modulePath, filePath); - const classifiedName = strings.classify(options.name) + strings.classify(options.type); + const classifiedName = + strings.classify(options.name) + (options.type ? strings.classify(options.type) : ''); const changes = addDeclarationToModule(source, modulePath, classifiedName, importPath); if (options.export) { diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/ast-utils.ts b/npm/ng-packs/packages/schematics/src/utils/angular/ast-utils.ts index d435b90c85..2683e20149 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/ast-utils.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/ast-utils.ts @@ -3,20 +3,22 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ import { tags } from '@angular-devkit/core'; import * as ts from 'typescript'; import { Change, InsertChange, NoopChange } from './change'; +import { getEOL } from './eol'; /** * Add Import `import { symbolName } from fileName` if the import doesn't exit * already. Assumes fileToEdit can be resolved and accessed. - * @param fileToEdit (file we want to add import to) - * @param symbolName (item to import) - * @param fileName (path to the file) - * @param isDefault (if true, import follows style for importing default exports) + * @param fileToEdit File we want to add import to. + * @param symbolName Item to import. + * @param fileName Path to the file. + * @param isDefault If true, import follows style for importing default exports. + * @param alias Alias that the symbol should be inserted under. * @return Change */ export function insertImport( @@ -25,46 +27,40 @@ export function insertImport( symbolName: string, fileName: string, isDefault = false, + alias?: string, ): Change { const rootNode = source; - const allImports = findNodes(rootNode, ts.SyntaxKind.ImportDeclaration); + const allImports = findNodes(rootNode, ts.isImportDeclaration); + const importExpression = alias ? `${symbolName} as ${alias}` : symbolName; // get nodes that map to import statements from the file fileName const relevantImports = allImports.filter(node => { - // StringLiteral of the ImportDeclaration is the import file (fileName in this case). - const importFiles = node - .getChildren() - .filter(ts.isStringLiteral) - .map(n => n.text); - - return importFiles.filter(file => file === fileName).length === 1; + return ts.isStringLiteralLike(node.moduleSpecifier) && node.moduleSpecifier.text === fileName; }); if (relevantImports.length > 0) { - let importsAsterisk = false; - // imports from import file - const imports: ts.Node[] = []; - relevantImports.forEach(n => { - Array.prototype.push.apply(imports, findNodes(n, ts.SyntaxKind.Identifier)); - if (findNodes(n, ts.SyntaxKind.AsteriskToken).length > 0) { - importsAsterisk = true; - } + const hasNamespaceImport = relevantImports.some(node => { + return node.importClause?.namedBindings?.kind === ts.SyntaxKind.NamespaceImport; }); // if imports * from fileName, don't add symbolName - if (importsAsterisk) { + if (hasNamespaceImport) { return new NoopChange(); } - const importTextNodes = imports.filter(n => (n as ts.Identifier).text === symbolName); + const imports = relevantImports.flatMap(node => { + return node.importClause?.namedBindings && ts.isNamedImports(node.importClause.namedBindings) + ? node.importClause.namedBindings.elements + : []; + }); // insert import if it's not there - if (importTextNodes.length === 0) { + if (!imports.some(node => (node.propertyName || node.name).text === symbolName)) { const fallbackPos = findNodes(relevantImports[0], ts.SyntaxKind.CloseBraceToken)[0].getStart() || findNodes(relevantImports[0], ts.SyntaxKind.FromKeyword)[0].getStart(); - return insertAfterLastOccurrence(imports, `, ${symbolName}`, fileToEdit, fallbackPos); + return insertAfterLastOccurrence(imports, `, ${importExpression}`, fileToEdit, fallbackPos); } return new NoopChange(); @@ -78,12 +74,13 @@ export function insertImport( } const open = isDefault ? '' : '{ '; const close = isDefault ? '' : ' }'; + const eol = getEOL(rootNode.getText()); // if there are no imports or 'use strict' statement, insert import at beginning of file const insertAtBeginning = allImports.length === 0 && useStrict.length === 0; - const separator = insertAtBeginning ? '' : ';\n'; + const separator = insertAtBeginning ? '' : `;${eol}`; const toInsert = - `${separator}import ${open}${symbolName}${close}` + - ` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`; + `${separator}import ${open}${importExpression}${close}` + + ` from '${fileName}'${insertAtBeginning ? `;${eol}` : ''}`; return insertAfterLastOccurrence( allImports, @@ -222,7 +219,7 @@ function nodesByPosition(first: ts.Node, second: ts.Node): number { * @throw Error if toInsert is first occurence but fall back is not set */ export function insertAfterLastOccurrence( - nodes: ts.Node[], + nodes: ts.Node[] | ts.NodeArray, toInsert: string, file: string, fallbackPos: number, @@ -345,7 +342,7 @@ export function getDecoratorMetadata( export function getMetadataField( node: ts.ObjectLiteralExpression, metadataField: string, -): ts.ObjectLiteralElement[] { +): ts.PropertyAssignment[] { return ( node.properties .filter(ts.isPropertyAssignment) @@ -399,12 +396,7 @@ export function addSymbolToNgModuleMetadata( if (importPath !== null) { return [ new InsertChange(ngModulePath, position, toInsert), - insertImport( - source, - ngModulePath, - symbolName.replace(/\..*$/, '').replace(/\(\)/, ''), - importPath, - ), + insertImport(source, ngModulePath, symbolName.replace(/\..*$/, ''), importPath), ]; } else { return [new InsertChange(ngModulePath, position, toInsert)]; @@ -420,7 +412,7 @@ export function addSymbolToNgModuleMetadata( return []; } - let expresssion: ts.Expression | ts.ArrayLiteralExpression; + let expression: ts.Expression | ts.ArrayLiteralExpression; const assignmentInit = assignment.initializer; const elements = assignmentInit.elements; @@ -430,20 +422,20 @@ export function addSymbolToNgModuleMetadata( return []; } - expresssion = elements[elements.length - 1]; + expression = elements[elements.length - 1]; } else { - expresssion = assignmentInit; + expression = assignmentInit; } let toInsert: string; - let position = expresssion.getEnd(); - if (ts.isArrayLiteralExpression(expresssion)) { + let position = expression.getEnd(); + if (ts.isArrayLiteralExpression(expression)) { // We found the field but it's empty. Insert it just before the `]`. position--; toInsert = `\n${tags.indentBy(4)`${symbolName}`}\n `; } else { // Get the indentation of the last element, if any. - const text = expresssion.getFullText(source); + const text = expression.getFullText(source); const matches = text.match(/^(\r?\n)(\s*)/); if (matches) { toInsert = `,${matches[1]}${tags.indentBy(matches[2].length)`${symbolName}`}`; @@ -451,15 +443,11 @@ export function addSymbolToNgModuleMetadata( toInsert = `, ${symbolName}`; } } + if (importPath !== null) { return [ new InsertChange(ngModulePath, position, toInsert), - insertImport( - source, - ngModulePath, - symbolName.replace(/\..*$/, '').replace(/\(\)/, ''), - importPath, - ), + insertImport(source, ngModulePath, symbolName.replace(/\..*$/, ''), importPath), ]; } @@ -572,13 +560,9 @@ export function getRouterModuleDeclaration(source: ts.SourceFile): ts.Expression } const matchingProperties = getMetadataField(node, 'imports'); - if (!matchingProperties) { - return; - } - - const assignment = matchingProperties[0] as ts.PropertyAssignment; + const assignment = matchingProperties[0]; - if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) { + if (!assignment || assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) { return; } @@ -678,3 +662,52 @@ export function addRouteDeclarationToModule( return new InsertChange(fileToAdd, insertPos, route); } + +/** Asserts if the specified node is a named declaration (e.g. class, interface). */ +function isNamedNode( + node: ts.Node & { name?: ts.Node }, +): node is ts.Node & { name: ts.Identifier } { + return !!node.name && ts.isIdentifier(node.name); +} + +/** + * Determines if a SourceFile has a top-level declaration whose name matches a specific symbol. + * Can be used to avoid conflicts when inserting new imports into a file. + * @param sourceFile File in which to search. + * @param symbolName Name of the symbol to search for. + * @param skipModule Path of the module that the symbol may have been imported from. Used to + * avoid false positives where the same symbol we're looking for may have been imported. + */ +export function hasTopLevelIdentifier( + sourceFile: ts.SourceFile, + symbolName: string, + skipModule: string | null = null, +): boolean { + for (const node of sourceFile.statements) { + if (isNamedNode(node) && node.name.text === symbolName) { + return true; + } + + if ( + ts.isVariableStatement(node) && + node.declarationList.declarations.some(decl => { + return isNamedNode(decl) && decl.name.text === symbolName; + }) + ) { + return true; + } + + if ( + ts.isImportDeclaration(node) && + ts.isStringLiteralLike(node.moduleSpecifier) && + node.moduleSpecifier.text !== skipModule && + node.importClause?.namedBindings && + ts.isNamedImports(node.importClause.namedBindings) && + node.importClause.namedBindings.elements.some(el => el.name.text === symbolName) + ) { + return true; + } + } + + return false; +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/change.ts b/npm/ng-packs/packages/schematics/src/utils/angular/change.ts index 08df56a6dd..62e3ab659a 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/change.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/change.ts @@ -3,7 +3,7 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ import { UpdateRecorder } from '@angular-devkit/schematics'; @@ -47,7 +47,11 @@ export class InsertChange implements Change { order: number; description: string; - constructor(public path: string, public pos: number, public toAdd: string) { + constructor( + public path: string, + public pos: number, + public toAdd: string, + ) { if (pos < 0) { throw new Error('Negative positions are invalid'); } @@ -59,7 +63,7 @@ export class InsertChange implements Change { * This method does not insert spaces if there is none in the original string. */ apply(host: Host) { - return host.read(this.path).then((content) => { + return host.read(this.path).then(content => { const prefix = content.substring(0, this.pos); const suffix = content.substring(this.pos); @@ -75,7 +79,11 @@ export class RemoveChange implements Change { order: number; description: string; - constructor(public path: string, private pos: number, public toRemove: string) { + constructor( + public path: string, + private pos: number, + public toRemove: string, + ) { if (pos < 0) { throw new Error('Negative positions are invalid'); } @@ -84,7 +92,7 @@ export class RemoveChange implements Change { } apply(host: Host): Promise { - return host.read(this.path).then((content) => { + return host.read(this.path).then(content => { const prefix = content.substring(0, this.pos); const suffix = content.substring(this.pos + this.toRemove.length); @@ -115,7 +123,7 @@ export class ReplaceChange implements Change { } apply(host: Host): Promise { - return host.read(this.path).then((content) => { + return host.read(this.path).then(content => { const prefix = content.substring(0, this.pos); const suffix = content.substring(this.pos + this.oldText.length); const text = content.substring(this.pos, this.pos + this.oldText.length); diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/dependencies.ts b/npm/ng-packs/packages/schematics/src/utils/angular/dependencies.ts index c9aa617191..06c4f38653 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/dependencies.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/dependencies.ts @@ -3,7 +3,7 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ import { Tree } from '@angular-devkit/schematics'; diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/dependency.ts b/npm/ng-packs/packages/schematics/src/utils/angular/dependency.ts index 3522b59042..2f3501c33a 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/dependency.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/dependency.ts @@ -3,12 +3,12 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ import { Rule, SchematicContext } from '@angular-devkit/schematics'; import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; -import * as path from 'path'; +import * as path from 'node:path'; const installTasks = new WeakMap>(); @@ -41,11 +41,13 @@ export enum InstallBehavior { * which may install the dependency. */ None, + /** * Automatically determine the need to schedule a {@link NodePackageInstallTask} based on * previous usage of the {@link addDependency} within the schematic. */ Auto, + /** * Always schedule a {@link NodePackageInstallTask} when the rule is executed. */ @@ -62,6 +64,7 @@ export enum ExistingBehavior { * The dependency will not be added or otherwise changed if it already exists. */ Skip, + /** * The dependency's existing specifier will be replaced with the specifier provided in the * {@link addDependency} call. A warning will also be shown during schematic execution to @@ -95,17 +98,20 @@ export function addDependency( * dependency will be added. Defaults to {@link DependencyType.Default} (`dependencies`). */ type?: DependencyType; + /** * The path of the package manifest file (`package.json`) that will be modified. * Defaults to `/package.json`. */ packageJsonPath?: string; + /** * The dependency installation behavior to use to determine whether a * {@link NodePackageInstallTask} should be scheduled after adding the dependency. * Defaults to {@link InstallBehavior.Auto}. */ install?: InstallBehavior; + /** * The behavior to use when the dependency already exists within the `package.json`. * Defaults to {@link ExistingBehavior.Replace}. diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/eol.ts b/npm/ng-packs/packages/schematics/src/utils/angular/eol.ts new file mode 100644 index 0000000000..5fdae3f575 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/angular/eol.ts @@ -0,0 +1,25 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { EOL } from 'node:os'; + +const CRLF = '\r\n'; +const LF = '\n'; + +export function getEOL(content: string): string { + const newlines = content.match(/(?:\r?\n)/g); + + if (newlines?.length) { + const crlf = newlines.filter(l => l === CRLF).length; + const lf = newlines.length - crlf; + + return crlf > lf ? CRLF : LF; + } + + return EOL; +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/find-module.ts b/npm/ng-packs/packages/schematics/src/utils/angular/find-module.ts index 112269d5e5..085592abaa 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/find-module.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/find-module.ts @@ -3,7 +3,7 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ import { NormalizedRoot, Path, dirname, join, normalize, relative } from '@angular-devkit/core'; @@ -54,12 +54,12 @@ export function findModuleFromOptions(host: Tree, options: ModuleOptions): Path const candidatesDirs = [...candidateSet].sort((a, b) => b.length - a.length); for (const c of candidatesDirs) { - const candidateFiles = ['', `${moduleBaseName}.ts`, `${moduleBaseName}${moduleExt}`].map( - (x) => join(c, x), + const candidateFiles = ['', `${moduleBaseName}.ts`, `${moduleBaseName}${moduleExt}`].map(x => + join(c, x), ); for (const sc of candidateFiles) { - if (host.exists(sc)) { + if (host.exists(sc) && host.readText(sc).includes('@NgModule')) { return normalize(sc); } } @@ -85,8 +85,8 @@ export function findModule( let foundRoutingModule = false; while (dir) { - const allMatches = dir.subfiles.filter((p) => p.endsWith(moduleExt)); - const filteredMatches = allMatches.filter((p) => !p.endsWith(routingModuleExt)); + const allMatches = dir.subfiles.filter(p => p.endsWith(moduleExt)); + const filteredMatches = allMatches.filter(p => !p.endsWith(routingModuleExt)); foundRoutingModule = foundRoutingModule || allMatches.length !== filteredMatches.length; diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/generate-from-files.ts b/npm/ng-packs/packages/schematics/src/utils/angular/generate-from-files.ts index 169c6c389a..946c4119d3 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/generate-from-files.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/generate-from-files.ts @@ -3,16 +3,18 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ import { + FileOperator, Rule, Tree, apply, applyTemplates, chain, filter, + forEach, mergeWith, move, noop, @@ -30,6 +32,8 @@ export interface GenerateFromFilesOptions { prefix?: string; project: string; skipTests?: boolean; + templateFilesDirectory?: string; + type?: string; } export function generateFromFiles( @@ -41,19 +45,33 @@ export function generateFromFiles( options.prefix ??= ''; options.flat ??= true; + // Schematic templates require a defined type value + options.type ??= ''; + const parsedPath = parseName(options.path, options.name); options.name = parsedPath.name; options.path = parsedPath.path; validateClassName(strings.classify(options.name)); - const templateSource = apply(url('./files'), [ + const templateFilesDirectory = options.templateFilesDirectory ?? './files'; + const templateSource = apply(url(templateFilesDirectory), [ options.skipTests ? filter(path => !path.endsWith('.spec.ts.template')) : noop(), applyTemplates({ ...strings, ...options, ...extraTemplateValues, }), + !options.type + ? forEach((file => { + return file.path.includes('..') + ? { + content: file.content, + path: file.path.replace('..', '.'), + } + : file; + }) as FileOperator) + : noop(), move(parsedPath.path + (options.flat ? '' : '/' + strings.dasherize(options.name))), ]); diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/index.ts b/npm/ng-packs/packages/schematics/src/utils/angular/index.ts index abf2992d90..2c1bfd086e 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/index.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/index.ts @@ -11,3 +11,4 @@ export * from './project-targets'; export * from './validation'; export * from './workspace'; export * from './workspace-models'; +export * from './standalone'; diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/json-file.ts b/npm/ng-packs/packages/schematics/src/utils/angular/json-file.ts index 6bb532416f..26a06e16a6 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/json-file.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/json-file.ts @@ -3,7 +3,7 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ import { JsonValue } from '@angular-devkit/core'; @@ -18,16 +18,22 @@ import { parseTree, printParseErrorCode, } from 'jsonc-parser'; +import { getEOL } from './eol'; export type InsertionIndex = (properties: string[]) => number; export type JSONPath = (string | number)[]; -/** @internal */ +/** @private */ export class JSONFile { content: string; + private eol: string; - constructor(private readonly host: Tree, private readonly path: string) { + constructor( + private readonly host: Tree, + private readonly path: string, + ) { this.content = this.host.readText(this.path); + this.eol = getEOL(this.content); } private _jsonAst: Node | undefined; @@ -73,15 +79,17 @@ export class JSONFile { let getInsertionIndex: InsertionIndex | undefined; if (insertInOrder === undefined) { const property = jsonPath.slice(-1)[0]; - getInsertionIndex = (properties) => - [...properties, property].sort().findIndex((p) => p === property); + getInsertionIndex = properties => + [...properties, property].sort().findIndex(p => p === property); } else if (insertInOrder !== false) { getInsertionIndex = insertInOrder; } const edits = modify(this.content, jsonPath, value, { getInsertionIndex, + formattingOptions: { + eol: this.eol, insertSpaces: true, tabSize: 2, }, diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/ng-ast-utils.ts b/npm/ng-packs/packages/schematics/src/utils/angular/ng-ast-utils.ts index 75962f31d7..b4eb1fd056 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/ng-ast-utils.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/ng-ast-utils.ts @@ -3,14 +3,14 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ -import { normalize } from '@angular-devkit/core'; import { SchematicsException, Tree } from '@angular-devkit/schematics'; -import { dirname } from 'path'; +import { dirname, join } from 'node:path/posix'; import * as ts from 'typescript'; import { findNode, getSourceNodes } from './ast-utils'; +import { findBootstrapApplicationCall } from './standalone/util'; export function findBootstrapModuleCall(host: Tree, mainPath: string): ts.CallExpression | null { const mainText = host.readText(mainPath); @@ -46,7 +46,7 @@ export function findBootstrapModuleCall(host: Tree, mainPath: string): ts.CallEx return bootstrapCall; } -export function findBootstrapModulePath(host: Tree, mainPath: string): string { +function findBootstrapModulePath(host: Tree, mainPath: string): string { const bootstrapCall = findBootstrapModuleCall(host, mainPath); if (!bootstrapCall) { throw new SchematicsException('Bootstrap call not found'); @@ -74,7 +74,21 @@ export function findBootstrapModulePath(host: Tree, mainPath: string): string { export function getAppModulePath(host: Tree, mainPath: string): string { const moduleRelativePath = findBootstrapModulePath(host, mainPath); const mainDir = dirname(mainPath); - const modulePath = normalize(`/${mainDir}/${moduleRelativePath}.ts`); + const modulePath = join(mainDir, `${moduleRelativePath}.ts`); return modulePath; } + +export function isStandaloneApp(host: Tree, mainPath: string): boolean { + try { + findBootstrapApplicationCall(host, mainPath); + + return true; + } catch (error) { + if (error instanceof SchematicsException) { + return false; + } + + throw error; + } +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/parse-name.ts b/npm/ng-packs/packages/schematics/src/utils/angular/parse-name.ts index 01227198c9..37a019e940 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/parse-name.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/parse-name.ts @@ -3,7 +3,7 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ import { Path, basename, dirname, join, normalize } from '@angular-devkit/core'; diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/paths.ts b/npm/ng-packs/packages/schematics/src/utils/angular/paths.ts index dffa9e841d..2842018802 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/paths.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/paths.ts @@ -3,17 +3,15 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ -import { normalize, split } from '@angular-devkit/core'; +import { join, relative } from 'node:path/posix'; export function relativePathToWorkspaceRoot(projectRoot: string | undefined): string { - const normalizedPath = split(normalize(projectRoot || '')); - - if (normalizedPath.length === 0 || !normalizedPath[0]) { + if (!projectRoot) { return '.'; - } else { - return normalizedPath.map(() => '..').join('/'); } + + return relative(join('/', projectRoot), '/') || '.'; } diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/project-targets.ts b/npm/ng-packs/packages/schematics/src/utils/angular/project-targets.ts index 7f4b7ba8c6..8897a3ddab 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/project-targets.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/project-targets.ts @@ -3,11 +3,21 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ import { SchematicsException } from '@angular-devkit/schematics'; +import { ProjectDefinition } from './workspace'; +import { Builders } from './workspace-models'; export function targetBuildNotFoundError(): SchematicsException { return new SchematicsException(`Project target "build" not found.`); } + +export function isUsingApplicationBuilder(project: ProjectDefinition): boolean { + const buildBuilder = project.targets.get('build')?.builder; + const isUsingApplicationBuilder = + buildBuilder === Builders.Application || buildBuilder === Builders.BuildApplication; + + return isUsingApplicationBuilder; +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/standalone/app_component.ts b/npm/ng-packs/packages/schematics/src/utils/angular/standalone/app_component.ts new file mode 100644 index 0000000000..f617e0baaa --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/angular/standalone/app_component.ts @@ -0,0 +1,148 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { SchematicsException, Tree } from '@angular-devkit/schematics'; +import * as ts from 'typescript'; +import { getDecoratorMetadata, getMetadataField } from '../ast-utils'; +import { findBootstrapModuleCall, getAppModulePath } from '../ng-ast-utils'; +import { findBootstrapApplicationCall, getSourceFile } from './util'; + +/** Data resolved for a bootstrapped component. */ +interface BootstrappedComponentData { + /** Original name of the component class. */ + componentName: string; + + /** Path under which the component was imported in the main entrypoint. */ + componentImportPathInSameFile: string; + + /** Original name of the NgModule being bootstrapped, null if the app isn't module-based. */ + moduleName: string | null; + + /** + * Path under which the module was imported in the main entrypoint, + * null if the app isn't module-based. + */ + moduleImportPathInSameFile: string | null; +} + +/** + * Finds the original name and path relative to the `main.ts` of the bootrstrapped app component. + * @param tree File tree in which to look for the component. + * @param mainFilePath Path of the `main` file. + */ +export function resolveBootstrappedComponentData( + tree: Tree, + mainFilePath: string, +): BootstrappedComponentData | null { + // First try to resolve for a standalone app. + try { + const call = findBootstrapApplicationCall(tree, mainFilePath); + + if (call.arguments.length > 0 && ts.isIdentifier(call.arguments[0])) { + const resolved = resolveIdentifier(call.arguments[0]); + + if (resolved) { + return { + componentName: resolved.name, + componentImportPathInSameFile: resolved.path, + moduleName: null, + moduleImportPathInSameFile: null, + }; + } + } + } catch (e) { + // `findBootstrapApplicationCall` will throw if it can't find the `bootrstrapApplication` call. + // Catch so we can continue to the fallback logic. + if (!(e instanceof SchematicsException)) { + throw e; + } + } + + // Otherwise fall back to resolving an NgModule-based app. + return resolveNgModuleBasedData(tree, mainFilePath); +} + +/** Resolves the bootstrap data for a NgModule-based app. */ +function resolveNgModuleBasedData( + tree: Tree, + mainFilePath: string, +): BootstrappedComponentData | null { + const appModulePath = getAppModulePath(tree, mainFilePath); + const appModuleFile = getSourceFile(tree, appModulePath); + const metadataNodes = getDecoratorMetadata(appModuleFile, 'NgModule', '@angular/core'); + + for (const node of metadataNodes) { + if (!ts.isObjectLiteralExpression(node)) { + continue; + } + + const bootstrapProp = getMetadataField(node, 'bootstrap').find(prop => { + return ( + ts.isArrayLiteralExpression(prop.initializer) && + prop.initializer.elements.length > 0 && + ts.isIdentifier(prop.initializer.elements[0]) + ); + }); + + const componentIdentifier = (bootstrapProp?.initializer as ts.ArrayLiteralExpression) + .elements[0] as ts.Identifier | undefined; + const componentResult = componentIdentifier ? resolveIdentifier(componentIdentifier) : null; + const bootstrapCall = findBootstrapModuleCall(tree, mainFilePath); + + if ( + componentResult && + bootstrapCall && + bootstrapCall.arguments.length > 0 && + ts.isIdentifier(bootstrapCall.arguments[0]) + ) { + const moduleResult = resolveIdentifier(bootstrapCall.arguments[0]); + + if (moduleResult) { + return { + componentName: componentResult.name, + componentImportPathInSameFile: componentResult.path, + moduleName: moduleResult.name, + moduleImportPathInSameFile: moduleResult.path, + }; + } + } + } + + return null; +} + +/** Resolves an identifier to its original name and path that it was imported from. */ +function resolveIdentifier(identifier: ts.Identifier): { name: string; path: string } | null { + const sourceFile = identifier.getSourceFile(); + + // Try to resolve the import path by looking at the top-level named imports of the file. + for (const node of sourceFile.statements) { + if ( + !ts.isImportDeclaration(node) || + !ts.isStringLiteral(node.moduleSpecifier) || + !node.importClause || + !node.importClause.namedBindings || + !ts.isNamedImports(node.importClause.namedBindings) + ) { + continue; + } + + for (const element of node.importClause.namedBindings.elements) { + if (element.name.text === identifier.text) { + return { + // Note that we use `propertyName` if available, because it contains + // the real name in the case where the import is aliased. + name: (element.propertyName || element.name).text, + path: node.moduleSpecifier.text, + }; + } + } + } + + return null; +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/standalone/app_config.ts b/npm/ng-packs/packages/schematics/src/utils/angular/standalone/app_config.ts new file mode 100644 index 0000000000..817f7b680a --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/angular/standalone/app_config.ts @@ -0,0 +1,127 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { Tree } from '@angular-devkit/schematics'; +import { dirname, join } from 'node:path'; +import * as ts from 'typescript'; +import { getSourceFile } from './util'; + +/** App config that was resolved to its source node. */ +export interface ResolvedAppConfig { + /** Tree-relative path of the file containing the app config. */ + filePath: string; + + /** Node defining the app config. */ + node: ts.ObjectLiteralExpression; +} + +/** + * Resolves the node that defines the app config from a bootstrap call. + * @param bootstrapCall Call for which to resolve the config. + * @param tree File tree of the project. + * @param filePath File path of the bootstrap call. + */ +export function findAppConfig( + bootstrapCall: ts.CallExpression, + tree: Tree, + filePath: string, +): ResolvedAppConfig | null { + if (bootstrapCall.arguments.length > 1) { + const config = bootstrapCall.arguments[1]; + + if (ts.isObjectLiteralExpression(config)) { + return { filePath, node: config }; + } + + if (ts.isIdentifier(config)) { + return resolveAppConfigFromIdentifier(config, tree, filePath); + } + } + + return null; +} + +/** + * Resolves the app config from an identifier referring to it. + * @param identifier Identifier referring to the app config. + * @param tree File tree of the project. + * @param bootstapFilePath Path of the bootstrap call. + */ +function resolveAppConfigFromIdentifier( + identifier: ts.Identifier, + tree: Tree, + bootstapFilePath: string, +): ResolvedAppConfig | null { + const sourceFile = identifier.getSourceFile(); + + for (const node of sourceFile.statements) { + // Only look at relative imports. This will break if the app uses a path + // mapping to refer to the import, but in order to resolve those, we would + // need knowledge about the entire program. + if ( + !ts.isImportDeclaration(node) || + !node.importClause?.namedBindings || + !ts.isNamedImports(node.importClause.namedBindings) || + !ts.isStringLiteralLike(node.moduleSpecifier) || + !node.moduleSpecifier.text.startsWith('.') + ) { + continue; + } + + for (const specifier of node.importClause.namedBindings.elements) { + if (specifier.name.text !== identifier.text) { + continue; + } + + // Look for a variable with the imported name in the file. Note that ideally we would use + // the type checker to resolve this, but we can't because these utilities are set up to + // operate on individual files, not the entire program. + const filePath = join(dirname(bootstapFilePath), node.moduleSpecifier.text + '.ts'); + const importedSourceFile = getSourceFile(tree, filePath); + const resolvedVariable = findAppConfigFromVariableName( + importedSourceFile, + (specifier.propertyName || specifier.name).text, + ); + + if (resolvedVariable) { + return { filePath, node: resolvedVariable }; + } + } + } + + const variableInSameFile = findAppConfigFromVariableName(sourceFile, identifier.text); + + return variableInSameFile ? { filePath: bootstapFilePath, node: variableInSameFile } : null; +} + +/** + * Finds an app config within the top-level variables of a file. + * @param sourceFile File in which to search for the config. + * @param variableName Name of the variable containing the config. + */ +function findAppConfigFromVariableName( + sourceFile: ts.SourceFile, + variableName: string, +): ts.ObjectLiteralExpression | null { + for (const node of sourceFile.statements) { + if (ts.isVariableStatement(node)) { + for (const decl of node.declarationList.declarations) { + if ( + ts.isIdentifier(decl.name) && + decl.name.text === variableName && + decl.initializer && + ts.isObjectLiteralExpression(decl.initializer) + ) { + return decl.initializer; + } + } + } + } + + return null; +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/standalone/code_block.ts b/npm/ng-packs/packages/schematics/src/utils/angular/standalone/code_block.ts new file mode 100644 index 0000000000..a572f3b6c9 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/angular/standalone/code_block.ts @@ -0,0 +1,115 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { Rule, Tree } from '@angular-devkit/schematics'; +import * as ts from 'typescript'; +import { hasTopLevelIdentifier, insertImport } from '../ast-utils'; +import { applyToUpdateRecorder } from '../change'; + +/** Generated code that hasn't been interpolated yet. */ +export interface PendingCode { + /** Code that will be inserted. */ + expression: string; + + /** Imports that need to be added to the file in which the code is inserted. */ + imports: PendingImports; +} + +/** Map keeping track of imports and aliases under which they're referred to in an expression. */ +type PendingImports = Map>; + +/** Counter used to generate unique IDs. */ +let uniqueIdCounter = 0; + +/** + * Callback invoked by a Rule that produces the code + * that needs to be inserted somewhere in the app. + */ +export type CodeBlockCallback = (block: CodeBlock) => PendingCode; + +/** + * Utility class used to generate blocks of code that + * can be inserted by the devkit into a user's app. + */ +export class CodeBlock { + private _imports: PendingImports = new Map>(); + + // Note: the methods here are defined as arrow function so that they can be destructured by + // consumers without losing their context. This makes the API more concise. + + /** Function used to tag a code block in order to produce a `PendingCode` object. */ + code = (strings: TemplateStringsArray, ...params: unknown[]): PendingCode => { + return { + expression: strings.map((part, index) => part + (params[index] || '')).join(''), + imports: this._imports, + }; + }; + + /** + * Used inside of a code block to mark external symbols and which module they should be imported + * from. When the code is inserted, the required import statements will be produced automatically. + * @param symbolName Name of the external symbol. + * @param moduleName Module from which the symbol should be imported. + */ + external = (symbolName: string, moduleName: string): string => { + if (!this._imports.has(moduleName)) { + this._imports.set(moduleName, new Map()); + } + + const symbolsPerModule = this._imports.get(moduleName) as Map; + + if (!symbolsPerModule.has(symbolName)) { + symbolsPerModule.set(symbolName, `@@__SCHEMATIC_PLACEHOLDER_${uniqueIdCounter++}__@@`); + } + + return symbolsPerModule.get(symbolName) as string; + }; + + /** + * Produces the necessary rules to transform a `PendingCode` object into valid code. + * @param initialCode Code pending transformed. + * @param filePath Path of the file in which the code will be inserted. + */ + static transformPendingCode(initialCode: PendingCode, filePath: string) { + const code = { ...initialCode }; + const rules: Rule[] = []; + + code.imports.forEach((symbols, moduleName) => { + symbols.forEach((placeholder, symbolName) => { + rules.push((tree: Tree) => { + const recorder = tree.beginUpdate(filePath); + const sourceFile = ts.createSourceFile( + filePath, + tree.readText(filePath), + ts.ScriptTarget.Latest, + true, + ); + + // Note that this could still technically clash if there's a top-level symbol called + // `${symbolName}_alias`, however this is unlikely. We can revisit this if it becomes + // a problem. + const alias = hasTopLevelIdentifier(sourceFile, symbolName, moduleName) + ? symbolName + '_alias' + : undefined; + + code.expression = code.expression.replace( + new RegExp(placeholder, 'g'), + alias || symbolName, + ); + + applyToUpdateRecorder(recorder, [ + insertImport(sourceFile, filePath, symbolName, moduleName, false, alias), + ]); + tree.commitUpdate(recorder); + }); + }); + }); + + return { code, rules }; + } +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/standalone/index.ts b/npm/ng-packs/packages/schematics/src/utils/angular/standalone/index.ts new file mode 100644 index 0000000000..b522156c49 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/angular/standalone/index.ts @@ -0,0 +1,10 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +export { addRootImport, addRootProvider } from './rules'; +export type { PendingCode, CodeBlockCallback, CodeBlock } from './code_block'; diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/standalone/rules.ts b/npm/ng-packs/packages/schematics/src/utils/angular/standalone/rules.ts new file mode 100644 index 0000000000..79bc5ea8e4 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/angular/standalone/rules.ts @@ -0,0 +1,258 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { tags } from '@angular-devkit/core'; +import { Rule, SchematicsException, Tree, chain } from '@angular-devkit/schematics'; +import * as ts from 'typescript'; +import { addSymbolToNgModuleMetadata, insertAfterLastOccurrence } from '../ast-utils'; +import { InsertChange } from '../change'; +import { getAppModulePath, isStandaloneApp } from '../ng-ast-utils'; +import { ResolvedAppConfig, findAppConfig } from './app_config'; +import { CodeBlock, CodeBlockCallback, PendingCode } from './code_block'; +import { + applyChangesToFile, + findBootstrapApplicationCall, + findProvidersLiteral, + getMainFilePath, + getSourceFile, + isMergeAppConfigCall, +} from './util'; + +/** + * Adds an import to the root of the project. + * @param project Name of the project to which to add the import. + * @param callback Function that generates the code block which should be inserted. + * @example + * + * ```ts + * import { Rule } from '@angular-devkit/schematics'; + * import { addRootImport } from '@schematics/angular/utility'; + * + * export default function(): Rule { + * return addRootImport('default', ({code, external}) => { + * return code`${external('MyModule', '@my/module')}.forRoot({})`; + * }); + * } + * ``` + */ +export function addRootImport(project: string, callback: CodeBlockCallback): Rule { + return getRootInsertionRule(project, callback, 'imports', { + name: 'importProvidersFrom', + module: '@angular/core', + }); +} + +/** + * Adds a provider to the root of the project. + * @param project Name of the project to which to add the import. + * @param callback Function that generates the code block which should be inserted. + * @example + * + * ```ts + * import { Rule } from '@angular-devkit/schematics'; + * import { addRootProvider } from '@schematics/angular/utility'; + * + * export default function(): Rule { + * return addRootProvider('default', ({code, external}) => { + * return code`${external('provideLibrary', '@my/library')}({})`; + * }); + * } + * ``` + */ +export function addRootProvider(project: string, callback: CodeBlockCallback): Rule { + return getRootInsertionRule(project, callback, 'providers'); +} + +/** + * Creates a rule that inserts code at the root of either a standalone or NgModule-based project. + * @param project Name of the project into which to inser tthe code. + * @param callback Function that generates the code block which should be inserted. + * @param ngModuleField Field of the root NgModule into which the code should be inserted, if the + * app is based on NgModule + * @param standaloneWrapperFunction Function with which to wrap the code if the app is standalone. + */ +function getRootInsertionRule( + project: string, + callback: CodeBlockCallback, + ngModuleField: string, + standaloneWrapperFunction?: { name: string; module: string }, +): Rule { + return async host => { + const mainFilePath = await getMainFilePath(host, project); + const codeBlock = new CodeBlock(); + + if (isStandaloneApp(host, mainFilePath)) { + return tree => + addProviderToStandaloneBootstrap( + tree, + callback(codeBlock), + mainFilePath, + standaloneWrapperFunction, + ); + } + + const modulePath = getAppModulePath(host, mainFilePath); + const pendingCode = CodeBlock.transformPendingCode(callback(codeBlock), modulePath); + + return chain([ + ...pendingCode.rules, + tree => { + const changes = addSymbolToNgModuleMetadata( + getSourceFile(tree, modulePath), + modulePath, + ngModuleField, + pendingCode.code.expression, + // Explicitly set the import path to null since we deal with imports here separately. + null, + ); + + applyChangesToFile(tree, modulePath, changes); + }, + ]); + }; +} + +/** + * Adds a provider to the root of a standalone project. + * @param host Tree of the root rule. + * @param pendingCode Code that should be inserted. + * @param mainFilePath Path to the project's main file. + * @param wrapperFunction Optional function with which to wrap the provider. + */ +function addProviderToStandaloneBootstrap( + host: Tree, + pendingCode: PendingCode, + mainFilePath: string, + wrapperFunction?: { name: string; module: string }, +): Rule { + const bootstrapCall = findBootstrapApplicationCall(host, mainFilePath); + const fileToEdit = findAppConfig(bootstrapCall, host, mainFilePath)?.filePath || mainFilePath; + const { code, rules } = CodeBlock.transformPendingCode(pendingCode, fileToEdit); + + return chain([ + ...rules, + () => { + let wrapped: PendingCode; + let additionalRules: Rule[]; + + if (wrapperFunction) { + const block = new CodeBlock(); + const result = CodeBlock.transformPendingCode( + block.code`${block.external(wrapperFunction.name, wrapperFunction.module)}(${ + code.expression + })`, + fileToEdit, + ); + + wrapped = result.code; + additionalRules = result.rules; + } else { + wrapped = code; + additionalRules = []; + } + + return chain([ + ...additionalRules, + tree => insertStandaloneRootProvider(tree, mainFilePath, wrapped.expression), + ]); + }, + ]); +} + +/** + * Inserts a string expression into the root of a standalone project. + * @param tree File tree used to modify the project. + * @param mainFilePath Path to the main file of the project. + * @param expression Code expression to be inserted. + */ +function insertStandaloneRootProvider(tree: Tree, mainFilePath: string, expression: string): void { + const bootstrapCall = findBootstrapApplicationCall(tree, mainFilePath); + const appConfig = findAppConfig(bootstrapCall, tree, mainFilePath); + + if (bootstrapCall.arguments.length === 0) { + throw new SchematicsException( + `Cannot add provider to invalid bootstrapApplication call in ${ + bootstrapCall.getSourceFile().fileName + }`, + ); + } + + if (appConfig) { + addProvidersExpressionToAppConfig(tree, appConfig, expression); + + return; + } + + const newAppConfig = `, {\n${tags.indentBy(2)`providers: [${expression}]`}\n}`; + let targetCall: ts.CallExpression; + + if (bootstrapCall.arguments.length === 1) { + targetCall = bootstrapCall; + } else if (isMergeAppConfigCall(bootstrapCall.arguments[1])) { + targetCall = bootstrapCall.arguments[1]; + } else { + throw new SchematicsException( + `Cannot statically analyze bootstrapApplication call in ${ + bootstrapCall.getSourceFile().fileName + }`, + ); + } + + applyChangesToFile(tree, mainFilePath, [ + insertAfterLastOccurrence( + targetCall.arguments, + newAppConfig, + mainFilePath, + targetCall.getEnd() - 1, + ), + ]); +} + +/** + * Adds a string expression to an app config object. + * @param tree File tree used to modify the project. + * @param appConfig Resolved configuration object of the project. + * @param expression Code expression to be inserted. + */ +function addProvidersExpressionToAppConfig( + tree: Tree, + appConfig: ResolvedAppConfig, + expression: string, +): void { + const { node, filePath } = appConfig; + const configProps = node.properties; + const providersLiteral = findProvidersLiteral(node); + + // If there's a `providers` property, we can add the provider + // to it, otherwise we need to declare it ourselves. + if (providersLiteral) { + applyChangesToFile(tree, filePath, [ + insertAfterLastOccurrence( + providersLiteral.elements, + (providersLiteral.elements.length === 0 ? '' : ', ') + expression, + filePath, + providersLiteral.getStart() + 1, + ), + ]); + } else { + const prop = tags.indentBy(2)`providers: [${expression}]`; + let toInsert: string; + let insertPosition: number; + + if (configProps.length === 0) { + toInsert = '\n' + prop + '\n'; + insertPosition = node.getEnd() - 1; + } else { + const hasTrailingComma = configProps.hasTrailingComma; + toInsert = (hasTrailingComma ? '' : ',') + '\n' + prop; + insertPosition = configProps[configProps.length - 1].getEnd() + (hasTrailingComma ? 1 : 0); + } + + applyChangesToFile(tree, filePath, [new InsertChange(filePath, insertPosition, toInsert)]); + } +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/standalone/util.ts b/npm/ng-packs/packages/schematics/src/utils/angular/standalone/util.ts new file mode 100644 index 0000000000..0bb1419a63 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/angular/standalone/util.ts @@ -0,0 +1,171 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { SchematicsException, Tree } from '@angular-devkit/schematics'; +import * as ts from 'typescript'; +import { Change, applyToUpdateRecorder } from '../change'; +import { targetBuildNotFoundError } from '../project-targets'; +import { getWorkspace } from '../workspace'; +import { Builders } from '../workspace-models'; + +/** + * Finds the main file of a project. + * @param tree File tree for the project. + * @param projectName Name of the project in which to search. + */ +export async function getMainFilePath(tree: Tree, projectName: string): Promise { + const workspace = await getWorkspace(tree); + const project = workspace.projects.get(projectName); + const buildTarget = project?.targets.get('build'); + + if (!buildTarget) { + throw targetBuildNotFoundError(); + } + + const options = buildTarget.options as Record; + + return buildTarget.builder === Builders.Application || + buildTarget.builder === Builders.BuildApplication + ? options.browser + : options.main; +} + +/** + * Gets a TypeScript source file at a specific path. + * @param tree File tree of a project. + * @param path Path to the file. + */ +export function getSourceFile(tree: Tree, path: string): ts.SourceFile { + const content = tree.readText(path); + const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true); + + return source; +} + +/** Finds the call to `bootstrapApplication` within a file. */ +export function findBootstrapApplicationCall(tree: Tree, mainFilePath: string): ts.CallExpression { + const sourceFile = getSourceFile(tree, mainFilePath); + const localName = findImportLocalName( + sourceFile, + 'bootstrapApplication', + '@angular/platform-browser', + ); + + if (localName) { + let result: ts.CallExpression | null = null; + + sourceFile.forEachChild(function walk(node) { + if ( + ts.isCallExpression(node) && + ts.isIdentifier(node.expression) && + node.expression.text === localName + ) { + result = node; + } + + if (!result) { + node.forEachChild(walk); + } + }); + + if (result) { + return result; + } + } + + throw new SchematicsException(`Could not find bootstrapApplication call in ${mainFilePath}`); +} + +/** + * Finds the local name of an imported symbol. Could be the symbol name itself or its alias. + * @param sourceFile File within which to search for the import. + * @param name Actual name of the import, not its local alias. + * @param moduleName Name of the module from which the symbol is imported. + */ +function findImportLocalName( + sourceFile: ts.SourceFile, + name: string, + moduleName: string, +): string | null { + for (const node of sourceFile.statements) { + // Only look for top-level imports. + if ( + !ts.isImportDeclaration(node) || + !ts.isStringLiteral(node.moduleSpecifier) || + node.moduleSpecifier.text !== moduleName + ) { + continue; + } + + // Filter out imports that don't have the right shape. + if ( + !node.importClause || + !node.importClause.namedBindings || + !ts.isNamedImports(node.importClause.namedBindings) + ) { + continue; + } + + // Look through the elements of the declaration for the specific import. + for (const element of node.importClause.namedBindings.elements) { + if ((element.propertyName || element.name).text === name) { + // The local name is always in `name`. + return element.name.text; + } + } + } + + return null; +} + +/** + * Applies a set of changes to a file. + * @param tree File tree of the project. + * @param path Path to the file that is being changed. + * @param changes Changes that should be applied to the file. + */ +export function applyChangesToFile(tree: Tree, path: string, changes: Change[]) { + if (changes.length > 0) { + const recorder = tree.beginUpdate(path); + applyToUpdateRecorder(recorder, changes); + tree.commitUpdate(recorder); + } +} + +/** Checks whether a node is a call to `mergeApplicationConfig`. */ +export function isMergeAppConfigCall(node: ts.Node): node is ts.CallExpression { + if (!ts.isCallExpression(node)) { + return false; + } + + const localName = findImportLocalName( + node.getSourceFile(), + 'mergeApplicationConfig', + '@angular/core', + ); + + return !!localName && ts.isIdentifier(node.expression) && node.expression.text === localName; +} + +/** Finds the `providers` array literal within an application config. */ +export function findProvidersLiteral( + config: ts.ObjectLiteralExpression, +): ts.ArrayLiteralExpression | null { + for (const prop of config.properties) { + if ( + ts.isPropertyAssignment(prop) && + ts.isIdentifier(prop.name) && + prop.name.text === 'providers' && + ts.isArrayLiteralExpression(prop.initializer) + ) { + return prop.initializer; + } + } + + return null; +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/validation.ts b/npm/ng-packs/packages/schematics/src/utils/angular/validation.ts index 619fe8e924..8b380d1b82 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/validation.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/validation.ts @@ -3,14 +3,15 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ import { SchematicsException } from '@angular-devkit/schematics'; // Must start with a letter, and must contain only alphanumeric characters or dashes. // When adding a dash the segment after the dash must also start with a letter. -export const htmlSelectorRe = /^[a-zA-Z][.0-9a-zA-Z]*(:?-[a-zA-Z][.0-9a-zA-Z]*)*$/; +export const htmlSelectorRe = + /^[a-zA-Z][.0-9a-zA-Z]*((:?-[0-9]+)*|(:?-[a-zA-Z][.0-9a-zA-Z]*(:?-[0-9]+)*)*)$/; // See: https://github.com/tc39/proposal-regexp-unicode-property-escapes/blob/fe6d07fad74cd0192d154966baa1e95e7cda78a1/README.md#other-examples const ecmaIdentifierNameRegExp = /^(?:[$_\p{ID_Start}])(?:[$_\u200C\u200D\p{ID_Continue}])*$/u; diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/workspace-models.ts b/npm/ng-packs/packages/schematics/src/utils/angular/workspace-models.ts index fb7f18c3f7..34c329b470 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/workspace-models.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/workspace-models.ts @@ -3,7 +3,7 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ export enum ProjectType { @@ -18,16 +18,24 @@ export enum ProjectType { * `angular.json` workspace file. */ export enum Builders { + Application = '@angular-devkit/build-angular:application', AppShell = '@angular-devkit/build-angular:app-shell', Server = '@angular-devkit/build-angular:server', Browser = '@angular-devkit/build-angular:browser', + SsrDevServer = '@angular-devkit/build-angular:ssr-dev-server', + Prerender = '@angular-devkit/build-angular:prerender', + BrowserEsbuild = '@angular-devkit/build-angular:browser-esbuild', Karma = '@angular-devkit/build-angular:karma', + BuildKarma = '@angular/build:karma', TsLint = '@angular-devkit/build-angular:tslint', - DeprecatedNgPackagr = '@angular-devkit/build-ng-packagr:build', NgPackagr = '@angular-devkit/build-angular:ng-packagr', + BuildNgPackagr = '@angular/build:ng-packagr', DevServer = '@angular-devkit/build-angular:dev-server', + BuildDevServer = '@angular/build:dev-server', ExtractI18n = '@angular-devkit/build-angular:extract-i18n', - Protractor = '@angular-devkit/build-angular:protractor', + BuildExtractI18n = '@angular/build:extract-i18n', + Protractor = '@angular-devkit/build-angular:private-protractor', + BuildApplication = '@angular/build:application', } export interface FileReplacements { @@ -70,8 +78,9 @@ export interface BrowserBuilderOptions extends BrowserBuilderBaseOptions { } export interface ServeBuilderOptions { - browserTarget: string; + buildTarget: string; } + export interface LibraryBuilderOptions { tsConfig: string; project: string; @@ -138,11 +147,9 @@ export type E2EBuilderTarget = BuilderTarget; interface WorkspaceCLISchema { warnings?: Record; schematicCollections?: string[]; - defaultCollection?: string; } export interface WorkspaceSchema { version: 1; - defaultProject?: string; cli?: WorkspaceCLISchema; projects: { [key: string]: WorkspaceProject; @@ -165,6 +172,7 @@ export interface WorkspaceProject; + /** * Tool options. */ diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/workspace.ts b/npm/ng-packs/packages/schematics/src/utils/angular/workspace.ts index 410c079e2f..b831458edf 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/workspace.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/workspace.ts @@ -3,7 +3,7 @@ * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license + * found in the LICENSE file at https://angular.dev/license */ import { json, workspaces } from '@angular-devkit/core'; @@ -20,7 +20,7 @@ export type TargetDefinition = workspaces.TargetDefinition; /** * A {@link workspaces.WorkspaceHost} backed by a Schematics {@link Tree} instance. */ -class TreeWorkspaceHost implements workspaces.WorkspaceHost { +export class TreeWorkspaceHost implements workspaces.WorkspaceHost { constructor(private readonly tree: Tree) {} async readFile(path: string): Promise { @@ -58,14 +58,12 @@ class TreeWorkspaceHost implements workspaces.WorkspaceHost { export function updateWorkspace( updater: (workspace: WorkspaceDefinition) => void | Rule | PromiseLike, ): Rule { - return async (tree: Tree) => { - const host = new TreeWorkspaceHost(tree); - - const { workspace } = await workspaces.readWorkspace(DEFAULT_WORKSPACE_PATH, host); + return async (host: Tree) => { + const workspace = await getWorkspace(host); const result = await updater(workspace); - await workspaces.writeWorkspace(workspace, host); + await workspaces.writeWorkspace(workspace, new TreeWorkspaceHost(host)); return result || noop; }; diff --git a/npm/ng-packs/packages/schematics/src/utils/ast.ts b/npm/ng-packs/packages/schematics/src/utils/ast.ts index a1ffe5a319..0b720e63cc 100644 --- a/npm/ng-packs/packages/schematics/src/utils/ast.ts +++ b/npm/ng-packs/packages/schematics/src/utils/ast.ts @@ -35,3 +35,10 @@ export function isBooleanStringOrNumberLiteral( node.kind === ts.SyntaxKind.FalseKeyword ); } + +export function removeEmptyElementsFromArrayLiteral( + array: ts.ArrayLiteralExpression, +): ts.ArrayLiteralExpression { + const cleaned = array.elements.filter(el => el.kind !== ts.SyntaxKind.OmittedExpression); + return ts.factory.updateArrayLiteralExpression(array, ts.factory.createNodeArray(cleaned)); +} diff --git a/npm/ng-packs/packages/schematics/src/utils/index.ts b/npm/ng-packs/packages/schematics/src/utils/index.ts index eeb12e6d9e..06b96892fc 100644 --- a/npm/ng-packs/packages/schematics/src/utils/index.ts +++ b/npm/ng-packs/packages/schematics/src/utils/index.ts @@ -18,3 +18,5 @@ export * from './text'; export * from './tree'; export * from './type'; export * from './workspace'; +export * from './standalone'; +export * from './ng-module'; diff --git a/npm/ng-packs/packages/schematics/src/utils/ng-module.ts b/npm/ng-packs/packages/schematics/src/utils/ng-module.ts new file mode 100644 index 0000000000..ca2d327eb6 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/ng-module.ts @@ -0,0 +1,159 @@ +import { SchematicsException, Tree, UpdateRecorder } from '@angular-devkit/schematics'; +import { getMainFilePath } from './angular/standalone/util'; +import * as ts from 'typescript'; +import { getAppModulePath, getDecoratorMetadata, getMetadataField } from './angular'; +import { createSourceFile } from '../commands/change-theme/index'; +import { normalize, Path } from '@angular-devkit/core'; +import * as path from 'path'; +import { removeEmptyElementsFromArrayLiteral } from './ast'; + +/** + * Checks whether a specific import or provider exists in the specified metadata + * array (`imports`, `providers`, etc.) of the `NgModule` decorator in the AppModule. + * + * This function locates the AppModule file of the given Angular project, + * parses its AST, and inspects the specified metadata array to determine + * if it includes an element matching the provided string (e.g., `CommonModule`, `HttpClientModule`). + * + * @param host - The virtual file system tree used by Angular schematics. + * @param projectName - The name of the Angular project. + * @param metadataFn - The name (string) to match against the elements of the metadata array. + * @param metadataName - The metadata field to search in (e.g., 'imports', 'providers'). Defaults to 'imports'. + * @returns A promise that resolves to `true` if the metadata function is found, or `false` otherwise. + * @throws SchematicsException if the AppModule file or expected metadata is not found or malformed. + */ +export const hasImportInNgModule = async ( + host: Tree, + projectName: string, + metadataFn: string, + metadataName = 'imports', +): Promise => { + const mainFilePath = await getMainFilePath(host, projectName); + const appModulePath = getAppModulePath(host, mainFilePath); + const buffer = host.read(appModulePath); + + if (!buffer) { + throw new SchematicsException(`Could not read file: ${appModulePath}`); + } + + const source = createSourceFile(host, appModulePath); + + // Get the NgModule decorator metadata + const ngModuleDecorator = getDecoratorMetadata(source, 'NgModule', '@angular/core')[0]; + + if (!ngModuleDecorator) { + throw new SchematicsException('The app module does not found'); + } + + const matchingProperties = getMetadataField( + ngModuleDecorator as ts.ObjectLiteralExpression, + metadataName, + ); + const assignment = matchingProperties[0] as ts.PropertyAssignment; + const assignmentInit = assignment.initializer as ts.ArrayLiteralExpression; + + const elements = assignmentInit.elements; + if (!elements || elements.length < 1) { + throw new SchematicsException(`Elements could not found: ${elements}`); + } + + return elements.some(f => f.getText().match(metadataFn)); +}; + +/** + * Attempts to locate the path of the `AppRoutingModule` file that is imported + * within the root AppModule file of an Angular application. + * + * This function reads the AppModule file (resolved from the main file path), + * parses its AST, and searches for an import declaration that imports + * `AppRoutingModule`. Once found, it resolves the import path to a normalized + * file path relative to the workspace root. + * + * @param tree - The virtual file system tree used by Angular schematics. + * @param mainFilePath - The path to the main entry file of the Angular application (typically `main.ts`). + * @returns A normalized workspace-relative path to the AppRoutingModule file if found, or `null` otherwise. + * @throws If the route file path is resolved but the file does not exist in the tree. + */ +export async function findAppRoutesModulePath( + tree: Tree, + mainFilePath: string, +): Promise { + const appModulePath = getAppModulePath(tree, mainFilePath); + if (!appModulePath || !tree.exists(appModulePath)) return null; + + const buffer = tree.read(appModulePath); + if (!buffer) return null; + + const source = ts.createSourceFile( + appModulePath, + buffer.toString('utf-8'), + ts.ScriptTarget.Latest, + true, + ); + + for (const stmt of source.statements) { + if (!ts.isImportDeclaration(stmt)) continue; + + const importClause = stmt.importClause; + if (!importClause?.namedBindings || !ts.isNamedImports(importClause.namedBindings)) continue; + + const isRoutesImport = importClause.namedBindings.elements.some( + el => el.name.getText() === 'AppRoutingModule', + ); + if (!isRoutesImport || !ts.isStringLiteral(stmt.moduleSpecifier)) continue; + + let importPath = stmt.moduleSpecifier.text; + + if (!importPath.endsWith('.ts')) { + importPath += '.ts'; + } + + const configDir = path.dirname(appModulePath); + const resolvedFsPath = path.resolve(configDir, importPath); + const workspaceRelativePath = path.relative(process.cwd(), resolvedFsPath).replace(/\\/g, '/'); + + const normalizedPath = normalize(workspaceRelativePath); + + if (!tree.exists(normalizedPath)) { + throw new Error(`Cannot find routes file: ${normalizedPath}`); + } + + return normalizedPath; + } + + return null; +} + +/** + * Cleans up empty or invalid expressions (e.g., extra commas) from the `imports` and `providers` + * arrays in the NgModule decorator of an Angular module file. + * + * This function parses the source file's AST, locates the `NgModule` decorator, and processes + * the `imports` and `providers` metadata fields. If these fields contain array literals with + * empty slots (such as trailing or double commas), they are removed and the array is rewritten. + * + * @param source - The TypeScript source file containing the Angular module. + * @param recorder - The recorder used to apply changes to the source file. + */ +export function cleanEmptyExprFromModule(source: ts.SourceFile, recorder: UpdateRecorder): void { + const ngModuleNode = getDecoratorMetadata(source, 'NgModule', '@angular/core')[0]; + if (!ngModuleNode) return; + + const printer = ts.createPrinter(); + const metadataKeys = ['imports', 'providers']; + for (const key of metadataKeys) { + const metadataField = getMetadataField(ngModuleNode as ts.ObjectLiteralExpression, key); + if (!metadataField.length) continue; + + const assignment = metadataField[0] as ts.PropertyAssignment; + const arrayLiteral = assignment.initializer as ts.ArrayLiteralExpression; + + const cleanedArray = removeEmptyElementsFromArrayLiteral(arrayLiteral); + + recorder.remove(arrayLiteral.getStart(), arrayLiteral.getWidth()); + recorder.insertLeft( + arrayLiteral.getStart(), + printer.printNode(ts.EmitHint.Expression, cleanedArray, source), + ); + } +} diff --git a/npm/ng-packs/packages/schematics/src/utils/standalone.ts b/npm/ng-packs/packages/schematics/src/utils/standalone.ts new file mode 100644 index 0000000000..60364eb976 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/standalone.ts @@ -0,0 +1,180 @@ +import { SchematicsException, Tree, UpdateRecorder } from '@angular-devkit/schematics'; +import { findBootstrapApplicationCall, getMainFilePath } from './angular/standalone/util'; +import { findAppConfig } from './angular/standalone/app_config'; +import * as ts from 'typescript'; +import { normalize, Path } from '@angular-devkit/core'; +import * as path from 'path'; +import { findNodes } from './angular'; +import { removeEmptyElementsFromArrayLiteral } from './ast'; + +/** + * Retrieves the file path of the application's configuration used in a standalone + * Angular application setup. + * + * This function locates the `bootstrapApplication` call in the main entry file and + * resolves the path to the configuration object passed to it (typically `appConfig`). + * + * @param host - The virtual file system tree used by Angular schematics. + * @param mainFilePath - The path to the main entry file of the Angular application (e.g., `main.ts`). + * @returns The resolved file path of the application's configuration, or an empty string if not found. + */ +export const getAppConfigPath = (host: Tree, mainFilePath: string): string => { + const bootstrapCall = findBootstrapApplicationCall(host, mainFilePath); + const appConfig = findAppConfig(bootstrapCall, host, mainFilePath); + return appConfig?.filePath || ''; +}; + +/** + * Attempts to locate the file path of the `routes` array used in a standalone + * Angular application configuration. + * + * This function resolves the application's config file (typically where `routes` is defined or imported), + * parses the file, and inspects its import declarations to find the import associated with `routes`. + * It then resolves and normalizes the file path of the `routes` definition and returns it. + * + * @param tree - The virtual file system tree used by Angular schematics. + * @param mainFilePath - The path to the main entry file of the Angular application (e.g., `main.ts`). + * @returns The normalized workspace-relative path to the file where `routes` is defined, or `null` if not found. + * @throws If the `routes` import path is found but the file does not exist in the tree. + */ +export function findAppRoutesPath(tree: Tree, mainFilePath: string): Path | null { + const appConfigPath = getAppConfigPath(tree, mainFilePath); + if (!appConfigPath || !tree.exists(appConfigPath)) return null; + + const buffer = tree.read(appConfigPath); + if (!buffer) return null; + + const source = ts.createSourceFile( + appConfigPath, + buffer.toString('utf-8'), + ts.ScriptTarget.Latest, + true, + ); + + for (const stmt of source.statements) { + if (!ts.isImportDeclaration(stmt)) continue; + + const importClause = stmt.importClause; + if (!importClause?.namedBindings || !ts.isNamedImports(importClause.namedBindings)) continue; + + const isRoutesImport = importClause.namedBindings.elements.some( + el => el.name.getText() === 'routes', + ); + if (!isRoutesImport || !ts.isStringLiteral(stmt.moduleSpecifier)) continue; + + let importPath = stmt.moduleSpecifier.text; + + if (!importPath.endsWith('.ts')) { + importPath += '.ts'; + } + + const configDir = path.dirname(appConfigPath); + const resolvedFsPath = path.resolve(configDir, importPath); + const workspaceRelativePath = path.relative(process.cwd(), resolvedFsPath).replace(/\\/g, '/'); + + const normalizedPath = normalize(workspaceRelativePath); + + if (!tree.exists(normalizedPath)) { + throw new Error(`Cannot find routes file: ${normalizedPath}`); + } + + return normalizedPath; + } + + return null; +} + +/** + * Checks whether a specific provider is registered in the `providers` array of the + * standalone application configuration (typically within `app.config.ts`) in an Angular project. + * + * This function reads and parses the application configuration file, looks for the + * `providers` property in the configuration object, and checks whether it includes + * the specified provider name. + * + * @param host - The virtual file system tree used by Angular schematics. + * @param projectName - The name of the Angular project. + * @param providerName - The name of the provider to search for (as a string match). + * @returns A promise that resolves to `true` if the provider is found, otherwise `false`. + * @throws SchematicsException if the app config file cannot be read. + */ +export const hasProviderInStandaloneAppConfig = async ( + host: Tree, + projectName: string, + providerName: string, +): Promise => { + const mainFilePath = await getMainFilePath(host, projectName); + const appConfigPath = getAppConfigPath(host, mainFilePath); + const buffer = host.read(appConfigPath); + + if (!buffer) { + throw new SchematicsException(`Could not read file: ${appConfigPath}`); + } + + const source = ts.createSourceFile( + appConfigPath, + buffer.toString('utf-8'), + ts.ScriptTarget.Latest, + true, + ); + const callExpressions = source.statements + .flatMap(stmt => (ts.isVariableStatement(stmt) ? stmt.declarationList.declarations : [])) + .flatMap(decl => + decl.initializer && ts.isObjectLiteralExpression(decl.initializer) + ? decl.initializer.properties + : [], + ) + .filter(ts.isPropertyAssignment) + .filter(prop => prop.name.getText() === 'providers'); + + if (callExpressions.length === 0) return false; + + const providersArray = callExpressions[0].initializer as ts.ArrayLiteralExpression; + return providersArray.elements.some(el => el.getText().includes(providerName)); +}; + +/** + * Cleans up empty or invalid expressions (e.g., extra or trailing commas) from the + * `providers` array within a standalone Angular application configuration object. + * + * This function parses the source file's AST to locate variable declarations that + * define an object literal. It then searches for a `providers` property and removes + * any empty elements from its array literal, replacing it with a cleaned version. + * + * Typically used in Angular schematics to ensure the `providers` array in `app.config.ts` + * is free of empty slots after modifications. + * + * @param source - The TypeScript source file containing the app configuration. + * @param recorder - The recorder used to apply changes to the source file. + */ +export function cleanEmptyExprFromProviders(source: ts.SourceFile, recorder: UpdateRecorder): void { + const varStatements = findNodes(source, ts.isVariableStatement); + const printer = ts.createPrinter(); + + for (const stmt of varStatements) { + const declList = stmt.declarationList; + for (const decl of declList.declarations) { + if (!decl.initializer || !ts.isObjectLiteralExpression(decl.initializer)) continue; + + const obj = decl.initializer; + + const providersProp = obj.properties.find( + prop => + ts.isPropertyAssignment(prop) && + ts.isIdentifier(prop.name) && + prop.name.text === 'providers', + ) as ts.PropertyAssignment; + + if (!providersProp || !ts.isArrayLiteralExpression(providersProp.initializer)) continue; + + const arrayLiteral = providersProp.initializer; + const cleanedArray = removeEmptyElementsFromArrayLiteral(arrayLiteral); + + recorder.remove(arrayLiteral.getStart(), arrayLiteral.getWidth()); + recorder.insertLeft( + arrayLiteral.getStart(), + printer.printNode(ts.EmitHint.Expression, cleanedArray, source), + ); + } + } +} diff --git a/npm/ng-packs/packages/schematics/src/utils/workspace.ts b/npm/ng-packs/packages/schematics/src/utils/workspace.ts index ed53e482e8..8105a5e1e4 100644 --- a/npm/ng-packs/packages/schematics/src/utils/workspace.ts +++ b/npm/ng-packs/packages/schematics/src/utils/workspace.ts @@ -52,7 +52,7 @@ export async function resolveProject( // @typescript-eslint/no-explicit-any notFoundValue: T = NOT_FOUND_VALUE as unknown as any, ): Promise { - name = name || readWorkspaceSchema(tree).defaultProject || getFirstApplication(tree).name!; + name = name || getFirstApplication(tree).name!; const workspace = await getWorkspace(tree); let definition: Project['definition'] | undefined; diff --git a/npm/ng-packs/packages/setting-management/package.json b/npm/ng-packs/packages/setting-management/package.json index 776184b0a9..8e9c3b8a4a 100644 --- a/npm/ng-packs/packages/setting-management/package.json +++ b/npm/ng-packs/packages/setting-management/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.setting-management", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.components": "~9.1.0", - "@abp/ng.theme.shared": "~9.1.0", + "@abp/ng.components": "~9.2.0-rc.2", + "@abp/ng.theme.shared": "~9.2.0-rc.2", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/tenant-management/package.json b/npm/ng-packs/packages/tenant-management/package.json index f114aa0b34..9294148f66 100644 --- a/npm/ng-packs/packages/tenant-management/package.json +++ b/npm/ng-packs/packages/tenant-management/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.tenant-management", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.feature-management": "~9.1.0", - "@abp/ng.theme.shared": "~9.1.0", + "@abp/ng.feature-management": "~9.2.0-rc.2", + "@abp/ng.theme.shared": "~9.2.0-rc.2", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/theme-basic/package.json b/npm/ng-packs/packages/theme-basic/package.json index b7d4615dbc..9d98ed2798 100644 --- a/npm/ng-packs/packages/theme-basic/package.json +++ b/npm/ng-packs/packages/theme-basic/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.theme.basic", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.account.core": "~9.1.0", - "@abp/ng.theme.shared": "~9.1.0", + "@abp/ng.account.core": "~9.2.0-rc.2", + "@abp/ng.theme.shared": "~9.2.0-rc.2", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html index e537c26f34..9f4fdd900d 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html @@ -3,8 +3,7 @@ - + /> } @@ -13,7 +12,7 @@ @if (route.iconClass) { } - {{ route.name | abpLocalization }} + {{ route.name | abpLazyLocalization | async }} @@ -40,7 +39,7 @@ @if (route.iconClass) { } - {{ route.name | abpLocalization }} + {{ route.name | abpLazyLocalization | async }}
- +
} @@ -61,7 +58,7 @@ + /> } @@ -105,7 +102,7 @@ class="dropdown-menu dropdown-menu-start border-0 shadow-sm" [class.d-block]="smallScreen && dropdownSubmenu.isOpen()" > - + diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.ts index dfdca734a6..dc6b797446 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.ts @@ -2,6 +2,7 @@ import { ABP, RoutesService, TreeNode } from '@abp/ng.core'; import { Component, ElementRef, + inject, Input, QueryList, Renderer2, @@ -15,6 +16,9 @@ import { templateUrl: 'routes.component.html', }) export class RoutesComponent { + public readonly routesService = inject(RoutesService); + protected renderer = inject(Renderer2); + @Input() smallScreen?: boolean; @ViewChildren('childrenContainer') childrenContainers!: QueryList>; @@ -23,11 +27,6 @@ export class RoutesComponent { trackByFn: TrackByFunction> = (_, item) => item.name; - constructor( - public readonly routesService: RoutesService, - protected renderer: Renderer2, - ) {} - isDropdown(node: TreeNode) { return !node?.isLeaf || this.routesService.hasChildren(node.name); } diff --git a/npm/ng-packs/packages/theme-shared/package.json b/npm/ng-packs/packages/theme-shared/package.json index 66186e8145..e8a0a20a72 100644 --- a/npm/ng-packs/packages/theme-shared/package.json +++ b/npm/ng-packs/packages/theme-shared/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.theme.shared", - "version": "9.1.0", + "version": "9.2.0-rc.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.core": "~9.1.0", + "@abp/ng.core": "~9.2.0-rc.2", "@fortawesome/fontawesome-free": "^5.0.0", "@ng-bootstrap/ng-bootstrap": "~18.0.0", "@ngx-validate/core": "^0.2.0", diff --git a/npm/ng-packs/scripts/build-schematics.ts b/npm/ng-packs/scripts/build-schematics.ts index 6690b272b3..0699eb0567 100644 --- a/npm/ng-packs/scripts/build-schematics.ts +++ b/npm/ng-packs/scripts/build-schematics.ts @@ -23,10 +23,15 @@ const FILES_TO_COPY_AFTER_BUILD: (FileCopy | string)[] = [ { src: 'src/commands/create-lib/schema.json', dest: 'commands/create-lib/schema.json' }, { src: 'src/commands/change-theme/schema.json', dest: 'commands/change-theme/schema.json' }, { src: 'src/commands/create-lib/files-package', dest: 'commands/create-lib/files-package' }, + { src: 'src/commands/create-lib/files-package-standalone', dest: 'commands/create-lib/files-package-standalone' }, { src: 'src/commands/create-lib/files-secondary-entrypoint', dest: 'commands/create-lib/files-secondary-entrypoint', }, + { + src: 'src/commands/create-lib/files-secondary-entrypoint-standalone', + dest: 'commands/create-lib/files-secondary-entrypoint-standalone', + }, { src: 'src/commands/proxy-add/schema.json', dest: 'commands/proxy-add/schema.json' }, { src: 'src/commands/proxy-index/schema.json', dest: 'commands/proxy-index/schema.json' }, { src: 'src/commands/proxy-refresh/schema.json', dest: 'commands/proxy-refresh/schema.json' }, diff --git a/npm/packs/anchor-js/package.json b/npm/packs/anchor-js/package.json index 9d969caf2b..309f47fa0f 100644 --- a/npm/packs/anchor-js/package.json +++ b/npm/packs/anchor-js/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/anchor-js", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "anchor-js": "^5.0.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/aspnetcore.components.server.basictheme/package.json b/npm/packs/aspnetcore.components.server.basictheme/package.json index 12c71f64a7..48428e7e12 100644 --- a/npm/packs/aspnetcore.components.server.basictheme/package.json +++ b/npm/packs/aspnetcore.components.server.basictheme/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/aspnetcore.components.server.basictheme", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/aspnetcore.components.server.theming": "~9.1.0" + "@abp/aspnetcore.components.server.theming": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/aspnetcore.components.server.theming/package.json b/npm/packs/aspnetcore.components.server.theming/package.json index 21ade699fd..8ce906ce48 100644 --- a/npm/packs/aspnetcore.components.server.theming/package.json +++ b/npm/packs/aspnetcore.components.server.theming/package.json @@ -1,12 +1,12 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/aspnetcore.components.server.theming", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/bootstrap": "~9.1.0", - "@abp/font-awesome": "~9.1.0" + "@abp/bootstrap": "~9.2.0-rc.2", + "@abp/font-awesome": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json b/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json index 1698a9c7bb..f942547f1f 100644 --- a/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json +++ b/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/aspnetcore.mvc.ui.theme.basic", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.shared": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.shared": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json b/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json index c2eb417d62..0d90dbefdb 100644 --- a/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json +++ b/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/aspnetcore.mvc.ui.theme.shared", "repository": { "type": "git", @@ -10,21 +10,21 @@ "access": "public" }, "dependencies": { - "@abp/aspnetcore.mvc.ui": "~9.1.0-rc.3", - "@abp/bootstrap": "~9.1.0-rc.3", - "@abp/bootstrap-datepicker": "~9.1.0-rc.3", - "@abp/bootstrap-daterangepicker": "~9.1.0-rc.3", - "@abp/datatables.net-bs5": "~9.1.0-rc.3", - "@abp/font-awesome": "~9.1.0-rc.3", - "@abp/jquery-form": "~9.1.0-rc.3", - "@abp/jquery-validation-unobtrusive": "~9.1.0-rc.3", - "@abp/lodash": "~9.1.0-rc.3", - "@abp/luxon": "~9.1.0-rc.3", - "@abp/malihu-custom-scrollbar-plugin": "~9.1.0-rc.3", - "@abp/moment": "~9.1.0-rc.3", - "@abp/select2": "~9.1.0-rc.3", - "@abp/sweetalert2": "~9.1.0-rc.3", - "@abp/timeago": "~9.1.0-rc.3" + "@abp/aspnetcore.mvc.ui": "~9.2.0-rc.2", + "@abp/bootstrap": "~9.2.0-rc.2", + "@abp/bootstrap-datepicker": "~9.2.0-rc.2", + "@abp/bootstrap-daterangepicker": "~9.2.0-rc.2", + "@abp/datatables.net-bs5": "~9.2.0-rc.2", + "@abp/font-awesome": "~9.2.0-rc.2", + "@abp/jquery-form": "~9.2.0-rc.2", + "@abp/jquery-validation-unobtrusive": "~9.2.0-rc.2", + "@abp/lodash": "~9.2.0-rc.2", + "@abp/luxon": "~9.2.0-rc.2", + "@abp/malihu-custom-scrollbar-plugin": "~9.2.0-rc.2", + "@abp/moment": "~9.2.0-rc.2", + "@abp/select2": "~9.2.0-rc.2", + "@abp/sweetalert2": "~9.2.0-rc.2", + "@abp/timeago": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/aspnetcore.mvc.ui/package-lock.json b/npm/packs/aspnetcore.mvc.ui/package-lock.json index 9c165b8f9f..b427a42055 100644 --- a/npm/packs/aspnetcore.mvc.ui/package-lock.json +++ b/npm/packs/aspnetcore.mvc.ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "@abp/aspnetcore.mvc.ui", - "version": "9.1.0", + "version": "9.2.0-rc.2", "lockfileVersion": 1, "requires": true, "packages": { diff --git a/npm/packs/aspnetcore.mvc.ui/package.json b/npm/packs/aspnetcore.mvc.ui/package.json index da4cacc02f..7b229e1eda 100644 --- a/npm/packs/aspnetcore.mvc.ui/package.json +++ b/npm/packs/aspnetcore.mvc.ui/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/aspnetcore.mvc.ui", "repository": { "type": "git", diff --git a/npm/packs/blogging/package.json b/npm/packs/blogging/package.json index 92711041fa..882e893864 100644 --- a/npm/packs/blogging/package.json +++ b/npm/packs/blogging/package.json @@ -1,14 +1,14 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/blogging", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.shared": "~9.1.0", - "@abp/owl.carousel": "~9.1.0", - "@abp/prismjs": "~9.1.0", - "@abp/tui-editor": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.shared": "~9.2.0-rc.2", + "@abp/owl.carousel": "~9.2.0-rc.2", + "@abp/prismjs": "~9.2.0-rc.2", + "@abp/tui-editor": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/bootstrap-datepicker/package.json b/npm/packs/bootstrap-datepicker/package.json index dd0ff42756..01e2a5047a 100644 --- a/npm/packs/bootstrap-datepicker/package.json +++ b/npm/packs/bootstrap-datepicker/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/bootstrap-datepicker", "repository": { "type": "git", diff --git a/npm/packs/bootstrap-daterangepicker/package.json b/npm/packs/bootstrap-daterangepicker/package.json index d781d09684..f39becb110 100644 --- a/npm/packs/bootstrap-daterangepicker/package.json +++ b/npm/packs/bootstrap-daterangepicker/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/bootstrap-daterangepicker", "repository": { "type": "git", diff --git a/npm/packs/bootstrap/package.json b/npm/packs/bootstrap/package.json index 9a5113ce39..3fb5fae512 100644 --- a/npm/packs/bootstrap/package.json +++ b/npm/packs/bootstrap/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/bootstrap", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "bootstrap": "^5.3.3" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/chart.js/package.json b/npm/packs/chart.js/package.json index f8583b45f8..2e77962199 100644 --- a/npm/packs/chart.js/package.json +++ b/npm/packs/chart.js/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/chart.js", "publishConfig": { "access": "public" diff --git a/npm/packs/clipboard/package.json b/npm/packs/clipboard/package.json index a202577b70..1bf77e3417 100644 --- a/npm/packs/clipboard/package.json +++ b/npm/packs/clipboard/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/clipboard", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "clipboard": "^2.0.11" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/cms-kit.admin/package.json b/npm/packs/cms-kit.admin/package.json index 0598fb1846..3830180d79 100644 --- a/npm/packs/cms-kit.admin/package.json +++ b/npm/packs/cms-kit.admin/package.json @@ -1,16 +1,16 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/cms-kit.admin", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/codemirror": "~9.1.0", - "@abp/jstree": "~9.1.0", - "@abp/markdown-it": "~9.1.0", - "@abp/slugify": "~9.1.0", - "@abp/tui-editor": "~9.1.0", - "@abp/uppy": "~9.1.0" + "@abp/codemirror": "~9.2.0-rc.2", + "@abp/jstree": "~9.2.0-rc.2", + "@abp/markdown-it": "~9.2.0-rc.2", + "@abp/slugify": "~9.2.0-rc.2", + "@abp/tui-editor": "~9.2.0-rc.2", + "@abp/uppy": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/cms-kit.public/package.json b/npm/packs/cms-kit.public/package.json index 9b4cab1f3e..3e7794cb72 100644 --- a/npm/packs/cms-kit.public/package.json +++ b/npm/packs/cms-kit.public/package.json @@ -1,12 +1,12 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/cms-kit.public", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/highlight.js": "~9.1.0", - "@abp/star-rating-svg": "~9.1.0" + "@abp/highlight.js": "~9.2.0-rc.2", + "@abp/star-rating-svg": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/cms-kit/package.json b/npm/packs/cms-kit/package.json index cde6e5028c..f459b58e9c 100644 --- a/npm/packs/cms-kit/package.json +++ b/npm/packs/cms-kit/package.json @@ -1,12 +1,12 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/cms-kit", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/cms-kit.admin": "~9.1.0", - "@abp/cms-kit.public": "~9.1.0" + "@abp/cms-kit.admin": "~9.2.0-rc.2", + "@abp/cms-kit.public": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/codemirror/package.json b/npm/packs/codemirror/package.json index aee1494139..2b0aa3b39b 100644 --- a/npm/packs/codemirror/package.json +++ b/npm/packs/codemirror/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/codemirror", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "codemirror": "^5.65.1" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/core/package.json b/npm/packs/core/package.json index d3b43f9759..3c2419a697 100644 --- a/npm/packs/core/package.json +++ b/npm/packs/core/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/core", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/utils": "~9.1.0" + "@abp/utils": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/core/src/abp.js b/npm/packs/core/src/abp.js index 05c9f3bbe5..f117c4d5f1 100644 --- a/npm/packs/core/src/abp.js +++ b/npm/packs/core/src/abp.js @@ -4,7 +4,9 @@ var abp = abp || {}; /* Application paths *****************************************/ //Current application root path (including virtual directory if exists). - abp.appPath = abp.appPath || '/'; + var baseElement = document.querySelector('base'); + var baseHref = baseElement ? baseElement.getAttribute('href') : null; + abp.appPath = baseHref || abp.appPath || '/'; abp.pageLoadTime = new Date(); @@ -81,7 +83,7 @@ var abp = abp || {}; if (resource) { return resource; } - + var legacySource = abp.localization.values[resourceName]; if (legacySource) { return { @@ -89,11 +91,11 @@ var abp = abp || {}; baseResources: [] }; } - - abp.log.warn('Could not find localization source: ' + resourceName); + + abp.log.warn('Could not find localization source: ' + resourceName); return null; }; - + abp.localization.internal.localize = function (key, sourceName) { var resource = abp.localization.internal.getResource(sourceName); if (!resource){ @@ -104,7 +106,7 @@ var abp = abp || {}; } var value = resource.texts[key]; - if (value === undefined) { + if (value === undefined) { for (var i = 0; i < resource.baseResources.length; i++){ var basedArguments = Array.prototype.slice.call(arguments, 0); basedArguments[1] = resource.baseResources[i]; @@ -114,7 +116,7 @@ var abp = abp || {}; return result; } } - + return { value: key, found: false @@ -135,7 +137,7 @@ var abp = abp || {}; if (sourceName === '_') { //A convention to suppress the localization return key; } - + if (sourceName) { return abp.localization.internal.localize.apply(this, arguments).value; } @@ -750,7 +752,11 @@ var abp = abp || {}; abp.clock.supportsMultipleTimezone = function () { return abp.clock.kind === 'Utc'; - }; + } + + abp.clock.timeZone = function () { + return abp.setting.get('Abp.Timing.TimeZone') || abp.clock.browserTimeZone(); + } // Normalize Date object or date string to standard string format that will be sent to server abp.clock.normalizeToString = function (date) { @@ -763,32 +769,42 @@ var abp = abp || {}; return date; } - if (abp.clock.kind === 'Utc') { - return dateObj.toISOString(); - } - function padZero(num) { return num < 10 ? '0' + num : num; } - function padMilliseconds(num) { - if (num < 10) return '00' + num; - if (num < 100) return '0' + num; - return num; + var addZulu = false; + if (abp.clock.supportsMultipleTimezone()) { + var timeZone = abp.clock.timeZone(); + var now = new Date(); + var formattedDate = now.toLocaleString('en-US', { timeZone: timeZone, timeZoneName: 'longOffset' }); + var match = formattedDate.match(/GMT([+-]\d+)/); + var targetOffsetHours = match ? parseInt(match[1], 10) : 0; + dateObj = new Date(dateObj.getTime() - (targetOffsetHours * 60 * 60 * 1000)); + addZulu = true; } - - // yyyy-MM-ddTHH:mm:ss.SSS + + // yyyy-MM-DDTHH:mm:ss return dateObj.getFullYear() + '-' + - padZero(dateObj.getMonth() + 1) + '-' + - padZero(dateObj.getDate()) + 'T' + - padZero(dateObj.getHours()) + ':' + - padZero(dateObj.getMinutes()) + ':' + - padZero(dateObj.getSeconds()) + '.' + - padMilliseconds(dateObj.getMilliseconds()); + padZero(dateObj.getMonth() + 1) + '-' + + padZero(dateObj.getDate()) + 'T' + + padZero(dateObj.getHours()) + ':' + + padZero(dateObj.getMinutes()) + ':' + + padZero(dateObj.getSeconds()) + (addZulu ? 'Z' : ''); + }; + + // Default options for toLocaleString + abp.clock.toLocaleStringOptions = abp.clock.toLocaleStringOptions || { + "year": "numeric", + "month": "long", + "day": "numeric", + "hour": "numeric", + "minute": "numeric", + "second": "numeric" }; // Normalize date string to locale date string that will be displayed to user - abp.clock.normalizeToLocaleString = function (dateString) { + abp.clock.normalizeToLocaleString = function (dateString, options) { if (!dateString) { return dateString; } @@ -797,11 +813,36 @@ var abp = abp || {}; if (isNaN(date)) { return dateString; } - - //TODO: Get timezone setting and pass it to toLocaleString - return date.toLocaleString(); + + var culture = abp.localization.currentCulture.cultureName; + options = options || abp.clock.toLocaleStringOptions; + if (abp.clock.supportsMultipleTimezone()) { + var timezone = abp.clock.timeZone(); + if (timezone) { + return date.toLocaleString(culture, Object.assign({}, options, { timeZone: timezone })); + } + } + return date.toLocaleString(culture, options); } + abp.clock.browserTimeZone = function () { + return Intl.DateTimeFormat().resolvedOptions().timeZone; + } + + abp.clock.trySetBrowserTimeZoneToCookie = true; + + abp.clock.setBrowserTimeZoneToCookie = function () { + if (!abp.clock.trySetBrowserTimeZoneToCookie || !abp.clock.supportsMultipleTimezone() || abp.currentUser.isAuthenticated) { + return; + } + + abp.utils.setCookieValue('__timezone', abp.clock.browserTimeZone(), new Date(new Date().setFullYear(new Date().getFullYear() + 1)), '/'); + } + + abp.event.on('abp.configurationInitialized', function () { + abp.clock.setBrowserTimeZoneToCookie(); + }); + /* FEATURES *************************************************/ abp.features = abp.features || {}; diff --git a/npm/packs/cropperjs/package.json b/npm/packs/cropperjs/package.json index ba141a7139..3c36153e55 100644 --- a/npm/packs/cropperjs/package.json +++ b/npm/packs/cropperjs/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/cropperjs", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "cropperjs": "^1.6.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/datatables.net-bs4/package.json b/npm/packs/datatables.net-bs4/package.json index 40eea480dd..2df39398db 100644 --- a/npm/packs/datatables.net-bs4/package.json +++ b/npm/packs/datatables.net-bs4/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/datatables.net-bs4", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/datatables.net": "~9.1.0", + "@abp/datatables.net": "~9.2.0-rc.2", "datatables.net-bs4": "^2.1.8" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/datatables.net-bs5/package.json b/npm/packs/datatables.net-bs5/package.json index 1959b96048..ca4890c289 100644 --- a/npm/packs/datatables.net-bs5/package.json +++ b/npm/packs/datatables.net-bs5/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/datatables.net-bs5", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/datatables.net": "~9.1.0", + "@abp/datatables.net": "~9.2.0-rc.2", "datatables.net-bs5": "^2.1.8" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/datatables.net/package.json b/npm/packs/datatables.net/package.json index 5c996ac641..8e74431ef0 100644 --- a/npm/packs/datatables.net/package.json +++ b/npm/packs/datatables.net/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/datatables.net", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~9.1.0", + "@abp/jquery": "~9.2.0-rc.2", "datatables.net": "^2.1.8" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/docs/package.json b/npm/packs/docs/package.json index 406e6c55c3..9917675e46 100644 --- a/npm/packs/docs/package.json +++ b/npm/packs/docs/package.json @@ -1,15 +1,15 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/docs", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/anchor-js": "~9.1.0", - "@abp/clipboard": "~9.1.0", - "@abp/malihu-custom-scrollbar-plugin": "~9.1.0", - "@abp/popper.js": "~9.1.0", - "@abp/prismjs": "~9.1.0" + "@abp/anchor-js": "~9.2.0-rc.2", + "@abp/clipboard": "~9.2.0-rc.2", + "@abp/malihu-custom-scrollbar-plugin": "~9.2.0-rc.2", + "@abp/popper.js": "~9.2.0-rc.2", + "@abp/prismjs": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/flag-icon-css/package.json b/npm/packs/flag-icon-css/package.json index 56cf49fb2a..cd84a90f7a 100644 --- a/npm/packs/flag-icon-css/package.json +++ b/npm/packs/flag-icon-css/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/flag-icon-css", "publishConfig": { "access": "public" diff --git a/npm/packs/flag-icons/package.json b/npm/packs/flag-icons/package.json index aacd2e2b7f..b054c15886 100644 --- a/npm/packs/flag-icons/package.json +++ b/npm/packs/flag-icons/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/flag-icons", "publishConfig": { "access": "public" diff --git a/npm/packs/font-awesome/package.json b/npm/packs/font-awesome/package.json index 7d6960cb29..255935793a 100644 --- a/npm/packs/font-awesome/package.json +++ b/npm/packs/font-awesome/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/font-awesome", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "@fortawesome/fontawesome-free": "^6.6.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/highlight.js/package.json b/npm/packs/highlight.js/package.json index 7ebd7ffb49..5219c0d9c6 100644 --- a/npm/packs/highlight.js/package.json +++ b/npm/packs/highlight.js/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/highlight.js", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "@highlightjs/cdn-assets": "~11.10.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jquery-form/package.json b/npm/packs/jquery-form/package.json index 2dd33578e5..953fd44d13 100644 --- a/npm/packs/jquery-form/package.json +++ b/npm/packs/jquery-form/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/jquery-form", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~9.1.0", + "@abp/jquery": "~9.2.0-rc.2", "jquery-form": "^4.3.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jquery-validation-unobtrusive/package.json b/npm/packs/jquery-validation-unobtrusive/package.json index 3f095cf7b2..fd1dcadca9 100644 --- a/npm/packs/jquery-validation-unobtrusive/package.json +++ b/npm/packs/jquery-validation-unobtrusive/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/jquery-validation-unobtrusive", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery-validation": "~9.1.0", + "@abp/jquery-validation": "~9.2.0-rc.2", "jquery-validation-unobtrusive": "^4.0.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jquery-validation/package.json b/npm/packs/jquery-validation/package.json index 0d0556341d..f58b34119e 100644 --- a/npm/packs/jquery-validation/package.json +++ b/npm/packs/jquery-validation/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/jquery-validation", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~9.1.0", + "@abp/jquery": "~9.2.0-rc.2", "jquery-validation": "^1.21.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jquery/package.json b/npm/packs/jquery/package.json index 0960625744..78745784a0 100644 --- a/npm/packs/jquery/package.json +++ b/npm/packs/jquery/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/jquery", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "jquery": "~3.7.1" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jstree/package.json b/npm/packs/jstree/package.json index 71d103fd03..f87d2fca1f 100644 --- a/npm/packs/jstree/package.json +++ b/npm/packs/jstree/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/jstree", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "~9.1.0", + "@abp/jquery": "~9.2.0-rc.2", "jstree": "^3.3.17" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/lodash/package.json b/npm/packs/lodash/package.json index b9c68b3460..1c3d535f61 100644 --- a/npm/packs/lodash/package.json +++ b/npm/packs/lodash/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/lodash", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "lodash": "^4.17.21" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/luxon/package.json b/npm/packs/luxon/package.json index e67978142c..c0ab44403f 100644 --- a/npm/packs/luxon/package.json +++ b/npm/packs/luxon/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/luxon", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "luxon": "^3.5.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/luxon/src/abp.luxon.js b/npm/packs/luxon/src/abp.luxon.js index b04de7cadf..40b3f965b8 100644 --- a/npm/packs/luxon/src/abp.luxon.js +++ b/npm/packs/luxon/src/abp.luxon.js @@ -32,7 +32,7 @@ var abp = abp || {}; .fromFormat( getObjectValue(form, field), abp.localization.currentCulture.dateTimeFormat.shortDatePattern, - {locale: abp.localization.currentCulture.cultureName} + { locale: abp.localization.currentCulture.cultureName } ); if (!dateTime.invalid) { @@ -43,4 +43,60 @@ var abp = abp || {}; return form; } + // Normalize Date object or date string to standard string format that will be sent to server + abp.clock.normalizeToString = function (date) { + if (!date) { + return date; + } + + var dateObj = date instanceof Date ? date : new Date(date); + if (isNaN(dateObj)) { + return date; + } + + var timeZone = abp.clock.timeZone(); + if (abp.clock.supportsMultipleTimezone() && timeZone) { + return luxon.DateTime.fromObject({ + year: dateObj.getFullYear(), + month: dateObj.getMonth() + 1, + day: dateObj.getDate(), + hour: dateObj.getHours(), + minute: dateObj.getMinutes(), + second: dateObj.getSeconds() + }, { zone: timeZone }).setZone("utc").toISO(); + } else { + return luxon.DateTime.fromJSDate(dateObj).toFormat("yyyy-MM-dd'T'HH:mm:ss"); + } + }; + + // Normalize date string to locale date string that will be displayed to user + abp.clock.normalizeToLocaleString = function (dateString, options) { + if (!dateString) { + return dateString; + } + + var date = new Date(dateString); + if (isNaN(date)) { + return dateString; + } + + options = options || abp.clock.toLocaleStringOptions; + if (abp.clock.supportsMultipleTimezone()) { + var timezone = abp.clock.timeZone(); + if (timezone) { + return luxon.DateTime.fromJSDate(date) + .setZone(timezone) + .setLocale(abp.localization.currentCulture.cultureName) + .toLocaleString(options); + } + } + + return luxon.DateTime.fromJSDate(date) + .setLocale(abp.localization.currentCulture.cultureName) + .toLocaleString(options); + } + + abp.clock.browserTimeZone = function () { + return luxon.DateTime.local().zoneName; + } })(jQuery); diff --git a/npm/packs/malihu-custom-scrollbar-plugin/package.json b/npm/packs/malihu-custom-scrollbar-plugin/package.json index 0910318eee..b1c99662ed 100644 --- a/npm/packs/malihu-custom-scrollbar-plugin/package.json +++ b/npm/packs/malihu-custom-scrollbar-plugin/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/malihu-custom-scrollbar-plugin", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "malihu-custom-scrollbar-plugin": "^3.1.5" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/markdown-it/package.json b/npm/packs/markdown-it/package.json index a0d177f0dc..93bb465699 100644 --- a/npm/packs/markdown-it/package.json +++ b/npm/packs/markdown-it/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/markdown-it", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "markdown-it": "^14.1.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/moment/package.json b/npm/packs/moment/package.json index cc72bf86fe..6163def8e5 100644 --- a/npm/packs/moment/package.json +++ b/npm/packs/moment/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/moment", "repository": { "type": "git", diff --git a/npm/packs/owl.carousel/package.json b/npm/packs/owl.carousel/package.json index dd552235f1..80b9f44f6c 100644 --- a/npm/packs/owl.carousel/package.json +++ b/npm/packs/owl.carousel/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/owl.carousel", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "owl.carousel": "^2.3.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/popper.js/package.json b/npm/packs/popper.js/package.json index 4183859dc6..08a4387b45 100644 --- a/npm/packs/popper.js/package.json +++ b/npm/packs/popper.js/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/popper.js", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "@popperjs/core": "^2.11.8" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/prismjs/package.json b/npm/packs/prismjs/package.json index 90472e3825..b3112ab1f2 100644 --- a/npm/packs/prismjs/package.json +++ b/npm/packs/prismjs/package.json @@ -1,12 +1,12 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/prismjs", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/clipboard": "~9.1.0", - "@abp/core": "~9.1.0", + "@abp/clipboard": "~9.2.0-rc.2", + "@abp/core": "~9.2.0-rc.2", "prismjs": "^1.29.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/qrcode/package.json b/npm/packs/qrcode/package.json index 3a7ebf11cb..c38867b0ee 100644 --- a/npm/packs/qrcode/package.json +++ b/npm/packs/qrcode/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/qrcode", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0" + "@abp/core": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/select2/package.json b/npm/packs/select2/package.json index d49623735f..c19ae01eaf 100644 --- a/npm/packs/select2/package.json +++ b/npm/packs/select2/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/select2", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "select2": "^4.0.13" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/signalr/package.json b/npm/packs/signalr/package.json index 3fdfc65ff5..10bd7fa4d4 100644 --- a/npm/packs/signalr/package.json +++ b/npm/packs/signalr/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/signalr", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "@microsoft/signalr": "~8.0.7" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/slugify/package.json b/npm/packs/slugify/package.json index def5d8810a..cfb520e9ff 100644 --- a/npm/packs/slugify/package.json +++ b/npm/packs/slugify/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/slugify", "publishConfig": { "access": "public" diff --git a/npm/packs/star-rating-svg/package.json b/npm/packs/star-rating-svg/package.json index f4dfd3f4a4..8400694738 100644 --- a/npm/packs/star-rating-svg/package.json +++ b/npm/packs/star-rating-svg/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/star-rating-svg", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "~9.1.0", + "@abp/jquery": "~9.2.0-rc.2", "star-rating-svg": "^3.5.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/sweetalert2/package.json b/npm/packs/sweetalert2/package.json index 0718375fd6..e27f7ebf19 100644 --- a/npm/packs/sweetalert2/package.json +++ b/npm/packs/sweetalert2/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/sweetalert2", "publishConfig": { "access": "public" @@ -10,7 +10,7 @@ "directory": "npm/packs/sweetalert2" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "sweetalert2": "^11.14.1" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/timeago/package.json b/npm/packs/timeago/package.json index f67212ca13..87a05372a1 100644 --- a/npm/packs/timeago/package.json +++ b/npm/packs/timeago/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/timeago", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~9.1.0", + "@abp/jquery": "~9.2.0-rc.2", "timeago": "^1.6.7" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/toastr/package.json b/npm/packs/toastr/package.json index 3d4a144b07..54fbaa783f 100644 --- a/npm/packs/toastr/package.json +++ b/npm/packs/toastr/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/toastr", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~9.1.0", + "@abp/jquery": "~9.2.0-rc.2", "toastr": "^2.1.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/tui-editor/package.json b/npm/packs/tui-editor/package.json index 10e0b3e86f..4fdedfd141 100644 --- a/npm/packs/tui-editor/package.json +++ b/npm/packs/tui-editor/package.json @@ -1,12 +1,12 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/tui-editor", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "~9.1.0", - "@abp/prismjs": "~9.1.0" + "@abp/jquery": "~9.2.0-rc.2", + "@abp/prismjs": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/uppy/package.json b/npm/packs/uppy/package.json index 77192639e8..a1f929116c 100644 --- a/npm/packs/uppy/package.json +++ b/npm/packs/uppy/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/uppy", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "uppy": "^4.4.1" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/utils/package.json b/npm/packs/utils/package.json index f332e16e93..c21a5cbc39 100644 --- a/npm/packs/utils/package.json +++ b/npm/packs/utils/package.json @@ -1,6 +1,6 @@ { "name": "@abp/utils", - "version": "9.1.0", + "version": "9.2.0-rc.2", "scripts": { "prepublishOnly": "yarn install --ignore-scripts && node prepublish.js", "ng": "ng", diff --git a/npm/packs/vee-validate/package.json b/npm/packs/vee-validate/package.json index 7f7d993c12..18156c69ba 100644 --- a/npm/packs/vee-validate/package.json +++ b/npm/packs/vee-validate/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/vee-validate", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/vue": "~9.1.0", + "@abp/vue": "~9.2.0-rc.2", "vee-validate": "~3.4.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/virtual-file-explorer/package.json b/npm/packs/virtual-file-explorer/package.json index f1325521b4..27f5adde17 100644 --- a/npm/packs/virtual-file-explorer/package.json +++ b/npm/packs/virtual-file-explorer/package.json @@ -1,12 +1,12 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/virtual-file-explorer", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/clipboard": "~9.1.0", - "@abp/prismjs": "~9.1.0" + "@abp/clipboard": "~9.2.0-rc.2", + "@abp/prismjs": "~9.2.0-rc.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/vue/package.json b/npm/packs/vue/package.json index 6627466172..3a2e26d742 100644 --- a/npm/packs/vue/package.json +++ b/npm/packs/vue/package.json @@ -1,5 +1,5 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/vue", "publishConfig": { "access": "public" diff --git a/npm/packs/zxcvbn/package.json b/npm/packs/zxcvbn/package.json index 713bd926bd..6f56b4241a 100644 --- a/npm/packs/zxcvbn/package.json +++ b/npm/packs/zxcvbn/package.json @@ -1,11 +1,11 @@ { - "version": "9.1.0", + "version": "9.2.0-rc.2", "name": "@abp/zxcvbn", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~9.1.0", + "@abp/core": "~9.2.0-rc.2", "zxcvbn": "^4.4.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index 9244191d01..e2d65a631c 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -99,6 +99,7 @@ $projects = ( "framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect", "framework/src/Volo.Abp.AspNetCore.Abstractions", "framework/src/Volo.Abp.AspNetCore", + "framework/src/Volo.Abp.AspNetCore.Bundling", "framework/src/Volo.Abp.AspNetCore.Mvc.Dapr", "framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus", "framework/src/Volo.Abp.AspNetCore.Components", @@ -111,6 +112,7 @@ $projects = ( "framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming.Bundling", "framework/src/Volo.Abp.AspNetCore.Components.Server", "framework/src/Volo.Abp.AspNetCore.Components.Server.Theming", + "framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling", "framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Theming", "framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor.Theming.Bundling", "framework/src/Volo.Abp.AspNetCore.MultiTenancy", diff --git a/source-code/Volo.Abp.Account.SourceCode/Volo.Abp.Account.SourceCode.zip b/source-code/Volo.Abp.Account.SourceCode/Volo.Abp.Account.SourceCode.zip index 1e82cb36f3..ab0523e979 100644 Binary files a/source-code/Volo.Abp.Account.SourceCode/Volo.Abp.Account.SourceCode.zip and b/source-code/Volo.Abp.Account.SourceCode/Volo.Abp.Account.SourceCode.zip differ diff --git a/source-code/Volo.Abp.AuditLogging.SourceCode/Volo.Abp.AuditLogging.SourceCode.zip b/source-code/Volo.Abp.AuditLogging.SourceCode/Volo.Abp.AuditLogging.SourceCode.zip index 2c440abed4..52643e464c 100644 Binary files a/source-code/Volo.Abp.AuditLogging.SourceCode/Volo.Abp.AuditLogging.SourceCode.zip and b/source-code/Volo.Abp.AuditLogging.SourceCode/Volo.Abp.AuditLogging.SourceCode.zip differ diff --git a/source-code/Volo.Abp.BackgroundJobs.SourceCode/Volo.Abp.BackgroundJobs.SourceCode.zip b/source-code/Volo.Abp.BackgroundJobs.SourceCode/Volo.Abp.BackgroundJobs.SourceCode.zip index f8e59433ec..3f882cfb3d 100644 Binary files a/source-code/Volo.Abp.BackgroundJobs.SourceCode/Volo.Abp.BackgroundJobs.SourceCode.zip and b/source-code/Volo.Abp.BackgroundJobs.SourceCode/Volo.Abp.BackgroundJobs.SourceCode.zip differ diff --git a/source-code/Volo.Abp.BasicTheme.SourceCode/Volo.Abp.BasicTheme.SourceCode.zip b/source-code/Volo.Abp.BasicTheme.SourceCode/Volo.Abp.BasicTheme.SourceCode.zip index 8bbf604b22..0c98835f9c 100644 Binary files a/source-code/Volo.Abp.BasicTheme.SourceCode/Volo.Abp.BasicTheme.SourceCode.zip and b/source-code/Volo.Abp.BasicTheme.SourceCode/Volo.Abp.BasicTheme.SourceCode.zip differ diff --git a/source-code/Volo.Abp.BlobStoring.Database.SourceCode/Volo.Abp.BlobStoring.Database.SourceCode.zip b/source-code/Volo.Abp.BlobStoring.Database.SourceCode/Volo.Abp.BlobStoring.Database.SourceCode.zip index fcc61e518b..f9f5d58aa8 100644 Binary files a/source-code/Volo.Abp.BlobStoring.Database.SourceCode/Volo.Abp.BlobStoring.Database.SourceCode.zip and b/source-code/Volo.Abp.BlobStoring.Database.SourceCode/Volo.Abp.BlobStoring.Database.SourceCode.zip differ diff --git a/source-code/Volo.Abp.FeatureManagement.SourceCode/Volo.Abp.FeatureManagement.SourceCode.zip b/source-code/Volo.Abp.FeatureManagement.SourceCode/Volo.Abp.FeatureManagement.SourceCode.zip index 1d866503e1..9d3eca0cd1 100644 Binary files a/source-code/Volo.Abp.FeatureManagement.SourceCode/Volo.Abp.FeatureManagement.SourceCode.zip and b/source-code/Volo.Abp.FeatureManagement.SourceCode/Volo.Abp.FeatureManagement.SourceCode.zip differ diff --git a/source-code/Volo.Abp.Identity.SourceCode/Volo.Abp.Identity.SourceCode.zip b/source-code/Volo.Abp.Identity.SourceCode/Volo.Abp.Identity.SourceCode.zip index 8b7e25811b..84938ef264 100644 Binary files a/source-code/Volo.Abp.Identity.SourceCode/Volo.Abp.Identity.SourceCode.zip and b/source-code/Volo.Abp.Identity.SourceCode/Volo.Abp.Identity.SourceCode.zip differ diff --git a/source-code/Volo.Abp.IdentityServer.SourceCode/Volo.Abp.IdentityServer.SourceCode.zip b/source-code/Volo.Abp.IdentityServer.SourceCode/Volo.Abp.IdentityServer.SourceCode.zip index c3c1c83ab4..cdb2c0a744 100644 Binary files a/source-code/Volo.Abp.IdentityServer.SourceCode/Volo.Abp.IdentityServer.SourceCode.zip and b/source-code/Volo.Abp.IdentityServer.SourceCode/Volo.Abp.IdentityServer.SourceCode.zip differ diff --git a/source-code/Volo.Abp.OpenIddict.SourceCode/Volo.Abp.OpenIddict.SourceCode.zip b/source-code/Volo.Abp.OpenIddict.SourceCode/Volo.Abp.OpenIddict.SourceCode.zip index 0b8fa88db7..19ffa8d35f 100644 Binary files a/source-code/Volo.Abp.OpenIddict.SourceCode/Volo.Abp.OpenIddict.SourceCode.zip and b/source-code/Volo.Abp.OpenIddict.SourceCode/Volo.Abp.OpenIddict.SourceCode.zip differ diff --git a/source-code/Volo.Abp.PermissionManagement.SourceCode/Volo.Abp.PermissionManagement.SourceCode.zip b/source-code/Volo.Abp.PermissionManagement.SourceCode/Volo.Abp.PermissionManagement.SourceCode.zip index 2a67710260..2d91508aad 100644 Binary files a/source-code/Volo.Abp.PermissionManagement.SourceCode/Volo.Abp.PermissionManagement.SourceCode.zip and b/source-code/Volo.Abp.PermissionManagement.SourceCode/Volo.Abp.PermissionManagement.SourceCode.zip differ diff --git a/source-code/Volo.Abp.SettingManagement.SourceCode/Volo.Abp.SettingManagement.SourceCode.zip b/source-code/Volo.Abp.SettingManagement.SourceCode/Volo.Abp.SettingManagement.SourceCode.zip index 787b29fcf5..654034a512 100644 Binary files a/source-code/Volo.Abp.SettingManagement.SourceCode/Volo.Abp.SettingManagement.SourceCode.zip and b/source-code/Volo.Abp.SettingManagement.SourceCode/Volo.Abp.SettingManagement.SourceCode.zip differ diff --git a/source-code/Volo.Abp.TenantManagement.SourceCode/Volo.Abp.TenantManagement.SourceCode.zip b/source-code/Volo.Abp.TenantManagement.SourceCode/Volo.Abp.TenantManagement.SourceCode.zip index 9020cc0a8f..333d602e7b 100644 Binary files a/source-code/Volo.Abp.TenantManagement.SourceCode/Volo.Abp.TenantManagement.SourceCode.zip and b/source-code/Volo.Abp.TenantManagement.SourceCode/Volo.Abp.TenantManagement.SourceCode.zip differ diff --git a/source-code/Volo.Abp.Users.SourceCode/Volo.Abp.Users.SourceCode.zip b/source-code/Volo.Abp.Users.SourceCode/Volo.Abp.Users.SourceCode.zip index 78d4df97a3..03a5bc2f2c 100644 Binary files a/source-code/Volo.Abp.Users.SourceCode/Volo.Abp.Users.SourceCode.zip and b/source-code/Volo.Abp.Users.SourceCode/Volo.Abp.Users.SourceCode.zip differ diff --git a/source-code/Volo.Abp.VirtualFileExplorer.SourceCode/Volo.Abp.VirtualFileExplorer.SourceCode.zip b/source-code/Volo.Abp.VirtualFileExplorer.SourceCode/Volo.Abp.VirtualFileExplorer.SourceCode.zip index c293c30d76..4293d79605 100644 Binary files a/source-code/Volo.Abp.VirtualFileExplorer.SourceCode/Volo.Abp.VirtualFileExplorer.SourceCode.zip and b/source-code/Volo.Abp.VirtualFileExplorer.SourceCode/Volo.Abp.VirtualFileExplorer.SourceCode.zip differ diff --git a/source-code/Volo.Blogging.SourceCode/Volo.Blogging.SourceCode.zip b/source-code/Volo.Blogging.SourceCode/Volo.Blogging.SourceCode.zip index 3d24e20657..b72564fb51 100644 Binary files a/source-code/Volo.Blogging.SourceCode/Volo.Blogging.SourceCode.zip and b/source-code/Volo.Blogging.SourceCode/Volo.Blogging.SourceCode.zip differ diff --git a/source-code/Volo.ClientSimulation.SourceCode/Volo.ClientSimulation.SourceCode.zip b/source-code/Volo.ClientSimulation.SourceCode/Volo.ClientSimulation.SourceCode.zip index 69dc9ef644..641ca18e2d 100644 Binary files a/source-code/Volo.ClientSimulation.SourceCode/Volo.ClientSimulation.SourceCode.zip and b/source-code/Volo.ClientSimulation.SourceCode/Volo.ClientSimulation.SourceCode.zip differ diff --git a/source-code/Volo.CmsKit.SourceCode/Volo.CmsKit.SourceCode.zip b/source-code/Volo.CmsKit.SourceCode/Volo.CmsKit.SourceCode.zip index fad3b15607..60f7421652 100644 Binary files a/source-code/Volo.CmsKit.SourceCode/Volo.CmsKit.SourceCode.zip and b/source-code/Volo.CmsKit.SourceCode/Volo.CmsKit.SourceCode.zip differ diff --git a/source-code/Volo.Docs.SourceCode/Volo.Docs.SourceCode.zip b/source-code/Volo.Docs.SourceCode/Volo.Docs.SourceCode.zip index 33092e2f94..25996ff00d 100644 Binary files a/source-code/Volo.Docs.SourceCode/Volo.Docs.SourceCode.zip and b/source-code/Volo.Docs.SourceCode/Volo.Docs.SourceCode.zip differ diff --git a/templates/app-nolayers/angular/package.json b/templates/app-nolayers/angular/package.json index 7151d2898b..5c1ce01ab6 100644 --- a/templates/app-nolayers/angular/package.json +++ b/templates/app-nolayers/angular/package.json @@ -12,15 +12,15 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~9.1.0", - "@abp/ng.components": "~9.1.0", - "@abp/ng.core": "~9.1.0", - "@abp/ng.identity": "~9.1.0", - "@abp/ng.oauth": "~9.1.0", - "@abp/ng.setting-management": "~9.1.0", - "@abp/ng.tenant-management": "~9.1.0", - "@abp/ng.theme.lepton-x": "~4.1.0", - "@abp/ng.theme.shared": "~9.1.0", + "@abp/ng.account": "~9.2.0-rc.2", + "@abp/ng.components": "~9.2.0-rc.2", + "@abp/ng.core": "~9.2.0-rc.2", + "@abp/ng.identity": "~9.2.0-rc.2", + "@abp/ng.oauth": "~9.2.0-rc.2", + "@abp/ng.setting-management": "~9.2.0-rc.2", + "@abp/ng.tenant-management": "~9.2.0-rc.2", + "@abp/ng.theme.lepton-x": "~4.2.0-rc.2", + "@abp/ng.theme.shared": "~9.2.0-rc.2", "@angular/animations": "~19.1.0", "@angular/common": "~19.1.0", "@angular/compiler": "~19.1.0", @@ -36,7 +36,7 @@ "zone.js": "~0.15.0" }, "devDependencies": { - "@abp/ng.schematics": "~9.1.0", + "@abp/ng.schematics": "~9.2.0-rc.2", "@angular-devkit/build-angular": "~19.1.0", "@angular-eslint/builder": "~19.0.0", "@angular-eslint/eslint-plugin": "~19.0.0", diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj index 32cd17a78e..ee35a6e0e0 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj @@ -8,10 +8,10 @@ - - - - + + + + @@ -82,7 +82,7 @@ - + diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json index c8a062a98d..31595a69c3 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.components.server.leptonxlitetheme": "~4.1.0", - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0" + "@abp/aspnetcore.components.server.leptonxlitetheme": "~4.2.0-rc.2", + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj index 7761bbde29..a76d39682f 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj @@ -8,10 +8,10 @@ - - - - + + + + @@ -83,11 +83,11 @@ - + - + runtime; build; native; contentfiles; analyzers compile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/package.json index a9583936e1..eb2b1958bc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~4.2.0-rc.2" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj index 34eeb42de4..3ecc615439 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj @@ -9,10 +9,10 @@ - - - - + + + + diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.Mongo.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.Mongo.csproj index c42c2e72f1..47ac168149 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.Mongo.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.Mongo.csproj @@ -8,12 +8,12 @@ - + - - + + @@ -79,7 +79,7 @@ - + diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/package.json index b1143bb98a..2f8a6332f7 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.csproj index 8d3698cb1b..065ffb9552 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.csproj @@ -8,12 +8,12 @@ - + - - + + @@ -80,11 +80,11 @@ - + - + runtime; build; native; contentfiles; analyzers compile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/package.json index b1143bb98a..2f8a6332f7 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyCompanyName.MyProjectName.Blazor.WebAssembly.Shared.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyCompanyName.MyProjectName.Blazor.WebAssembly.Shared.csproj index d086ca8003..fa43424fa6 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyCompanyName.MyProjectName.Blazor.WebAssembly.Shared.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Shared/MyCompanyName.MyProjectName.Blazor.WebAssembly.Shared.csproj @@ -29,7 +29,7 @@ - + diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyCompanyName.MyProjectName.Host.Mongo.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyCompanyName.MyProjectName.Host.Mongo.csproj index a1436f339f..1e5069d33c 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyCompanyName.MyProjectName.Host.Mongo.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyCompanyName.MyProjectName.Host.Mongo.csproj @@ -8,8 +8,8 @@ - - + + @@ -74,7 +74,7 @@ - + diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/package.json index b1143bb98a..2f8a6332f7 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyCompanyName.MyProjectName.Host.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyCompanyName.MyProjectName.Host.csproj index d5843b5373..0431d2befa 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyCompanyName.MyProjectName.Host.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyCompanyName.MyProjectName.Host.csproj @@ -8,8 +8,8 @@ - - + + @@ -75,11 +75,11 @@ - + - + runtime; build; native; contentfiles; analyzers compile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/package.json index b1143bb98a..2f8a6332f7 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyCompanyName.MyProjectName.Mvc.Mongo.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyCompanyName.MyProjectName.Mvc.Mongo.csproj index f7de34413f..f4c32ff0c8 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyCompanyName.MyProjectName.Mvc.Mongo.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyCompanyName.MyProjectName.Mvc.Mongo.csproj @@ -8,8 +8,8 @@ - - + + @@ -77,7 +77,7 @@ - + diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/package.json index b1143bb98a..2f8a6332f7 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyCompanyName.MyProjectName.Mvc.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyCompanyName.MyProjectName.Mvc.csproj index ccbc44f2b8..16e8d6ba9f 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyCompanyName.MyProjectName.Mvc.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyCompanyName.MyProjectName.Mvc.csproj @@ -8,8 +8,8 @@ - - + + @@ -78,11 +78,11 @@ - + - + runtime; build; native; contentfiles; analyzers compile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/package.json index b1143bb98a..2f8a6332f7 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2" } } diff --git a/templates/app/angular/package.json b/templates/app/angular/package.json index 7151d2898b..5c1ce01ab6 100644 --- a/templates/app/angular/package.json +++ b/templates/app/angular/package.json @@ -12,15 +12,15 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~9.1.0", - "@abp/ng.components": "~9.1.0", - "@abp/ng.core": "~9.1.0", - "@abp/ng.identity": "~9.1.0", - "@abp/ng.oauth": "~9.1.0", - "@abp/ng.setting-management": "~9.1.0", - "@abp/ng.tenant-management": "~9.1.0", - "@abp/ng.theme.lepton-x": "~4.1.0", - "@abp/ng.theme.shared": "~9.1.0", + "@abp/ng.account": "~9.2.0-rc.2", + "@abp/ng.components": "~9.2.0-rc.2", + "@abp/ng.core": "~9.2.0-rc.2", + "@abp/ng.identity": "~9.2.0-rc.2", + "@abp/ng.oauth": "~9.2.0-rc.2", + "@abp/ng.setting-management": "~9.2.0-rc.2", + "@abp/ng.tenant-management": "~9.2.0-rc.2", + "@abp/ng.theme.lepton-x": "~4.2.0-rc.2", + "@abp/ng.theme.shared": "~9.2.0-rc.2", "@angular/animations": "~19.1.0", "@angular/common": "~19.1.0", "@angular/compiler": "~19.1.0", @@ -36,7 +36,7 @@ "zone.js": "~0.15.0" }, "devDependencies": { - "@abp/ng.schematics": "~9.1.0", + "@abp/ng.schematics": "~9.2.0-rc.2", "@angular-devkit/build-angular": "~19.1.0", "@angular-eslint/builder": "~19.0.0", "@angular-eslint/eslint-plugin": "~19.0.0", diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj index b95c06305c..2d2e9e301d 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj @@ -39,9 +39,9 @@ - - - + + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package.json index 256485c68a..3718a3cb19 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package.json @@ -3,6 +3,6 @@ "name": "my-app-authserver", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyCompanyName.MyProjectName.Blazor.Client.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyCompanyName.MyProjectName.Blazor.Client.csproj index 72840b70b4..1ba78a108b 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyCompanyName.MyProjectName.Blazor.Client.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyCompanyName.MyProjectName.Blazor.Client.csproj @@ -12,10 +12,10 @@ - - - - + + + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj index a8ff20d2ec..a5952f1911 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj @@ -14,11 +14,11 @@ - - - - - + + + + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json index a9583936e1..eb2b1958bc 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~4.2.0-rc.2" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj index 20b58b045d..00b35d31c8 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj @@ -14,11 +14,11 @@ - - - - - + + + + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/package.json index a9583936e1..eb2b1958bc 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~4.2.0-rc.2" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Client.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Client.csproj index 9347651ff4..24f55913bc 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Client.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Client.csproj @@ -13,10 +13,10 @@ - - - - + + + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client.csproj index 8861471b0f..8d591b19ec 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client.csproj @@ -13,10 +13,10 @@ - - - - + + + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.csproj index f0da067b27..b5359b0e17 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.csproj @@ -15,12 +15,12 @@ - - - - - - + + + + + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/package.json index a9583936e1..eb2b1958bc 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~4.2.0-rc.2" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyCompanyName.MyProjectName.Blazor.WebApp.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyCompanyName.MyProjectName.Blazor.WebApp.csproj index 65d54aeb32..20e3591801 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyCompanyName.MyProjectName.Blazor.WebApp.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyCompanyName.MyProjectName.Blazor.WebApp.csproj @@ -15,11 +15,11 @@ - - - - - + + + + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/package.json index a9583936e1..eb2b1958bc 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~4.2.0-rc.2" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj index 156e1147b7..9ea9c32df8 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj @@ -8,12 +8,12 @@ - - + + - + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/MyCompanyName.MyProjectName.DbMigrator.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/MyCompanyName.MyProjectName.DbMigrator.csproj index 7fbbb92fa8..b68c8562f0 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/MyCompanyName.MyProjectName.DbMigrator.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/MyCompanyName.MyProjectName.DbMigrator.csproj @@ -18,11 +18,11 @@ - - - + + + - + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj index 7f6b9936fa..6a84c34249 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj @@ -26,7 +26,7 @@ - +
diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj index 3c77133bb4..d03b199ebd 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj @@ -22,7 +22,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj index 6928fab989..7fdb28b27f 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj @@ -11,10 +11,10 @@ - - - - + + + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj index 05ef26fb35..f1f76659b6 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json index b1143bb98a..2f8a6332f7 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj index ea2058353f..f20ee25c62 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj @@ -17,9 +17,9 @@ - - - + + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json index b1143bb98a..2f8a6332f7 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj index 2e932fad4c..b97fd05460 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj @@ -39,8 +39,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json index b1143bb98a..2f8a6332f7 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.1.0" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~4.2.0-rc.2" } } diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj index 8cd45f7b82..d6f2e5f70e 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj @@ -22,8 +22,8 @@ - - + + diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj index 79c080216c..68848d75c2 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj @@ -15,10 +15,10 @@ - - - - + + + + diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj index 426dcaca1c..791033b241 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj @@ -17,15 +17,15 @@ - - + + all runtime; build; native; contentfiles; analyzers - - - - + + + + diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyCompanyName.MyProjectName.Web.Tests.csproj b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyCompanyName.MyProjectName.Web.Tests.csproj index 3ca0425b28..8d41ed9e74 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyCompanyName.MyProjectName.Web.Tests.csproj +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyCompanyName.MyProjectName.Web.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/templates/console/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj b/templates/console/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj index 65fee12cfc..1a29eebb8d 100644 --- a/templates/console/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj +++ b/templates/console/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj @@ -13,12 +13,12 @@ - - - - + + + + - + diff --git a/templates/maui/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj b/templates/maui/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj index f33122e97a..66e554f50f 100644 --- a/templates/maui/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj +++ b/templates/maui/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj @@ -35,7 +35,7 @@ - + diff --git a/templates/module/angular/package.json b/templates/module/angular/package.json index 08611fd06e..127ce48c49 100644 --- a/templates/module/angular/package.json +++ b/templates/module/angular/package.json @@ -13,15 +13,15 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~9.1.0", - "@abp/ng.components": "~9.1.0", - "@abp/ng.core": "~9.1.0", - "@abp/ng.identity": "~9.1.0", - "@abp/ng.oauth": "~9.1.0", - "@abp/ng.setting-management": "~9.1.0", - "@abp/ng.tenant-management": "~9.1.0", - "@abp/ng.theme.basic": "~9.1.0", - "@abp/ng.theme.shared": "~9.1.0", + "@abp/ng.account": "~9.2.0-rc.2", + "@abp/ng.components": "~9.2.0-rc.2", + "@abp/ng.core": "~9.2.0-rc.2", + "@abp/ng.identity": "~9.2.0-rc.2", + "@abp/ng.oauth": "~9.2.0-rc.2", + "@abp/ng.setting-management": "~9.2.0-rc.2", + "@abp/ng.tenant-management": "~9.2.0-rc.2", + "@abp/ng.theme.basic": "~9.2.0-rc.2", + "@abp/ng.theme.shared": "~9.2.0-rc.2", "@angular/animations": "~19.1.0", "@angular/common": "~19.1.0", "@angular/compiler": "~19.1.0", @@ -36,7 +36,7 @@ "zone.js": "~0.15.0" }, "devDependencies": { - "@abp/ng.schematics": "~9.1.0", + "@abp/ng.schematics": "~9.2.0-rc.2", "@angular-devkit/build-angular": "~19.1.0", "@angular-eslint/builder": "~19.0.0", "@angular-eslint/eslint-plugin": "~19.0.0", diff --git a/templates/module/angular/projects/my-project-name/package.json b/templates/module/angular/projects/my-project-name/package.json index d61229033b..cbf0fc3bc6 100644 --- a/templates/module/angular/projects/my-project-name/package.json +++ b/templates/module/angular/projects/my-project-name/package.json @@ -4,8 +4,8 @@ "peerDependencies": { "@angular/common": "~19.1.0", "@angular/core": "~19.1.0", - "@abp/ng.core": "~9.1.0", - "@abp/ng.theme.shared": "~9.1.0" + "@abp/ng.core": "~9.2.0-rc.2", + "@abp/ng.theme.shared": "~9.2.0-rc.2" }, "dependencies": { "tslib": "^2.1.0" diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj index 7efc0aae71..f48c02d1b4 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj @@ -11,10 +11,10 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/package.json index 3057d1d821..affa211500 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/package.json @@ -3,6 +3,6 @@ "name": "my-app-authserver", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2" } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/MyCompanyName.MyProjectName.Blazor.Host.Client.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/MyCompanyName.MyProjectName.Blazor.Host.Client.csproj index 3fe2b0415b..05e6cbac0b 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/MyCompanyName.MyProjectName.Blazor.Host.Client.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/MyCompanyName.MyProjectName.Blazor.Host.Client.csproj @@ -10,10 +10,10 @@ - - - - + + + + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/MyCompanyName.MyProjectName.Blazor.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/MyCompanyName.MyProjectName.Blazor.Host.csproj index 589c0fced6..dc15b0086f 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/MyCompanyName.MyProjectName.Blazor.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/MyCompanyName.MyProjectName.Blazor.Host.csproj @@ -8,12 +8,12 @@ - - + + - + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj index d05d66c666..c8096f7080 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj @@ -13,11 +13,11 @@ - - - - - + + + + + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/package.json index f5b2e4cd4e..b22f984673 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0", - "@abp/aspnetcore.components.server.basictheme": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2", + "@abp/aspnetcore.components.server.basictheme": "~9.2.0-rc.2" } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj index 8db094e467..b9dffe7646 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj @@ -11,12 +11,12 @@ - - + + - - - + + + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj index 666658da33..f2a0614cc0 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj @@ -11,9 +11,9 @@ - - - + + + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json index ef17244517..701670d422 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2" } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj index bc22e1a5f6..e2deed8b1f 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj @@ -11,9 +11,9 @@ - - - + + + all runtime; build; native; contentfiles; analyzers diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json index ef17244517..701670d422 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~9.1.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "~9.2.0-rc.2" } } diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj index 20c07d94f8..557b0e24be 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj @@ -15,7 +15,7 @@ - + diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj index b8791a1f3f..6d10c6e6fd 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj @@ -22,7 +22,7 @@ - + diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj index 7a93e9f941..6aff21d32c 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj index 98abac34f3..321143def3 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj @@ -23,7 +23,7 @@ - + diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj index f63599ff8a..f29fb32a5a 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj @@ -10,10 +10,10 @@ - - - - + + + + diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj index 4c004ee12a..9addb70011 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj @@ -10,15 +10,15 @@ - - + + all runtime; build; native; contentfiles; analyzers - - - - + + + + diff --git a/templates/wpf/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj b/templates/wpf/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj index 3f3a1535bb..b40aa0ffb2 100644 --- a/templates/wpf/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj +++ b/templates/wpf/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj @@ -14,11 +14,11 @@ - - - - - + + + + +