Browse Source

Merge branch 'dev' of https://github.com/abpframework/abp into dev

pull/16180/head
Ebicoglu 4 years ago
parent
commit
f1bf57fd38
  1. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/ar.json
  2. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/cs.json
  3. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  4. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/fi.json
  5. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/fr.json
  6. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/hi.json
  7. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/it.json
  8. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/sl.json
  9. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json
  10. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json
  11. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hant.json
  12. 1
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
  13. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json
  14. 5
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json
  15. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de.json
  16. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  17. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json
  18. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json
  19. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json
  20. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json
  21. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json
  22. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json
  23. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json
  24. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json
  25. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json
  26. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json
  27. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json
  28. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ru.json
  29. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json
  30. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json
  31. 22
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json
  32. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json
  33. 8
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json
  34. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json
  35. 2
      build/build-all-release.ps1
  36. 297
      docs/en/Community-Articles/2023-03-20-Dapper/POST.md
  37. BIN
      docs/en/Community-Articles/2023-03-20-Dapper/book-class-in-rider.png
  38. BIN
      docs/en/Community-Articles/2023-03-20-Dapper/book-database-table.png
  39. BIN
      docs/en/Community-Articles/2023-03-20-Dapper/book-table-data.png
  40. BIN
      docs/en/Community-Articles/2023-03-20-Dapper/list-of-books.png
  41. 372
      docs/en/Community-Articles/2023-03-28-Converting-Create-Edit-Modal-To-Page/POST.md
  42. BIN
      docs/en/Community-Articles/2023-03-28-Converting-Create-Edit-Modal-To-Page/images/books-remove-methods.png
  43. BIN
      docs/en/Community-Articles/2023-03-28-Converting-Create-Edit-Modal-To-Page/images/books-remove-modals.png
  44. BIN
      docs/en/Community-Articles/2023-03-28-Converting-Create-Edit-Modal-To-Page/images/new.gif
  45. BIN
      docs/en/Community-Articles/2023-03-28-Converting-Create-Edit-Modal-To-Page/images/old.gif
  46. 170
      docs/en/UI/Angular/Card-Component.md
  47. 56
      docs/en/UI/Angular/Modifying-the-Menu.md
  48. 17
      docs/en/UI/Angular/Page-Alerts.md
  49. BIN
      docs/en/UI/Angular/images/abp-card-body.png
  50. BIN
      docs/en/UI/Angular/images/abp-card-header-footer.png
  51. BIN
      docs/en/UI/Angular/images/abp-card-header.png
  52. BIN
      docs/en/UI/Angular/images/abp-card-image.png
  53. BIN
      docs/en/UI/Angular/images/abp-card-kitchen-sink.png
  54. BIN
      docs/en/UI/Angular/images/abp-card-list-group.png
  55. BIN
      docs/en/UI/Angular/images/abp-card-title-text-link.png
  56. BIN
      docs/en/UI/Angular/images/page-alert-with-html-example.png
  57. 223
      docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md
  58. 58
      framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyOptions.cs
  59. 8
      framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/MultiTenancyMiddleware.cs
  60. 82
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs
  61. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
  62. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs
  63. 215
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelper.cs
  64. 712
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs
  65. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerDrops.cs
  66. 10
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerOpens.cs
  67. 41
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerOptions.cs
  68. 90
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerRange.cs
  69. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelper.cs
  70. 88
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelperService.cs
  71. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerWeekNumbers.cs
  72. 20
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDateRangePickerTagHelper.cs
  73. 132
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDateRangePickerTagHelperService.cs
  74. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/DatePickerAttribute.cs
  75. 34
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/DatePickerOptionsAttribute.cs
  76. 19
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/DateRangePickerAttribute.cs
  77. 160
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/IAbpDatePickerOptions.cs
  78. 562
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js
  79. 7
      framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor
  80. 12
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
  81. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json
  82. 5
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json
  83. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/de.json
  84. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/el.json
  85. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en-GB.json
  86. 5
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json
  87. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json
  88. 5
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fa.json
  89. 5
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fi.json
  90. 5
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fr.json
  91. 5
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hi.json
  92. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hr.json
  93. 5
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hu.json
  94. 5
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/is.json
  95. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/it.json
  96. 5
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/nl.json
  97. 5
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl-PL.json
  98. 5
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json
  99. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ro-RO.json
  100. 5
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ru.json

7
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/ar.json

@ -348,6 +348,11 @@
"Volo.AbpIo.Commercial:030008": "يمكن تحديد تاريخ الشراء فقط عند شراء الحالة!",
"Volo.AbpIo.Commercial:030009": "لم يتم العثور على المستخدم!",
"Volo.AbpIo.Commercial:030010": "لشراء الترخيص التجريبي ، تحتاج أولاً إلى تنشيط الترخيص التجريبي الخاص بك!",
"Volo.AbpIo.Commercial:030011": "لا يمكنك حذف ترخيص تجريبي عند شرائه!"
"Volo.AbpIo.Commercial:030011": "لا يمكنك حذف ترخيص تجريبي عند شرائه!",
"MoveWaitList": "الانتقال إلى قائمة الانتظار",
"CommunityLinkTitle": "افتح على موقع المجتمع",
"CommunityLink": "رابط المجتمع",
"ReloadFromSource": "إعادة التحميل من المصدر",
"ReloadFromSourceConfirmationMessage": "سيتم تحديث هذه المشاركة من \"{0}\". هل تريد الاستمرار؟"
}
}

7
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/cs.json

@ -348,6 +348,11 @@
"Volo.AbpIo.Commercial:030008": "Datum nákupu lze nastavit pouze ve stavu Zakoupeno!",
"Volo.AbpIo.Commercial:030009": "Uživatel nenalezen!",
"Volo.AbpIo.Commercial:030010": "Chcete-li zakoupit zkušební licenci, musíte nejprve aktivovat zkušební licenci!",
"Volo.AbpIo.Commercial:030011": "Po zakoupení zkušební licence nelze odstranit!"
"Volo.AbpIo.Commercial:030011": "Po zakoupení zkušební licence nelze odstranit!",
"MoveWaitList": "Přejít na čekací listinu",
"CommunityLinkTitle": "Otevřít na webu komunity",
"CommunityLink": "Odkaz na komunitu",
"ReloadFromSource": "Znovu načíst ze zdroje",
"ReloadFromSourceConfirmationMessage": "Tento příspěvek bude obnoven z „{0}“. Chceš pokračovat?"
}
}

7
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -452,6 +452,11 @@
"WhoWeAreItem": "Who We Are Item",
"FieldIsRequired": "{0} is required.",
"FieldIsNotValid": " {0} is not valid.",
"InterestedLicenseType": "Interested License Type"
"InterestedLicenseType": "Interested License Type",
"MoveWaitList": "Move to wait list",
"CommunityLinkTitle": "Open on the community website",
"CommunityLink": "Community Link",
"ReloadFromSource": "Reload From the Source",
"ReloadFromSourceConfirmationMessage": "This post will be refreshed from \"{0}\". Do you want to continue?"
}
}

7
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/fi.json

@ -452,6 +452,11 @@
"WhoWeAreItem": "Keitä me olemme -tuote",
"FieldIsRequired": "{0} vaaditaan.",
"FieldIsNotValid": " {0} ei kelpaa.",
"InterestedLicenseType": "Kiinnostunut lisenssityyppi"
"InterestedLicenseType": "Kiinnostunut lisenssityyppi",
"MoveWaitList": "Siirrä jonotuslistalle",
"CommunityLinkTitle": "Avaa yhteisön verkkosivuilla",
"CommunityLink": "Yhteisön linkki",
"ReloadFromSource": "Lataa uudelleen lähteestä",
"ReloadFromSourceConfirmationMessage": "Tämä viesti päivitetään kohteesta \"{0}\". Haluatko jatkaa?"
}
}

7
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/fr.json

@ -348,6 +348,11 @@
"Volo.AbpIo.Commercial:030008": "La date d'achat ne peut être définie que lorsque le statut est Acheté !",
"Volo.AbpIo.Commercial:030009": "Utilisateur non trouvé!",
"Volo.AbpIo.Commercial:030010": "Pour acheter la licence d'essai, vous devez d'abord activer votre licence d'essai !",
"Volo.AbpIo.Commercial:030011": "Vous ne pouvez pas supprimer une licence d'essai lorsqu'elle est achetée !"
"Volo.AbpIo.Commercial:030011": "Vous ne pouvez pas supprimer une licence d'essai lorsqu'elle est achetée !",
"MoveWaitList": "Passer à la liste d'attente",
"CommunityLinkTitle": "Ouvert sur le site communautaire",
"CommunityLink": "Lien communautaire",
"ReloadFromSource": "Recharger à partir de la source",
"ReloadFromSourceConfirmationMessage": "Ce message sera actualisé à partir de \"{0}\". Voulez-vous continuer?"
}
}

7
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/hi.json

@ -348,6 +348,11 @@
"Volo.AbpIo.Commercial:030008": "खरीद की तारीख तभी निर्धारित की जा सकती है जब स्थिति खरीदी गई हो!",
"Volo.AbpIo.Commercial:030009": "उपयोगकर्ता नहीं मिला!",
"Volo.AbpIo.Commercial:030010": "परीक्षण लाइसेंस खरीदने के लिए, पहले आपको अपना परीक्षण लाइसेंस सक्रिय करना होगा!",
"Volo.AbpIo.Commercial:030011": "जब आप एक परीक्षण लाइसेंस खरीदा जाता है तो आप उसे हटा नहीं सकते हैं!"
"Volo.AbpIo.Commercial:030011": "जब आप एक परीक्षण लाइसेंस खरीदा जाता है तो आप उसे हटा नहीं सकते हैं!",
"MoveWaitList": "प्रतीक्षा सूची में ले जाएँ\n",
"CommunityLinkTitle": "सामुदायिक वेबसाइट पर खोलें",
"CommunityLink": "सामुदायिक लिंक",
"ReloadFromSource": "स्रोत से पुनः लोड करें",
"ReloadFromSourceConfirmationMessage": "यह पोस्ट \"{0}\" से ताज़ा की जाएगी। क्या आप जारी रखना चाहते हैं?"
}
}

7
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/it.json

@ -348,6 +348,11 @@
"Volo.AbpIo.Commercial:030008": "La data di acquisto può essere impostata solo quando lo stato è Acquistato!",
"Volo.AbpIo.Commercial:030009": "Utente non trovato!",
"Volo.AbpIo.Commercial:030010": "Per acquistare la licenza di prova, devi prima attivare la tua licenza di prova!",
"Volo.AbpIo.Commercial:030011": "Non è possibile eliminare una licenza di prova al momento dell'acquisto!"
"Volo.AbpIo.Commercial:030011": "Non è possibile eliminare una licenza di prova al momento dell'acquisto!",
"MoveWaitList": "Passa alla lista d'attesa",
"CommunityLinkTitle": "Apri sul sito web della comunità",
"CommunityLink": "Collegamento comunitario",
"ReloadFromSource": "Ricarica dalla sorgente",
"ReloadFromSourceConfirmationMessage": "Questo post verrà aggiornato da \"{0}\". Volete continuare?"
}
}

7
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/sl.json

@ -348,6 +348,11 @@
"Volo.AbpIo.Commercial:030008": "Datum nakupa je mogoče nastaviti samo, če je status Kupljeno!",
"Volo.AbpIo.Commercial:030009": "Uporabnik ni najden!",
"Volo.AbpIo.Commercial:030010": "Za nakup preizkusne licence morate najprej aktivirati preizkusno licenco!",
"Volo.AbpIo.Commercial:030011": "Preskusne licence ne morete izbrisati, ko je kupljena!"
"Volo.AbpIo.Commercial:030011": "Preskusne licence ne morete izbrisati, ko je kupljena!",
"MoveWaitList": "Presuňte sa na zoznam čakateľov",
"CommunityLinkTitle": "Otvorte na webovej stránke komunity",
"CommunityLink": "Odkaz na komunitu",
"ReloadFromSource": "Znovu načítať zo zdroja",
"ReloadFromSourceConfirmationMessage": "Tento príspevok bude obnovený z „{0}“. Chceš pokračovať?"
}
}

7
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json

@ -412,6 +412,11 @@
"CustomPaymentFlexSwitchDescription": "Lisanslı",
"AllowFeatureUpgradeOnLicenseExpire": "Lisans süresi dolunca özellik yükseltmesine izin ver",
"Deleted{0}": "[{0} silindi]",
"Permission:BookDiscount": "Kitap indirimleri"
"Permission:BookDiscount": "Kitap indirimleri",
"MoveWaitList": "Bekleme listesine geç",
"CommunityLinkTitle": "Topluluk web sitesinde açın",
"CommunityLink": "Topluluk Bağlantısı",
"ReloadFromSource": "Kaynaktan Yeniden Yükle",
"ReloadFromSourceConfirmationMessage": "Bu gönderi \"{0}\" adresinden yenilenecek. Devam etmek istiyor musun?"
}
}

7
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json

@ -452,6 +452,11 @@
"WhoWeAreItem": "关于我们",
"FieldIsRequired": "{0}是必须的。",
"FieldIsNotValid": "{0}是无效的。",
"InterestedLicenseType": "感兴趣的许可证类型"
"InterestedLicenseType": "感兴趣的许可证类型",
"MoveWaitList": "移至候补名单",
"CommunityLinkTitle": "在社区网站上打开",
"CommunityLink": "在社区网站上打开",
"ReloadFromSource": "从源重新加载",
"ReloadFromSourceConfirmationMessage": "此帖子将从“{0}”刷新。 你想继续吗?"
}
}

7
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hant.json

@ -348,6 +348,11 @@
"Volo.AbpIo.Commercial:030008": "購買日期只能在狀態為已購買時設置!",
"Volo.AbpIo.Commercial:030009": "未找到用戶!",
"Volo.AbpIo.Commercial:030010": "要購買試用許可證,首先您需要激活您的試用許可證!",
"Volo.AbpIo.Commercial:030011": "購買試用許可證後,您無法刪除它!"
"Volo.AbpIo.Commercial:030011": "購買試用許可證後,您無法刪除它!",
"MoveWaitList": "移至候补名单",
"CommunityLinkTitle": "在社区网站上打开",
"CommunityLink": "社区链接",
"ReloadFromSource": "從源重新加載",
"ReloadFromSourceConfirmationMessage": "此帖子將從“{0}”刷新。 你想繼續嗎?"
}
}

1
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json

@ -178,6 +178,7 @@
"BuyNewLicenseBetweenDatesToGetBenefit": "Buy a new license between {0} and {1} to get benefit for extra 2 months!",
"CheckAllCommunityTalks": "Check All Community Posts",
"ReadMore": "Read More",
"ContinueReading": "Continue Reading",
"Post": "Post",
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "Explore the contents created by the core ABP team and the ABP community.",
"WelcomeFallCampaign": "Welcome Fall Campaign!",

4
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "هل يمكنني تغيير المطورين المسجلين لمنظمتي في المستقبل؟",
"ChangingDevelopersExplanation": "بالإضافة إلى إضافة مطورين جدد إلى الترخيص الخاص بك ، يمكنك أيضًا تغيير المطورين الحاليين (يمكنك إزالة مطور وإضافة مطور جديد إلى نفس المقعد) دون أي تكلفة إضافية.",
"WhenShouldIRenewMyLicense": "متى يجب أن أجدد رخصتي؟",
"WhenShouldIRenewMyLicenseExplanation": "إذا قمت بتجديد ترخيصك في غضون <strong> شهر واحد </ strong> بعد انتهاء صلاحية ترخيصك ، فسيتم تطبيق الخصومات التالية: ترخيص الفريق {0}؛ رخصة تجارية {1} ؛ ترخيص المؤسسة {2}. ومع ذلك ، إذا جددت ترخيصك بعد <strong> شهر واحد </ strong> من تاريخ انتهاء صلاحية الترخيص ، فسيكون سعر التجديد هو نفسه سعر شراء الترخيص ولن يكون هناك خصم على التجديد.",
"WhenShouldIRenewMyLicenseExplanation": "إذا قمت بتجديد ترخيصك في غضون <strong> {3} يوما</ strong> بعد انتهاء صلاحية رخصتك ، فسيتم تطبيق الخصومات التالية: رخصة الفريق {0}؛ الرخصة التجارية {1} ؛ رخصة المؤسسة {2}. ومع ذلك ، إذا جددت رخصتك بعد <strong>{3} يوما </ strong> من تاريخ انتهاء صلاحية الرخصة ، فسيكون سعر التجديد هو نفسه سعر شراء الرخصة ولن يكون هناك خصم على التجديد.",
"TrialPlan": "هل لديك خطة تجريبية؟",
"DoYouAcceptBankWireTransfer": "هل تقبل التحويل البنكي؟",
"DoYouAcceptBankWireTransferExplanation": "نعم ، نحن نقبل التحويل البنكي. <br /> بعد إرسال رسوم الترخيص عبر التحويل المصرفي ، أرسل لنا بريدًا إلكترونيًا على accounting@abp.io إيصالك ونوع الترخيص المطلوب. معلومات حسابنا المصرفي الدولي:",
@ -780,7 +780,7 @@
"SupportPolicyFaqTitle": "ما هي سياسة الدعم الخاصة بك؟",
"SupportPolicyFaqExplanation": "نحن ندعم فقط الإصدار الرئيسي النشط والسابق. لا نضمن إصدار تصحيح للإصدارات الرئيسية الثالثة والأقدم. على سبيل المثال ، إذا كان الإصدار النشط هو 7.0.0 ، فسنصدر إصدارات تصحيح لكل من 6.x.x و 7.x.x. إلى جانب ذلك ، نحن نقدم الدعم فقط لإطار عمل ABP والقضايا التجارية المتعلقة بـ ABP. هذا يعني أنه لا يتم تقديم أي دعم لتطبيقات الطرف الثالث والخدمات السحابية والمكتبات الطرفية الأخرى التي تستخدمها منتجات ABP. سنبذل جهودًا معقولة تجاريًا لتزويد عملائنا بالدعم الفني خلال ساعات العمل الرسمية لـ \"Volosoft Bilisim A.S\". من ناحية أخرى ، نحن لا نلتزم بوقت استجابة اتفاقية مستوى الخدمة (SLA) ، لكننا سنحاول الرد على المشكلات الفنية في أسرع وقت ممكن خلال ساعات العمل الرسمية لدينا. ما لم يتم إبرام اتفاقية خاصة مع العميل ، فإننا نقدم الدعم فقط على https://support.abp.io. لدينا أيضًا دعم خاص بالبريد الإلكتروني ، وهو متاح فقط لحاملي تراخيص المؤسسة.",
"WhyUseAbpIoPlatform": "لماذا يجب علي استخدام منصة ABP.IO بدلاً من إنشاء حل جديد من البداية؟",
"WhyUseAbpIoPlatformFaqExplanation": "انظر الى <a href=\"https://docs.abp.io/en/commercial/latest/why-abp-io-platform \"> هذا المستند </a> للحصول على شرح مفصل لسبب استخدام ABP.IO Platform لفائدته الكبيرة بدلًا من القيام بكل شيء بنفسك.",
"WhyUseAbpIoPlatformFaqExplanation": "انظر الى <a href=\"https://docs.abp.io/en/commercial/latest/why-abp-io-platform \"> هذا المستند </a> للحصول على شرح مفصل لسبب استخدام ABP.IO Platform لفائدته الكبيرة بدلًا من القيام بكل شيء بنفسك.",
"EulaPageTitle": "اتفاقية ترخيص المستخدم النهائي (EULA)",
"PrivacyPolicyPageTitle": "سياسة الخصوصية - سياسة ملفات تعريف الارتباط",
"TermsConditionsPageTitle": "الأحكام والشروط",

5
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "Mohu v budoucnu změnit registrované vývojáře své organizace?",
"ChangingDevelopersExplanation": "Kromě přidání nových vývojářů do vaší licence můžete také změnit stávající vývojáře (můžete odebrat vývojáře a přidat nového na stejné místo) bez jakýchkoli dalších nákladů.",
"WhenShouldIRenewMyLicense": "Kdy bych měl obnovit licenci?",
"WhenShouldIRenewMyLicenseExplanation": "Pokud si licenci obnovíte do <strong>1 měsíce</strong> po vypršení platnosti licence, budou uplatněny následující slevy: Týmová licence {0}; Obchodní licence {1}; Enterprise Licence {2}. Pokud však licenci obnovíte po <strong>1 měsíci</strong> od data vypršení platnosti vaší licence, cena za obnovení bude stejná jako cena za zakoupení licence a na obnovení nebude poskytnuta žádná sleva.",
"WhenShouldIRenewMyLicenseExplanation": "Pokud si licenci obnovíte do <strong>{3} dní</strong> po vypršení platnosti licence, budou uplatněny následující slevy: Týmová licence {0}; Obchodní licence {1}; Enterprise Licence {2}. Pokud však licenci obnovíte po <strong>{3} měsíci</strong> od data vypršení platnosti vaší licence, cena za obnovení bude stejná jako cena za zakoupení licence a na obnovení nebude poskytnuta žádná sleva.",
"TrialPlan": "Máte zkušební plán?",
"DoYouAcceptBankWireTransfer": "Přijímáte bankovní převod?",
"DoYouAcceptBankWireTransferExplanation": "Ano, přijímáme bankovní převod.<br />Po zaslání licenčního poplatku bankovním převodem nám zašlete e-mail na adresu accounting@abp.io potvrzení a požadovaný typ licence. Informace o našem mezinárodním bankovním účtu:",
@ -399,9 +399,8 @@
"WhatHappensWhenLicenseEndsExplanation4": "Po skončení platnosti licence nelze instalovat nové moduly a motivy přidané do platformy ABP Commercial.",
"WhatHappensWhenLicenseEndsExplanation5": "Sadu ABP Suite nelze použít.",
"WhatHappensWhenLicenseEndsExplanation6": "Již nelze získat <a href=\"{0}\">prémiovou podporu</a>.",
"WhatHappensWhenLicenseEndsExplanation7": "Pokud chcete i nadále využívat tyto výhody, můžete svou licenci prodloužit (obnovit). Pokud prodloužíte licenci do <strong>1 měsíce</strong> po vypršení platnosti licence, budou uplatněny následující slevy: Týmová licence {0}; Obchodní licence {1}; Enterprise Licence {2}.",
"WhatHappensWhenLicenseEndsExplanation7": "Pokud chcete i nadále využívat tyto výhody, můžete svou licenci prodloužit (obnovit). Pokud prodloužíte licenci do <strong>{3} dní</strong> po vypršení platnosti licence, budou uplatněny následující slevy: Týmová licence {0}; Obchodní licence {1}; Enterprise Licence {2}.",
"BlazoriseLicense": "Musíme si koupit licenci Blazorise?",
"BlazoriseLicenseExplanation": "Máme dohodu mezi společnostmi Volosoft a Megabit, na základě této dohody je licence Blazorise přibalena k produktům ABP Commercial, proto si naši zákazníci nemusí kupovat další licenci Blazorise."
}
}

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "Kann ich die registrierten Entwickler meiner Organisation in Zukunft ändern?",
"ChangingDevelopersExplanation": "Sie können nicht nur neue Entwickler zu Ihrer Lizenz hinzufügen, sondern auch die vorhandenen Entwickler ändern (Sie können einen Entwickler entfernen und einen neuen zum selben Arbeitsplatz hinzufügen) ohne zusätzliche Kosten.",
"WhenShouldIRenewMyLicense": "Wann sollte ich meine Lizenz erneuern?",
"WhenShouldIRenewMyLicenseExplanation": "Wenn Sie Ihre Lizenz innerhalb von <strong>1 Monat</strong> nach Ablauf Ihrer Lizenz erneuern, werden die folgenden Rabatte gewährt: Team-Lizenz {0}% Rabatt, Business-Lizenz {1}% Rabatt, Enterprise-Lizenz {2}% Rabatt . Wenn Sie Ihre Lizenz <strong>1 Monat</strong> nach dem Ablaufdatum Ihrer Lizenz verlängern, entspricht der Verlängerungspreis dem Lizenzkaufpreis und es wird kein Rabatt auf Ihre Verlängerung gewährt.",
"WhenShouldIRenewMyLicenseExplanation": "Wenn Sie Ihre Lizenz innerhalb von <strong>{3} Tage</strong> nach Ablauf Ihrer Lizenz erneuern, werden die folgenden Rabatte gewährt: Team-Lizenz {0}% Rabatt, Business-Lizenz {1}% Rabatt, Enterprise-Lizenz {2}% Rabatt . Wenn Sie Ihre Lizenz <strong>{3} Tage</strong> nach dem Ablaufdatum Ihrer Lizenz verlängern, entspricht der Verlängerungspreis dem Lizenzkaufpreis und es wird kein Rabatt auf Ihre Verlängerung gewährt.",
"TrialPlan": "Hast du einen Probeplan?",
"DoYouAcceptBankWireTransfer": "Akzeptieren Sie Banküberweisungen?",
"DoYouAcceptBankWireTransferExplanation": "Ja, wir akzeptieren Banküberweisungen.<br />Nachdem Sie die Lizenzgebühr per Banküberweisung gesendet haben, senden Sie uns Ihre Quittung und den gewünschten Lizenztyp per E-Mail an accounting@abp.io. Unsere internationale Bankverbindung:",

4
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json

@ -202,11 +202,11 @@
"WhatHappensWhenLicenseEndsExplanation4": "You can not install new modules and themes added to the ABP Commercial platform after your license ends.",
"WhatHappensWhenLicenseEndsExplanation5": "You can not use the ABP Suite.",
"WhatHappensWhenLicenseEndsExplanation6": "You can not get the <a href=\"{0}\">premium support</a> anymore.",
"WhatHappensWhenLicenseEndsExplanation7": "You can extend (renew) your license if you want to continue getting these benefits. If you extend your license within <strong>1 month</strong> after your license expires, the following discounts will be applied: Team License {0}; Business License {1}; Enterprise License {2}.",
"WhatHappensWhenLicenseEndsExplanation7": "You can extend (renew) your license if you want to continue getting these benefits. If you extend your license within <strong>{3} days</strong> after your license expires, the following discounts will be applied: Team License {0}; Business License {1}; Enterprise License {2}.",
"discountForYears": "{0}% discount for {1} year(s)",
"WhatHappensWhenLicenseEndsExplanation8": "The ABP projects you generated are not stored on our servers. Therefore, it is your responsibility to keep the source code you download. When your license expires, there's no way to get your generated ABP project source code.",
"WhenShouldIRenewMyLicense": "When should I renew my license?",
"WhenShouldIRenewMyLicenseExplanation": "If you renew your license within <strong>1 month</strong> after your license expires, the following discounts will be applied: Team License {0}; Business License {1}; Enterprise License {2}. However, if you renew your license after <strong>1 month</strong> since the expiry date of your license, the renewal price will be the same as the license purchase price and there will be no discount on your renewal.",
"WhenShouldIRenewMyLicenseExplanation": "If you renew your license within <strong>{3} days</strong> after your license expires, the following discounts will be applied: Team License {0}; Business License {1}; Enterprise License {2}. However, if you renew your license after <strong>{3} days</strong> since the expiry date of your license, the renewal price will be the same as the license purchase price and there will be no discount on your renewal.",
"TrialPlan": "Do you have a trial plan?",
"TrialPlanExplanation": "No, there is no trial version for ABP Commercial. You can check the community edition to understand the code quality and approaches. We also offer a 30-day money-back guarantee for the Team license, no questions asked! You can request a refund within the first 30 days. We provide a 60% refund within 30 days for Business and Enterprise licenses. This is because the Business and Enterprise licenses contain the full source-code of all the modules and themes.",
"DoYouAcceptBankWireTransfer": "Do you accept bank wire transfers?",

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "¿Puedo cambiar los desarrolladores registrados de mi organización en el futuro?",
"ChangingDevelopersExplanation": "Además de agregar nuevos desarrolladores a su licencia, también puede cambiar los desarrolladores existentes (puede eliminar un desarrollador y agregar uno nuevo al mismo puesto) sin ningún costo adicional.",
"WhenShouldIRenewMyLicense": "¿Cuándo debo renovar mi licencia?",
"WhenShouldIRenewMyLicenseExplanation": "Si renueva su licencia dentro de <strong> 1 mes </strong> después de su vencimiento, se aplicarán los siguientes descuentos: Licencia de equipo {0}% de descuento, Licencia comercial {1}% de descuento, Licencia empresarial {2}% de descuento . Si renueva su licencia <strong> 1 mes </strong> después de la fecha de vencimiento de su licencia, el precio de renovación será el mismo que el precio de compra de la licencia y no habrá descuento en su renovación.",
"WhenShouldIRenewMyLicenseExplanation": "Si renueva su licencia dentro de <strong> {3} dias </strong> después de su vencimiento, se aplicarán los siguientes descuentos: Licencia de equipo {0}% de descuento, Licencia comercial {1}% de descuento, Licencia empresarial {2}% de descuento . Si renueva su licencia <strong> {3} dias </strong> después de la fecha de vencimiento de su licencia, el precio de renovación será el mismo que el precio de compra de la licencia y no habrá descuento en su renovación.",
"TrialPlan": "¿Tiene un plan de prueba?",
"DoYouAcceptBankWireTransfer": "¿Aceptan transferencia bancaria?",
"DoYouAcceptBankWireTransferExplanation": "Sí, aceptamos transferencia bancaria. <br /> Después de enviar la tarifa de la licencia mediante transferencia bancaria, envíenos un correo electrónico a accounting@abp.io con su recibo y el tipo de licencia solicitada. Nuestra información de cuenta bancaria internacional:",

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json

@ -202,7 +202,7 @@
"WhatHappensWhenLicenseEndsExplanation4": "Et voi asentaa uusia moduuleja ja teemoja, jotka on lisätty ABP Commercial -alustaan lisenssisi päätyttyä.",
"WhatHappensWhenLicenseEndsExplanation5": "Et voi käyttää ABP Suitea.",
"WhatHappensWhenLicenseEndsExplanation6": "Et voi enää saada <a href=\"{0}\">premium-tukea</a>.",
"WhatHappensWhenLicenseEndsExplanation7": "Voit jatkaa (uusia) lisenssiäsi, jos haluat jatkaa näiden etujen saamista. Jos jatkat lisenssiäsi <strong>yhden kuukauden</strong> kuluessa lisenssin vanhenemisesta, seuraavat alennukset sovelletaan: Tiimilisenssi {0}; Toimilupa {1}; Yrityslisenssi {2}.",
"WhatHappensWhenLicenseEndsExplanation7": "Voit jatkaa (uusia) lisenssiäsi, jos haluat jatkaa näiden etujen saamista. Jos jatkat lisenssiäsi <strong>{3} päivää</strong> kuluessa lisenssin vanhenemisesta, seuraavat alennukset sovelletaan: Tiimilisenssi {0}; Toimilupa {1}; Yrityslisenssi {2}.",
"discountForYears": "{0}% de remise pendant {1} an(s)",
"WhatHappensWhenLicenseEndsExplanation8": "Luomiasi ABP-projekteja ei tallenneta palvelimillemme. Siksi on sinun vastuullasi säilyttää lataamasi lähdekoodi. Kun lisenssisi vanhenee, luotua ABP-projektin lähdekoodia ei ole mahdollista saada.",
"WhenShouldIRenewMyLicense": "Milloin minun pitäisi uusia lisenssini?",

4
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "Puis-je changer les développeurs enregistrés de mon organisation à l'avenir?",
"ChangingDevelopersExplanation": "En plus d'ajouter de nouveaux développeurs à votre licence, vous pouvez également modifier les développeurs existants (vous pouvez supprimer un développeur et en ajouter un nouveau sur le même siège) sans aucun coût supplémentaire.",
"WhenShouldIRenewMyLicense": "Quand dois-je renouveler ma licence?",
"WhenShouldIRenewMyLicenseExplanation": "Si vous renouvelez votre licence dans <strong>1 mois</strong> après l'expiration de votre licence, les remises suivantes seront appliquées : licence d'équipe {0} ; licence commerciale {1} ; licence d'entreprise {2}. Toutefois, si vous renouveler votre licence après <strong>1 mois</strong> depuis la date d'expiration de votre licence, le prix de renouvellement sera le même que le prix d'achat de la licence et il n'y aura pas de remise sur votre renouvellement.",
"WhenShouldIRenewMyLicenseExplanation": "Si vous renouvelez votre licence dans <strong>{3} jours</strong> après l'expiration de votre licence, les remises suivantes seront appliquées : licence d'équipe {0} ; licence commerciale {1} ; licence d'entreprise {2}. Toutefois, si vous renouveler votre licence après <strong>{3} jours</strong> depuis la date d'expiration de votre licence, le prix de renouvellement sera le même que le prix d'achat de la licence et il n'y aura pas de remise sur votre renouvellement.",
"TrialPlan": "Avez-vous un plan d'essai?",
"DoYouAcceptBankWireTransfer": "Acceptez-vous les virements bancaires?",
"DoYouAcceptBankWireTransferExplanation": "Oui, nous acceptons les virements bancaires.<br />Après avoir envoyé les frais de licence par virement bancaire, envoyez-nous par e-mail à accounting@abp.io votre reçu et le type de licence demandé. Nos coordonnées bancaires internationales :",
@ -399,7 +399,7 @@
"WhatHappensWhenLicenseEndsExplanation4": "Vous ne pouvez pas installer les nouveaux modules et thèmes ajoutés à la plateforme ABP Commercial après l'expiration de votre licence.",
"WhatHappensWhenLicenseEndsExplanation5": "Vous ne pouvez pas utiliser la suite ABP.",
"WhatHappensWhenLicenseEndsExplanation6": "Vous ne pouvez plus bénéficier du <a href=\"{0}\">support premium</a>.",
"WhatHappensWhenLicenseEndsExplanation7": "Vous pouvez prolonger (renouveler) votre licence si vous souhaitez continuer à bénéficier de ces avantages. Si vous prolongez votre licence dans <strong>1 mois</strong> après l'expiration de votre licence, les remises suivantes seront appliquées : Licence d'équipe {0} ; Licence commerciale {1} ; Licence Entreprise {2}.",
"WhatHappensWhenLicenseEndsExplanation7": "Vous pouvez prolonger (renouveler) votre licence si vous souhaitez continuer à bénéficier de ces avantages. Si vous prolongez votre licence dans <strong>{3} jours</strong> après l'expiration de votre licence, les remises suivantes seront appliquées : Licence d'équipe {0} ; Licence commerciale {1} ; Licence Entreprise {2}.",
"BlazoriseLicense": "Faut-il acheter une licence Blazorise ?",
"BlazoriseLicenseExplanation": "Nous avons conclu un accord entre Volosoft et Megabit, dans le cadre duquel la licence Blazorise est intégrée aux produits ABP Commercial, de sorte que nos clients n'ont pas besoin d'acheter une licence Blazorise supplémentaire."
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "क्या मैं भविष्य में अपने संगठन के पंजीकृत डेवलपर्स को बदल सकता हूं?",
"ChangingDevelopersExplanation": "अपने लाइसेंस में नए डेवलपर्स को जोड़ने के अलावा, आप मौजूदा डेवलपर्स को भी बदल सकते हैं (आप एक डेवलपर को हटा सकते हैं और एक ही सीट पर एक नया जोड़ सकते हैं) बिना किसी अतिरिक्त लागत के।",
"WhenShouldIRenewMyLicense": "मुझे अपना लाइसेंस कब नवीनीकृत करना चाहिए?",
"WhenShouldIRenewMyLicenseExplanation": "यदि आप अपने लाइसेंस की समय सीमा समाप्त होने के बाद <strong>1 महीने</strong> के भीतर अपना लाइसेंस नवीनीकृत करते हैं, तो निम्नलिखित छूटें लागू होंगी: टीम लाइसेंस {0} ; व्यवसाय लाइसेंस {1} ; एंटरप्राइज़ लाइसेंस {2} . यदि आप अपने लाइसेंस की समाप्ति तिथि के बाद <strong>1 महीने</strong> अपने लाइसेंस का नवीनीकरण करते हैं, तो नवीनीकरण मूल्य लाइसेंस खरीद मूल्य के समान होगा और आपके नवीनीकरण पर कोई छूट नहीं होगी।",
"WhenShouldIRenewMyLicenseExplanation": "यदि आप अपने लाइसेंस की समय सीमा समाप्त होने के बाद <strong>{3} दिन</strong> के भीतर अपना लाइसेंस नवीनीकृत करते हैं, तो निम्नलिखित छूटें लागू होंगी: टीम लाइसेंस {0} ; व्यवसाय लाइसेंस {1} ; एंटरप्राइज़ लाइसेंस {2} . यदि आप अपने लाइसेंस की समाप्ति तिथि के बाद <strong>{3} दिन</strong> अपने लाइसेंस का नवीनीकरण करते हैं, तो नवीनीकरण मूल्य लाइसेंस खरीद मूल्य के समान होगा और आपके नवीनीकरण पर कोई छूट नहीं होगी।",
"TrialPlan": "क्या आपके पास एक परीक्षण योजना है?",
"DoYouAcceptBankWireTransfer": "क्या आप बैंक वायर ट्रांसफर स्वीकार करते हैं?",
"DoYouAcceptBankWireTransferExplanation": "हां, हम बैंक वायर ट्रांसफर स्वीकार करते हैं।<br />बैंक हस्तांतरण के माध्यम से लाइसेंस शुल्क भेजने के बाद, हमें अपनी रसीद और अनुरोधित लाइसेंस के प्रकार accounting@abp.io पर ईमेल करें। हमारे अंतरराष्ट्रीय बैंक खाते की जानकारी:",
@ -398,7 +398,7 @@
"WhatHappensWhenLicenseEndsExplanation4": "आपका लाइसेंस समाप्त होने के बाद आप ABP कमर्शियल प्लेटफॉर्म में जोड़े गए नए मॉड्यूल और थीम इंस्टॉल नहीं कर सकते।",
"WhatHappensWhenLicenseEndsExplanation5": "आप एबीपी सूट का उपयोग नहीं कर सकते।",
"WhatHappensWhenLicenseEndsExplanation6": "अब आप <a href=\"{0}\">प्रीमियम समर्थन</a> प्राप्त नहीं कर सकते।",
"WhatHappensWhenLicenseEndsExplanation7": "यदि आप इन लाभों को प्राप्त करना जारी रखना चाहते हैं तो आप अपने लाइसेंस का विस्तार (नवीनीकरण) कर सकते हैं। यदि आप अपना लाइसेंस समाप्त होने के बाद <strong>1 महीने</strong> के भीतर अपना लाइसेंस बढ़ाते हैं, तो निम्नलिखित छूट लागू होंगी: टीम लाइसेंस {0}; व्यवसाय लाइसेंस {1}; उद्यम लाइसेंस {2}।",
"WhatHappensWhenLicenseEndsExplanation7": "यदि आप इन लाभों को प्राप्त करना जारी रखना चाहते हैं तो आप अपने लाइसेंस का विस्तार (नवीनीकरण) कर सकते हैं। यदि आप अपना लाइसेंस समाप्त होने के बाद <strong>{3} दिन</strong> के भीतर अपना लाइसेंस बढ़ाते हैं, तो निम्नलिखित छूट लागू होंगी: टीम लाइसेंस {0}; व्यवसाय लाइसेंस {1}; उद्यम लाइसेंस {2}।",
"BlazoriseLicense": "क्या हमें ब्लेज़ोराइज़ लाइसेंस खरीदने की ज़रूरत है?",
"BlazoriseLicenseExplanation": "हमारे पास Volosoft और Megabit के बीच एक समझौता है, इस समझौते के साथ Blazorise लाइसेंस को ABP वाणिज्यिक उत्पादों के साथ बंडल किया गया है, इसलिए हमारे ग्राहकों को एक अतिरिक्त Blazorise लाइसेंस खरीदने की आवश्यकता नहीं है।"
}

6
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json

@ -206,9 +206,9 @@
"discountForYears": "{0}% kedvezmény {1} évre",
"WhatHappensWhenLicenseEndsExplanation8": "Az Ön által generált ABP projekteket nem tároljuk a szervereinken. Ezért az Ön felelőssége a letöltött forráskód megőrzése. Amikor a licensze lejár, nincs mód a generált ABP projekt forráskódjának lekérésére.",
"WhenShouldIRenewMyLicense": "Mikor kell megújítanom a jogosítványomat?",
"WhenShouldIRenewMyLicenseExplanation": "Ha a licenc lejártát követő <strong>1 hónapon belül</strong> megújítja a licencet, a következő kedvezmények érvényesek: Csapatlicenc {0}; Üzleti engedély {1}; Vállalati licenc {2}. Ha azonban a licenc lejárati dátuma óta <strong>1 hónap</strong> után megújítja a licencet, a megújítási ár megegyezik a licenc vásárlási árával, és nem jár kedvezmény a megújításra.",
"WhenShouldIRenewMyLicenseExplanation": "Ha a licenc lejártát követő <strong>{3} nap</strong> megújítja a licencet, a következő kedvezmények érvényesek: Csapatlicenc {0}; Üzleti engedély {1}; Vállalati licenc {2}. Ha azonban a licenc lejárati dátuma óta <strong>1 hónap</strong> után megújítja a licencet, a megújítási ár megegyezik a licenc vásárlási árával, és nem jár kedvezmény a megújításra.",
"TrialPlan": "Van próbaterv?",
"TrialPlanExplanation": "14 napos próbaidővel rendelkezik az ABP Commercial csapat licenszéhez. További információért látogasson el <a href={0} target='_blank'>ide</a> . Továbbá a Team licenszekre 30 napos pénz-visszafizetési garanciát biztosítunk. Az első 30 napban csak visszatérítést kérhet. A Business és Enterprise licenszek esetén 30 napon belül 60%-os visszatérítést biztosítunk. Ennek az az oka, hogy a Business és Enterprise licenszek tartalmazzák az összes modul és téma teljes forráskódját.",
"TrialPlanExplanation": "Nem, az ABP Commercialhoz nincs próbaverzió. Tekintse meg a közösségi kiadást, hogy megértse a kód minőségét és megközelítéseit. A Team licencre 30 napos pénz-visszafizetési garanciát is vállalunk, kérdés nélkül! Az első 30 napon belül kérheti a visszatérítést. Üzleti és vállalati licencek esetén 60% visszatérítést biztosítunk 30 napon belül. Ennek az az oka, hogy a Business és Enterprise licencek tartalmazzák az összes modul és téma teljes forráskódját.",
"DoYouAcceptBankWireTransfer": "Elfogadja a banki átutalást?",
"DoYouAcceptBankWireTransferExplanation": "Igen, elfogadunk banki átutalást.<br />Miután banki átutalással elküldte a licencdíjat, küldje el nekünk e-mailben az accounting@abp.io címre nyugtát és a kért engedély típusát. Nemzetközi bankszámlánk információi:",
"HowToUpgrade": "Hogyan lehet frissíteni a meglévő alkalmazásokat, ha új verzió érhető el?",
@ -749,4 +749,4 @@
"TotalNetPrice": "Total Net Price",
"BlackFridayDiscount": "Black Friday Kedvezmény"
}
}
}

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "Get ég breytt skráðum forriturum fyrirtækisins míns í framtíðinni?",
"ChangingDevelopersExplanation": "Til viðbótar við að bæta nýjum verktaka við leyfi þitt geturðu einnig breytt núverandi forriturum (þú getur fjarlægt verktaka og bætt nýjum við) án aukakostnaðar.",
"WhenShouldIRenewMyLicense": "Hvenær ætti ég að endurnýja leyfið mitt?",
"WhenShouldIRenewMyLicenseExplanation": "Ef þú endurnýjar leyfið þitt innan <strong>eins mánaðar</strong> eftir að leyfið þitt rennur út verða eftirfarandi afslættir notaðir: Team Leyfi {0}% afsláttur, Business License {1}% afsláttur, Enterprise License {2}% afsláttur . Ef þú endurnýjar leyfið þitt <strong>1 mánuði</strong> eftir að leyfið rennur út, verður endurnýjunarverðið það sama og kaupverð leyfisins og enginn afsláttur af endurnýjun þinni.",
"WhenShouldIRenewMyLicenseExplanation": "Ef þú endurnýjar leyfið þitt innan <strong>{3} dagar</strong> eftir að leyfið þitt rennur út verða eftirfarandi afslættir notaðir: Team Leyfi {0}% afsláttur, Business License {1}% afsláttur, Enterprise License {2}% afsláttur . Ef þú endurnýjar leyfið þitt <strong>{3} dagar</strong> eftir að leyfið rennur út, verður endurnýjunarverðið það sama og kaupverð leyfisins og enginn afsláttur af endurnýjun þinni.",
"TrialPlan": "Ertu með prufuáætlun?",
"DoYouAcceptBankWireTransfer": "Samþykki þið bankamillifærslu?",
"DoYouAcceptBankWireTransferExplanation": "Já, við tökum við bankamillifærslu.<br />Eftir að hafa sent leyfisgjaldið með millifærslu skaltu senda okkur tölvupóst á accounting@abp.io kvittun þína og tegund leyfis sem óskað er eftir. Upplýsingar um alþjóðlega bankareikninginn okkar:",

4
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "Posso cambiare gli sviluppatori registrati della mia organizzazione in futuro?",
"ChangingDevelopersExplanation": "Oltre ad aggiungere nuovi sviluppatori alla tua licenza, puoi anche Modifica gli sviluppatori esistenti (puoi rimuovere uno sviluppatore e aggiungerne uno nuovo alla stessa postazione) senza alcun costo aggiuntivo.",
"WhenShouldIRenewMyLicense": "Quando devo rinnovare la mia licenza?",
"WhenShouldIRenewMyLicenseExplanation": "Se rinnovi la licenza entro <strong>1 mese</strong> dopo la scadenza della licenza, verranno applicati i seguenti sconti: Licenza Team {0} ; Licenza Business {1} ; Licenza Enterprise {2} . Se rinnovi la licenza <strong>1 mese</strong> dopo la data di scadenza della licenza, il prezzo di rinnovo sarà lo stesso del prezzo di acquisto della licenza e non ci saranno sconti sul rinnovo.",
"WhenShouldIRenewMyLicenseExplanation": "Se rinnovi la licenza entro <strong>{3} giorni</strong> dopo la scadenza della licenza, verranno applicati i seguenti sconti: Licenza Team {0} ; Licenza Business {1} ; Licenza Enterprise {2} . Se rinnovi la licenza <strong>{3} giorni</strong> dopo la data di scadenza della licenza, il prezzo di rinnovo sarà lo stesso del prezzo di acquisto della licenza e non ci saranno sconti sul rinnovo.",
"TrialPlan": "Hai un piano di prova?",
"DoYouAcceptBankWireTransfer": "Accettate bonifici bancari?",
"DoYouAcceptBankWireTransferExplanation": "Sì, accettiamo bonifico bancario.<br />Dopo aver inviato il canone tramite bonifico bancario, inviaci un'e-mail a accounting@abp.io con la ricevuta e il tipo di licenza richiesta. Le nostre informazioni sul conto bancario internazionale:",
@ -398,7 +398,7 @@
"WhatHappensWhenLicenseEndsExplanation4": "Non è possibile installare nuovi moduli e temi aggiunti alla piattaforma ABP Commercial dopo la scadenza della licenza.",
"WhatHappensWhenLicenseEndsExplanation5": "Non è possibile utilizzare la suite ABP.",
"WhatHappensWhenLicenseEndsExplanation6": "Non è più possibile ottenere il <a href=\"{0}\">supporto premium</a>.",
"WhatHappensWhenLicenseEndsExplanation7": "Puoi estendere (rinnovare) la tua licenza se desideri continuare a ottenere questi vantaggi. Se estendi la licenza entro <strong>1 mese</strong> dalla scadenza della licenza, verranno applicati i seguenti sconti: Licenza Team {0}; Licenza commerciale {1}; Licenza aziendale {2}.",
"WhatHappensWhenLicenseEndsExplanation7": "Puoi estendere (rinnovare) la tua licenza se desideri continuare a ottenere questi vantaggi. Se estendi la licenza entro <strong>{3} giorni</strong> dalla scadenza della licenza, verranno applicati i seguenti sconti: Licenza Team {0}; Licenza commerciale {1}; Licenza aziendale {2}.",
"BlazoriseLicense": "Dobbiamo acquistare la licenza di Blazorise?",
"BlazoriseLicenseExplanation": "Abbiamo un accordo tra Volosoft e Megabit, con il quale la licenza di Blazorise viene fornita in bundle con i prodotti commerciali ABP, pertanto i nostri clienti non hanno bisogno di acquistare una licenza Blazorise aggiuntiva."
}

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "Kan ik de geregistreerde ontwikkelaars van mijn organisatie in de toekomst wijzigen?",
"ChangingDevelopersExplanation": "Naast het toevoegen van nieuwe ontwikkelaars aan uw licentie, kunt u ook de bestaande ontwikkelaars wijzigen (u kunt een ontwikkelaar verwijderen en een nieuwe toevoegen aan dezelfde stoel) zonder extra kosten.",
"WhenShouldIRenewMyLicense": "Wanneer moet ik mijn licentie verlengen?",
"WhenShouldIRenewMyLicenseExplanation": "Als u uw licentie verlengt binnen <strong>1 maand</strong> nadat uw licentie is verlopen, worden de volgende kortingen toegepast: Teamlicentie {0}% korting, Zakelijke licentie {1}% korting, Enterprise-licentie {2}% korting . Als u uw licentie <strong>1 maand</strong> na de vervaldatum van uw licentie verlengt, is de verlengingsprijs gelijk aan de aankoopprijs van de licentie en wordt er geen korting op uw verlenging gegeven.",
"WhenShouldIRenewMyLicenseExplanation": "Als u uw licentie verlengt binnen <strong>{3} dagen</strong> nadat uw licentie is verlopen, worden de volgende kortingen toegepast: Teamlicentie {0}% korting, Zakelijke licentie {1}% korting, Enterprise-licentie {2}% korting . Als u uw licentie <strong>{3} dagen</strong> na de vervaldatum van uw licentie verlengt, is de verlengingsprijs gelijk aan de aankoopprijs van de licentie en wordt er geen korting op uw verlenging gegeven.",
"TrialPlan": "Heb je een proefplan?",
"DoYouAcceptBankWireTransfer": "Accepteert u bankoverschrijvingen?",
"DoYouAcceptBankWireTransferExplanation": "Ja, we accepteren bankoverschrijvingen.<br />Nadat u de licentievergoeding via bankoverschrijving heeft verzonden, stuurt u een e-mail naar accounting@abp.io met uw kwitantie en het type licentie dat u heeft aangevraagd. Onze internationale bankrekeninggegevens:",

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "Czy mogę w przyszłości zmienić zarejestrowanych programistów mojej organizacji?",
"ChangingDevelopersExplanation": "Oprócz dodawania nowych programistów do swojej licencji możesz również zmieniać istniejących programistów (możesz usunąć programistę i dodać nowego do tego samego stanowiska) bez żadnych dodatkowych kosztów.",
"WhenShouldIRenewMyLicense": "Kiedy powinienem odnowić licencję?",
"WhenShouldIRenewMyLicenseExplanation": "Jeśli odnowisz licencję w ciągu <strong>1 miesiąca</strong> po wygaśnięciu licencji, zostaną zastosowane następujące rabaty: licencja zespołowa {0}% rabatu, licencja biznesowa {1}% rabatu, licencja Enterprise {2}% rabatu . Jeśli odnowisz licencję <strong>1 miesiąc</strong> po dacie wygaśnięcia licencji, cena odnowienia będzie taka sama jak cena zakupu licencji i nie będzie rabatu na odnowienie.",
"WhenShouldIRenewMyLicenseExplanation": "Jeśli odnowisz licencję w ciągu <strong>{3} dni</strong> po wygaśnięciu licencji, zostaną zastosowane następujące rabaty: licencja zespołowa {0}% rabatu, licencja biznesowa {1}% rabatu, licencja Enterprise {2}% rabatu . Jeśli odnowisz licencję <strong>1 miesiąc</strong> po dacie wygaśnięcia licencji, cena odnowienia będzie taka sama jak cena zakupu licencji i nie będzie rabatu na odnowienie.",
"TrialPlan": "Czy masz plan próbny?",
"DoYouAcceptBankWireTransfer": "Czy akceptujesz przelew bankowy?",
"DoYouAcceptBankWireTransferExplanation": "Tak, akceptujemy przelew bankowy.<br />Po wysłaniu opłaty licencyjnej przelewem bankowym wyślij nam e-mail na adres accounting@abp.io z potwierdzeniem odbioru i rodzajem żądanej licencji. Informacje o naszych międzynarodowych kontach bankowych:",

4
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json

@ -191,7 +191,7 @@
"ChangingDevelopers": "Posso mudar os desenvolvedores registrados da minha organização no futuro?",
"ChangingDevelopersExplanation": "Além de adicionar novos desenvolvedores à sua licença, você também pode alterar os desenvolvedores existentes (você pode remover um desenvolvedor e adicionar um novo ao mesmo assento) sem nenhum custo adicional.",
"WhenShouldIRenewMyLicense": "Quando devo renovar minha licença?",
"WhenShouldIRenewMyLicenseExplanation": "Se você renovar sua licença em <strong> 1 mês </strong> após a expiração da licença, os seguintes descontos serão aplicados: Licença de equipe {0} ; Licença de negócios {1} ; Licença empresarial {2} . Se você renovar sua licença <strong> 1 mês </strong> após a data de expiração de sua licença, o preço de renovação será o mesmo que o preço de compra da licença e não haverá desconto em sua renovação.",
"WhenShouldIRenewMyLicenseExplanation": "Se você renovar sua licença em <strong> {3} dias </strong> após a expiração da licença, os seguintes descontos serão aplicados: Licença de equipe {0} ; Licença de negócios {1} ; Licença empresarial {2} . Se você renovar sua licença <strong> {3} dias </strong> após a data de expiração de sua licença, o preço de renovação será o mesmo que o preço de compra da licença e não haverá desconto em sua renovação.",
"TrialPlan": "Você tem um plano experimental?",
"DoYouAcceptBankWireTransfer": "Você aceita transferência bancária?",
"DoYouAcceptBankWireTransferExplanation": "Sim, aceitamos transferência bancária. <br /> Após enviar a taxa de licença por transferência bancária, envie-nos um e-mail para accounting@abp.io com seu recibo e o tipo de licença solicitada. Nossas informações de conta bancária internacional:",
@ -402,6 +402,6 @@
"WhatHappensWhenLicenseEndsExplanation4": "Você não pode instalar novos módulos e temas adicionados à plataforma comercial da ABP após o término de sua licença.",
"WhatHappensWhenLicenseEndsExplanation5": "Você não pode usar o ABP Suite",
"WhatHappensWhenLicenseEndsExplanation6": "Você não pode mais obter o <a href==\"{0}\">apoio de premium</a>.",
"WhatHappensWhenLicenseEndsExplanation7": "Você pode estender (renovar) sua licença se quiser continuar obtendo esses benefícios. Se você estender sua licença dentro de <strong>1 mês</strong> após a expiração de sua licença, os seguintes descontos serão aplicados: Licença de equipe {0}; Licença Comercial {1}; Licença empresarial {2}."
"WhatHappensWhenLicenseEndsExplanation7": "Você pode estender (renovar) sua licença se quiser continuar obtendo esses benefícios. Se você estender sua licença dentro de <strong>{3} dias</strong> após a expiração de sua licença, os seguintes descontos serão aplicados: Licença de equipe {0}; Licença Comercial {1}; Licença empresarial {2}."
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "Pe viitor, pot schimba dezvoltatorii ataşaţi organizaţiei mele?",
"ChangingDevelopersExplanation": "În plus faţă de adăugarea de noi dezvoltatori la licenţa dumneavoastră, puteţi schimba şi dezvoltatorii existenţi(puteţi elimina un dezvoltator şi adăuga unul nou în locul lui) fără costuri suplimentare.",
"WhenShouldIRenewMyLicense": "Când ar trebui să-mi reînnoiesc licenţa?",
"WhenShouldIRenewMyLicenseExplanation": "Dacă vă reînnoiți licența în termen de <strong>o lună</strong> de la expirarea licenței, se vor aplica următoarele reduceri: Licență de echipă {0}% reducere, Licență de afaceri {1}% reducere, Licență Enterprise {2}% reducere . Dacă vă reînnoiți licența la <strong>1 lună</strong> după data de expirare a licenței, prețul de reînnoire va fi același cu prețul de achiziție a licenței și nu va exista nicio reducere la reînnoirea dvs.",
"WhenShouldIRenewMyLicenseExplanation": "Dacă vă reînnoiți licența în termen de <strong>{3} de zile</strong> de la expirarea licenței, se vor aplica următoarele reduceri: Licență de echipă {0}% reducere, Licență de afaceri {1}% reducere, Licență Enterprise {2}% reducere . Dacă vă reînnoiți licența la <strong>{3} de zile</strong> după data de expirare a licenței, prețul de reînnoire va fi același cu prețul de achiziție a licenței și nu va exista nicio reducere la reînnoirea dvs.",
"TrialPlan": "Aveţi un plan de încercare?",
"DoYouAcceptBankWireTransfer": "Acceptaţi transfer bancar?",
"DoYouAcceptBankWireTransferExplanation": "Da, acceptăm transfer bancar.<br />După ce ați trimis taxa de licență prin transfer bancar, trimiteți-ne un e-mail la accounting@abp.io chitanța dvs. și tipul de licență solicitat. Informațiile noastre internaționale despre contul nostru bancar:",
@ -381,4 +381,4 @@
"CommercialNewsletterConfirmationMessage": "Sunt de acord cu <a href=\"https://commercial.abp.io/TermsConditions\">Termenii și condițiile</a> și cu <a href=\"https://commercial.abp.io/Privacy\">Politica de confidențialitate </a>.",
"BlackFridayDiscount": "Black Friday Discount"
}
}
}

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ru.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "Могу ли я сменить зарегистрированных разработчиков моей организации в будущем?",
"ChangingDevelopersExplanation": "Помимо добавления новых разработчиков к вашей лицензии, вы также можете изменить существующих разработчиков (вы можете удалить разработчика и добавить нового на то же место) без каких-либо дополнительных затрат.",
"WhenShouldIRenewMyLicense": "Когда мне следует продлить лицензию?",
"WhenShouldIRenewMyLicenseExplanation": "Если вы продлите лицензию в течение <strong> 1 месяца </strong> после истечения срока действия лицензии, будут применяться следующие скидки: групповая лицензия {0}% скидка, бизнес-лицензия {1}% скидка, корпоративная лицензия {2}% скидка . Если вы продлеваете лицензию <strong> через 1 месяц </strong> после даты истечения срока действия лицензии, цена продления будет такой же, как цена покупки лицензии, и при продлении скидки не будет.",
"WhenShouldIRenewMyLicenseExplanation": "Если вы продлите лицензию в течение <strong> {3} дней </strong> после истечения срока действия лицензии, будут применяться следующие скидки: групповая лицензия {0}% скидка, бизнес-лицензия {1}% скидка, корпоративная лицензия {2}% скидка . Если вы продлеваете лицензию <strong> {3} дней </strong> после даты истечения срока действия лицензии, цена продления будет такой же, как цена покупки лицензии, и при продлении скидки не будет.",
"TrialPlan": "У вас есть пробный план?",
"DoYouAcceptBankWireTransfer": "Вы принимаете банковский перевод?",
"DoYouAcceptBankWireTransferExplanation": "Да, мы принимаем банковский перевод. <br /> После отправки платы за лицензию банковским переводом отправьте нам электронное письмо по адресу accounting@abp.io, квитанцию и тип запрошенной лицензии. Информация о нашем международном банковском счете:",

4
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "Môžem v budúcnosti zmeniť registrovaných vývojárov svojej organizácie?",
"ChangingDevelopersExplanation": "Okrem pridávania nových vývojárov do licencie môžete tiež meniť existujúcich vývojárov (môžete odstrániť vývojára a pridať nového na to isté miesto) bez akýchkoľvek ďalších nákladov.",
"WhenShouldIRenewMyLicense": "Kedy si mám obnoviť licenciu?",
"WhenShouldIRenewMyLicenseExplanation": "Ak si licenciu obnovíte do <strong>1 mesiaca</strong> po vypršaní platnosti licencie, budú sa uplatňovať tieto zľavy: Tímová licencia {0} ; Obchodná licencia {1} ; Enterprise licencia {2} . Ak si licenciu obnovíte <strong>1 mesiac</strong> po dátume skončenia platnosti licencie, cena za obnovenie bude rovnaká ako cena za nákup licencie a na obnovenie nebude poskytnutá žiadna zľava.",
"WhenShouldIRenewMyLicenseExplanation": "Ak si licenciu obnovíte do <strong>30 dní</strong> po vypršaní platnosti licencie, budú sa uplatňovať tieto zľavy: Tímová licencia {0} ; Obchodná licencia {1} ; Enterprise licencia {2} . Ak si licenciu obnovíte <strong>30 dní</strong> po dátume skončenia platnosti licencie, cena za obnovenie bude rovnaká ako cena za nákup licencie a na obnovenie nebude poskytnutá žiadna zľava.",
"TrialPlan": "Máte skúšobný plán?",
"DoYouAcceptBankWireTransfer": "Prijímate bankový prevod?",
"DoYouAcceptBankWireTransferExplanation": "Áno, akceptujeme bankový prevod.<br />Po odoslaní licenčného poplatku bankovým prevodom nám pošlite e-mail na adresu accounting@abp.io svoje potvrdenie a požadovaný typ licencie. Informácie o našom medzinárodnom bankovom účte:",
@ -396,7 +396,7 @@
"WhatHappensWhenLicenseEndsExplanation4": "Po skončení platnosti vašej licencie nemôžete inštalovať nové moduly a témy pridané do platformy ABP Commercial.",
"WhatHappensWhenLicenseEndsExplanation5": "Balík ABP Suite nemôžete používať.",
"WhatHappensWhenLicenseEndsExplanation6": "Už nemôžete získať <a href=\"{0}\">prémiovú podporu</a>.",
"WhatHappensWhenLicenseEndsExplanation7": "Ak chcete naďalej využívať tieto výhody, môžete si licenciu predĺžiť (obnoviť). Ak predĺžite svoju licenciu do <strong>1 mesiaca</strong> po vypršaní platnosti licencie, budú sa uplatňovať nasledujúce zľavy: Tímová licencia {0}; Obchodná licencia {1}; Podniková licencia {2}",
"WhatHappensWhenLicenseEndsExplanation7": "Ak chcete naďalej využívať tieto výhody, môžete si licenciu predĺžiť (obnoviť). Ak predĺžite svoju licenciu do <strong>30 dní</strong> po vypršaní platnosti licencie, budú sa uplatňovať nasledujúce zľavy: Tímová licencia {0}; Obchodná licencia {1}; Podniková licencia {2}",
"BlazoriseLicense": "Musíme si kúpiť licenciu Blazorise?",
"BlazoriseLicenseExplanation": "Máme dohodu medzi spoločnosťami Volosoft a Megabit, na základe ktorej je licencia Blazorise pribalená k produktom ABP Commercial, preto si naši zákazníci nemusia kupovať ďalšiu licenciu Blazorise."
}

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "Ali lahko v prihodnosti spremenim registrirane razvijalce svoje organizacije?",
"ChangingDevelopersExplanation": "Poleg dodajanja novih razvijalcev vaši licenci lahko spremenite tudi obstoječe razvijalce (lahko odstranite razvijalca in dodate novega na isto mesto) brez dodatnih stroškov.",
"WhenShouldIRenewMyLicense": "Kdaj naj podaljšam licenco?",
"WhenShouldIRenewMyLicenseExplanation": "Če licenco podaljšate v <strong>1 mesecu</strong> po poteku licence, bodo uporabljeni naslednji popusti: licenca za ekipo {0} % popusta, poslovna licenca {1} % popusta, licenca za podjetje {2} % popusta . Če podaljšate licenco <strong>1 mesec</strong> po datumu poteka veljavnosti licence, bo cena podaljšanja enaka nakupni ceni licence in pri podaljšanju ne bo popusta.",
"WhenShouldIRenewMyLicenseExplanation": "Če licenco podaljšate v <strong>{3} dni</strong> po poteku licence, bodo uporabljeni naslednji popusti: licenca za ekipo {0} % popusta, poslovna licenca {1} % popusta, licenca za podjetje {2} % popusta . Če podaljšate licenco <strong>{3} dni</strong> po datumu poteka veljavnosti licence, bo cena podaljšanja enaka nakupni ceni licence in pri podaljšanju ne bo popusta.",
"TrialPlan": "Ali imate poskusni načrt?",
"DoYouAcceptBankWireTransfer": "Ali sprejemate bančno nakazilo?",
"DoYouAcceptBankWireTransferExplanation": "Da, sprejemamo bančno nakazilo.<br />Ko pošljete licenčnino prek bančnega nakazila, nam pošljite e-pošto na accounting@abp.io vaše potrdilo o prejemu in vrsto zahtevane licence. Naši podatki o mednarodnem bančnem računu:",

22
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json

@ -193,7 +193,7 @@
"ChangingDevelopers": "Gelecekte kuruluşumun kayıtlı geliştiricilerini değiştirebilir miyim?",
"ChangingDevelopersExplanation": "Lisansınıza yeni geliştiriciler eklemenin yanı sıra mevcut geliştiricileri de herhangi bir ek ücret ödemeden değiştirebilirsiniz (bir geliştiriciyi kaldırıp aynı koltuğa yeni bir tane ekleyebilirsiniz).",
"WhenShouldIRenewMyLicense": "Ehliyetimi ne zaman yenilemeliyim?",
"WhenShouldIRenewMyLicenseExplanation": "Lisansınızın süresi dolduktan sonra <strong>1 ay</strong> içinde lisansınızı yenilerseniz, aşağıdaki indirimler uygulanacaktır: Takım Lisansı %{0} indirim, İşletme Lisansı %{1} indirim, Kurumsal Lisans %{2} indirim . Lisansınızın sona erme tarihinden <strong>1 ay</strong> sonra lisansınızı yenilerseniz, yenileme fiyatı lisans satın alma fiyatı ile aynı olacak ve yenilemenizde indirim yapılmayacaktır.",
"WhenShouldIRenewMyLicenseExplanation": "Lisansınızın süresi dolduktan sonra <strong>{3} gün</strong> içinde lisansınızı yenilerseniz, aşağıdaki indirimler uygulanacaktır: Takım Lisansı %{0} indirim, İşletme Lisansı %{1} indirim, Kurumsal Lisans %{2} indirim . Lisansınızın sona erme tarihinden <strong>{3} gün</strong> sonra lisansınızı yenilerseniz, yenileme fiyatı lisans satın alma fiyatı ile aynı olacak ve yenilemenizde indirim yapılmayacaktır.",
"TrialPlan": "Deneme planınız var mı?",
"DoYouAcceptBankWireTransfer": "Banka havalesini kabul ediyor musunuz?",
"DoYouAcceptBankWireTransferExplanation": "Evet, banka havalesini kabul ediyoruz.<br />Lisans ücretini banka havalesi yoluyla gönderdikten sonra, dekontunuzu ve talep edilen lisans türünü accounting@abp.io adresinden bize e-posta ile gönderin. Uluslararası banka hesap bilgilerimiz:",
@ -393,16 +393,16 @@
"LastNameField": "Soyad",
"AbpCommercialMetaTitle": " {0} | ABP Commercial",
"AbpCommercialMetaDescription": "ABP Commercial, açık kaynaklı ABP çerçevesinin üzerine inşa edilmiş önceden oluşturulmuş uygulama modülleri, hızlı geliştirme araçları, kullanıcı arayüzü temaları ve hizmetlerinden oluşan bir settir.",
"WhatHappensWhenLicenseEnds": "Lisans sürem sona erdiğinde ne olacak?",
"WhatHappensWhenLicenseEnds": "Lisans sürem sona erdiğinde ne olacak?",
"WhatHappensWhenLicenseEndsExplanation1": "ABP Ticari lisansı <a href=\"{0}\" target=\"_blank\">kalıcı bir lisanstır</a>. Lisansınızın süresi dolduktan sonra projenizi geliştirmeye devam edebilirsiniz. Ve lisansınızı yenilemek zorunda değilsiniz. Lisansınız kutudan bir yıllık güncelleme ve destek planı ile birlikte gelir. Yeni özellikler, performans geliştirmeleri, hata düzeltmeleri, destek almaya devam etmek ve ABP Suite'i kullanmaya devam etmek için lisansınızı yenilemeniz gerekir. Lisansınızın süresi dolduğunda aşağıdaki avantajlardan yararlanamazsınız:",
"WhatHappensWhenLicenseEndsExplanation2": "ABP Ticari'yi kullanarak yeni çözümler oluşturamazsınız, ancak mevcut uygulamalarınızı sonsuza kadar geliştirmeye devam edebilirsiniz.",
"WhatHappensWhenLicenseEndsExplanation3": "MINOR sürümünüzdeki modüller ve temalar için güncellemeleri alabileceksiniz (RC veya Önizleme sürümleri hariç). Örneğin: bir modülün v3.2.0 sürümünü kullanıyorsanız, bu modülün v3.2.x (v3.2.1, v3.2.5... vb.) sürümleri için güncellemeleri almaya devam edebilirsiniz. Ancak bir sonraki büyük veya küçük sürüm için güncelleme alamazsınız (v3.3.0, v3.3.3, 4.x.x... gibi). Örneğin, lisansınızın süresi dolduğunda, en son sürüm v4.4.3 idi ve daha sonra hem 4.4.4 sürümünü hem de 4.5.0 sürümünü yayınladı, v4.4.X'e erişebilirsiniz, ancak v4.5.X'e erişemezsiniz.",
"WhatHappensWhenLicenseEndsExplanation4": "Lisansınız sona erdikten sonra ABP Ticari platformuna eklenen yeni modülleri ve temaları yükleyemezsiniz.",
"WhatHappensWhenLicenseEndsExplanation5": "ABP Suite'i kullanamazsınız.",
"WhatHappensWhenLicenseEndsExplanation6": "Artık <a href=\"{0}\">premium desteği</a> alamazsınız.",
"WhatHappensWhenLicenseEndsExplanation7": "Bu avantajlardan yararlanmaya devam etmek istiyorsanız lisansınızı uzatabilirsiniz (yenileyebilirsiniz). Lisansınızın süresi dolduktan sonra <strong>1 ay</strong> içinde lisansınızı uzatırsanız, aşağıdaki indirimler uygulanacaktır: Takım Lisansı {0} % indirim, İşletme Lisansı %{1} indirim, Kurumsal Lisans %{2} indirim.",
"WhatHappensWhenLicenseEndsExplanation7": "Bu avantajlardan yararlanmaya devam etmek istiyorsanız lisansınızı uzatabilirsiniz (yenileyebilirsiniz). Lisansınızın süresi dolduktan sonra <strong>{3} gün</strong> içinde lisansınızı uzatırsanız, aşağıdaki indirimler uygulanacaktır: Takım Lisansı {0} % indirim, İşletme Lisansı %{1} indirim, Kurumsal Lisans %{2} indirim.",
"WhatHappensWhenLicenseEndsExplanation8": "Oluşturduğunuz ABP projeleri sunucularımızda saklanmamaktadır. Bu nedenle indirdiğiniz kaynak kodunu saklamak sizin sorumluluğunuzdadır. Lisansınızın süresi dolduğunda, oluşturulan ABP proje kaynak kodunuzu almanın bir yolu yoktur.",
"TrialPlanExplanation": "ABP Ticari takım lisansı için 14 günlük deneme süresi var. Daha fazla bilgi için <a href={0} target='_blank'>burayı</a> ziyaret edin. Ayrıca, Takım lisansları için 30 günlük para iade garantisi veriyoruz. Sadece ilk 30 gün içinde geri ödeme talebinde bulunabilirsiniz. İşletme ve Kurumsal lisansları için 30 gün içinde %60 geri ödeme sağlıyoruz. Bunun nedeni, İşletme ve Kurumsal lisanslarının tüm modüllerin ve temaların tam kaynak kodunu içermesidir.",
"TrialPlanExplanation": "Hayır, ABP Commercial için deneme sürümü yoktur. Kod kalitesini ve yaklaşımları anlamak için topluluk sürümünü kontrol edebilirsiniz. Ayrıca, Takım lisansları için 30 günlük para iade garantisi veriyoruz. Sadece ilk 30 gün içinde geri ödeme talebinde bulunabilirsiniz. İşletme ve Kurumsal lisansları için 30 gün içinde %60 geri ödeme sağlıyoruz. Bunun nedeni, İşletme ve Kurumsal lisanslarının tüm modüllerin ve temaların tam kaynak kodunu içermesidir.",
"ContactUsQuestions": "Herhangi bir sorunuz varsa bizimle iletişime geçin",
"ActivationRequirement": "Denemenizi başlatmanıza son bir adım kaldı.<br>Bilgilerinizi kontrol ettikten sonra lisansınızı etkinleştireceğiz. Lisansınız etkinleştirildiğinde, <b>{0}</b> adresine bir e-posta göndereceğiz. Merak etmeyin bu süreç uzun sürmeyecek!",
"PurchaseNow": "Şimdi satın al",
@ -419,7 +419,7 @@
"OpenSourceWebApplication": "Açık Kaynak Web Uygulaması",
"CompleteWebDevelopment": "Tam Web Geliştirme",
"ABPFrameworkDescription": "ABP Framework, yazılım geliştirme ve sözleşmelerin en iyi uygulamalarını takip ederek modern web uygulamaları oluşturmak için eksiksiz bir altyapıdır.",
"CommunityDescription": "ABP Çerçevesi ile ilgili deneyimlerinizi paylaşın!",
"CommunityDescription": "ABP Çerçevesi ile ilgili deneyimlerinizi paylaşın!",
"GetStarted": "Başlayın",
"Views": "Görünümler",
"LatestPosts": "Son Gönderiler",
@ -441,7 +441,7 @@
"ModularArchitectureExplanation": "Bu başlangıç şablonu, temiz ve sürdürülebilir bir kod tabanı oluşturmak için katmanlı, modüler ve DDD tabanlı bir çözüm mimarisi sağlar.",
"SeeDetails": "Ayrıntıları Gör",
"SeeDocumentation": "Belgelere göz atın",
"Bs5Compatible": "Bootstrap 5 uyumlu profesyonel tema, yönetici web siteniz için mükemmel.",
"Bs5Compatible": "Bootstrap 5 uyumlu profesyonel tema, yönetici web siteniz için mükemmel.",
"LeptonXTheme": "LeptonX Tema",
"LeptonXDark": "LeptonX Koyu",
"LeptonXLight": "LeptonX Açık",
@ -454,7 +454,7 @@
"EasilyInstallAndUpgrade": "Kolay kurulum ve yükseltme",
"SupportForum": "Destek Forumu",
"TrustedBy": "Güvenenler",
"OurPricing": "<span class=\"gradient-commercial\">Fiyatlandırmamız</span>",
"OurPricing": "<span class=\"gradient-commercial\">Fiyatlandırmamız</span>",
"Plans": "Planlar",
"NameSurname": "Ad Soyad",
"Unspecified": "Belirtilmemiş",
@ -465,7 +465,7 @@
"License": "Lisans",
"Development": "Geliştirme",
"Payment": "Ödeme",
"WatchExplainerVideo": "Hadi Tanışalım! Açıklayıcı Videoyu İzleyin",
"WatchExplainerVideo": "Hadi Tanışalım! Açıklayıcı Videoyu İzleyin",
"LightDarkAndSemiDarkThemes": "Açık, koyu ve yarı koyu temalar",
"LeptonXThemeExplanation": "Lepton Teması, temanızı sistem ayarlarınıza göre değiştirebilir.",
"PRO": "PRO",
@ -654,7 +654,7 @@
"UpgradePaymentInfoSection_LicenseUpgradeDescription": "Lisansınızı yükselterek, ek avantajlar elde etmenizi sağlayacak daha yüksek bir lisans türüne terfi edeceksiniz. Lisans türleri arasındaki farkları kontrol etmek için <a href=\"/Pricing\" target=\"_blank\">lisans karşılaştırma tablosuna</a> bakın.",
"Landing_Page_CustomerStories": "Müşteri Hikayeleri",
"Landing_Page_OurGreatCustomers": "Büyük Müşterilerimiz",
"Landing_Page_WebApplicationFramework": "Web Uygulama Çerçevesi",
"Landing_Page_WebApplicationFramework": "Web Uygulama Çerçevesi",
"Landing_Page_WebDevelopmentPlatform": "Web Geliştirme Platformu",
"Landing_Page_CompleteWebDevelopmentPlatform": "Tam Web Geliştirme Platformu",
"Landing_Page_TryFreeDemo": "Ücretsiz Demo Dene",
@ -721,7 +721,7 @@
"Landing_Page_DocsModuleDescription_9": "GitHub kaynağına ek olarak, dokümantasyon kaynağı olarak bir klasörün kullanılmasına izin verir.",
"Landing_Page_FileManagementModuleDescription_1": "Dosyaları hiyerarşik bir klasör yapısı içinde yükleyin, indirin ve düzenleyin.",
"Landing_Page_FileManagementModuleDescription_2": "Bu modül, dosyaları hiyerarşik bir klasör yapısında yüklemek, indirmek ve düzenlemek için kullanılır. Ayrıca çoklu kiracılığa uyumludur ve kiracılarınız için toplam boyut sınırını belirleyebilirsiniz.",
"Landing_Page_FileManagementModuleDescription_3": "Bu modül <a href=\"https://docs.abp.io/en/abp/latest/Blob-Storing\">BLOB Depolama</a> sistemine dayanmaktadır, bu nedenle dosya içeriklerini depolamak için farklı depolama sağlayıcıları kullanabilir.",
"Landing_Page_FileManagementModuleDescription_3": "Bu modül <a href=\"https://docs.abp.io/en/abp/latest/Blob-Storing\">BLOB Depolama</a> sistemine dayanmaktadır, bu nedenle dosya içeriklerini depolamak için farklı depolama sağlayıcıları kullanabilir.",
"Landing_Page_IdentityModuleDescription_1": "Bu modül bir uygulamanın Kullanıcı ve Rol sistemini uygular;",
"Landing_Page_IdentityModuleDescription_2": "<a href=\"https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity\">Microsoft'un ASP.NET Core Identity</a> kütüphanesi üzerine inşa edilmiştir.",
"Landing_Page_IdentityModuleDescription_3": "Sistemdeki <strong>rolleri</strong> ve <strong>kullanıcıları</strong> yönetin. Bir kullanıcının <strong>birden çok role</strong> sahip olmasına izin verilir.",
@ -752,4 +752,4 @@
"TrainingsPageTitle": "ABP Eğitim Paketleri",
"ModulesPageTitle": "ABP Önceden Oluşturulmuş Uygulama Modülleri"
}
}
}

2
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json

@ -190,7 +190,7 @@
"ChangingDevelopers": "Tôi có thể thay đổi các nhà phát triển đã đăng ký của tổ chức của mình trong tương lai không?",
"ChangingDevelopersExplanation": "Ngoài việc thêm các nhà phát triển mới vào giấy phép của mình, bạn cũng có thể thay đổi các nhà phát triển hiện có (bạn có thể xóa một nhà phát triển và thêm một nhà phát triển mới vào cùng một chỗ ngồi) mà không phải trả thêm bất kỳ chi phí nào.",
"WhenShouldIRenewMyLicense": "Khi nào tôi nên gia hạn giấy phép của mình?",
"WhenShouldIRenewMyLicenseExplanation": "Nếu bạn gia hạn giấy phép của mình trong vòng <strong> 1 tháng </strong> sau khi giấy phép của bạn hết hạn, các chiết khấu sau sẽ được áp dụng: Giảm giá {0}% cho Giấy phép Nhóm, Giảm giá {1}% Giấy phép Kinh doanh, Giảm giá {2}% Giấy phép Doanh nghiệp . Nếu bạn gia hạn giấy phép <strong> 1 tháng </strong> sau ngày giấy phép hết hạn, giá gia hạn sẽ giống như giá mua giấy phép và sẽ không có chiết khấu khi gia hạn của bạn.",
"WhenShouldIRenewMyLicenseExplanation": "Nếu bạn gia hạn giấy phép của mình trong vòng <strong> {3} ngày </strong> sau khi giấy phép của bạn hết hạn, các chiết khấu sau sẽ được áp dụng: Giảm giá {0}% cho Giấy phép Nhóm, Giảm giá {1}% Giấy phép Kinh doanh, Giảm giá {2}% Giấy phép Doanh nghiệp . Nếu bạn gia hạn giấy phép <strong> {3} ngày </strong> sau ngày giấy phép hết hạn, giá gia hạn sẽ giống như giá mua giấy phép và sẽ không có chiết khấu khi gia hạn của bạn.",
"TrialPlan": "Bạn có kế hoạch dùng thử không?",
"DoYouAcceptBankWireTransfer": "Bạn có chấp nhận chuyển khoản ngân hàng không?",
"DoYouAcceptBankWireTransferExplanation": "Có, chúng tôi chấp nhận chuyển khoản ngân hàng. <br /> Sau khi gửi phí cấp phép qua chuyển khoản ngân hàng, hãy gửi email cho chúng tôi theo địa chỉ accounting@abp.io biên lai của bạn và loại giấy phép được yêu cầu. Thông tin tài khoản ngân hàng quốc tế của chúng tôi:",

8
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json

@ -202,13 +202,13 @@
"WhatHappensWhenLicenseEndsExplanation4": "你不能在你的许可证到期后安装ABP商业平台上添加的新模块和主题。",
"WhatHappensWhenLicenseEndsExplanation5": "你不能使用ABP Suite。",
"WhatHappensWhenLicenseEndsExplanation6": "你不能再获得<a href=\"{0}\">高级支持</a>。",
"WhatHappensWhenLicenseEndsExplanation7": "如果您想继续获得这些好处,您可以延长(续订)您的许可证。 如果您在许可证到期后 <strong>1 个月</strong>内延长许可证,将应用以下折扣:团队许可证 {0}; 营业执照{1}; 企业许可证 {2}。",
"WhatHappensWhenLicenseEndsExplanation7": "如果您想继续获得这些好处,您可以延长(续订)您的许可证。 如果您在许可证到期后 <strong>{3} 天</strong>内延长许可证,将应用以下折扣:团队许可证 {0}; 营业执照{1}; 企业许可证 {2}。",
"discountForYears": "{0}% 折扣 {1} 年",
"WhatHappensWhenLicenseEndsExplanation8": "您生成的 ABP 项目未存储在我们的服务器上。 因此,您有责任保留下载的源代码。 当您的许可证到期时,将无法获取您生成的 ABP 项目源代码。",
"WhenShouldIRenewMyLicense": "我什么时候应该续订我的许可?",
"WhenShouldIRenewMyLicenseExplanation": "如果您在许可证到期后 <strong>1 个月</strong> 内续订许可证,将适用以下折扣:团队许可证 {0}; 营业执照{1}; 企业许可证 {2}。 但是,如果您在许可证到期后 <strong>1 个月</strong> 后续订许可证,则续订价格将与许可证购买价格相同,并且您的续订不会有任何折扣。",
"WhenShouldIRenewMyLicenseExplanation": "如果您在许可证到期后 <strong>{3} 天</strong> 内续订许可证,将适用以下折扣:团队许可证 {0}; 营业执照{1}; 企业许可证 {2}。 但是,如果您在许可证到期后 <strong>{3} 天</strong> 后续订许可证,则续订价格将与许可证购买价格相同,并且您的续订不会有任何折扣。",
"TrialPlan": "你们有试用计划吗?",
"TrialPlanExplanation": "ABP商业团队许可证有14天的试用期。若要了解更多信息,请访问<a href={0} target='_blank'>这里</a>。此外,我们为团队许可证提供30天的金额返还保证。你只需要在30天内请求退款。商业和企业许可证将提供60%的金额返还保证。这是因为商业和企业许可证包含了所有模块和主题的全部源代码。",
"TrialPlanExplanation": "不,ABP商业版没有试用版。您可以通过查看社区版了解代码质量和方法。我们还为团队许可证提供30天无条件退款保证!您可以在前30天内申请退款。对于商业和企业许可证,我们在30天内提供60%的退款。这是因为商业和企业许可证包含所有模块和主题的完整源代码。",
"DoYouAcceptBankWireTransfer": "你们接受银行电汇吗?",
"DoYouAcceptBankWireTransferExplanation": "是的,我们接受银行电汇。<br />在通过银行转账发送许可费后,将您的收据和所需的许可类型通过电子邮件发送至accounting@abp.io。 我们的国际银行账户信息:",
"HowToUpgrade": "可用新版本时如何升级现有应用程序?",
@ -815,7 +815,7 @@
"Other": "其他",
"WhereDidYouHearAboutUs_explain": "请说明...",
"DeletingMemberWarningMessage": "\"{0}\"将从开发者列表中删除。如果您愿意,您可以将此空位分配给另一个开发者。",
"AdditionalInfo": "如果开发者席位高于您的要求,您可以减少它们。您可以发送电子邮件至<a href=\"mailto:info@abp.io\">info@abp.io</a>以删除一些开发人员席位。清理未使用的开发人员席位将降低许可证续期成本。如果您愿意,您可以在有效许可期内重新购买额外的开发人员席位。请注意,由于此许可证包中有{0}个开发人员,因此无法减少此数量。",
"AdditionalInfo": "如果开发人员席位超出您的要求,您可以减少它们。 您可以发送电子邮件至 <a href=\"mailto:info@abp.io\">info@abp.io</a> 以删除您的一些开发人员席位。 清除未使用的开发人员席位将降低许可证续订成本。 如果需要,您可以在有效许可期限内重新购买额外的开发人员席位。 请注意,由于此许可证中有 {0} 个开发人员,因此您无法减少此数量。",
"LinkExpiredErrorMessage": "您正在尝试访问的链接已过期。",
"ExpirationDate": "过期时间",
"SpringCampaignDiscount": "春季促销折扣",

6
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json

@ -192,7 +192,7 @@
"ChangingDevelopers": "我將來可以更改我組織的註冊開發人員嗎?",
"ChangingDevelopersExplanation": "除了將新的開發人員添加到你的許可中之外,你還可以更改現有的開發人員(可以刪除一個開發人員並將新的開發人員添加到同一位置),而無需任何額外費用.",
"WhenShouldIRenewMyLicense": "我什麽時候應該續訂我的許可?",
"WhenShouldIRenewMyLicenseExplanation": "如果您在許可證到期後 <strong>1 個月</strong>內續訂許可證,將享受以下折扣:團隊許可證 {0} ; 商業許可證 {1} ;企業許可證 {2}. 如果您在許可證到期後 <strong>1 個月</strong>續訂許可證,續訂價格將與許可證購買價格相同,並且續訂不會有折扣。",
"WhenShouldIRenewMyLicenseExplanation": "如果您在許可證到期後 <strong>{3} 天</strong>內續訂許可證,將享受以下折扣:團隊許可證 {0} ; 商業許可證 {1} ;企業許可證 {2}. 如果您在許可證到期後 <strong>{3} 天</strong>續訂許可證,續訂價格將與許可證購買價格相同,並且續訂不會有折扣。",
"TrialPlan": "你們有試用計劃嗎?",
"DoYouAcceptBankWireTransfer": "你們接受銀行電匯嗎?",
"DoYouAcceptBankWireTransferExplanation": "是的,我們接受銀行電匯。<br />在通過銀行轉賬發送許可費後,將您的收據和所需的許可類型通過電子郵件發送至accounting@abp.io。 我們的國際銀行賬戶信息:",
@ -402,6 +402,6 @@
"WhatHappensWhenLicenseEndsExplanation4": "許可證到期後,您將無法安裝添加到 ABP 商業平台的新模塊和主題。",
"WhatHappensWhenLicenseEndsExplanation5": "您不能使用 ABP 套件。",
"WhatHappensWhenLicenseEndsExplanation6": "您無法再獲得<a href=\"{0}\">高級支持</a>。",
"WhatHappensWhenLicenseEndsExplanation7": "如果您想繼續獲得這些好處,您可以延長(更新)您的許可證。如果您在許可到期後 <strong>1 個月</strong> 內延長許可,將應用以下折扣:團隊許可 {0};營業執照{1};企業許可證{2}。"
"WhatHappensWhenLicenseEndsExplanation7": "如果您想繼續獲得這些好處,您可以延長(更新)您的許可證。如果您在許可到期後 <strong>{3} 天</strong> 內延長許可,將應用以下折扣:團隊許可 {0};營業執照{1};企業許可證{2}。"
}
}
}

2
build/build-all-release.ps1

@ -5,7 +5,7 @@
foreach ($solutionPath in $solutionPaths) {
$solutionAbsPath = (Join-Path $rootFolder $solutionPath)
Set-Location $solutionAbsPath
dotnet build --configuration Release
dotnet build --configuration Release -- /maxcpucount
if (-Not $?) {
Write-Host ("Build failed for the solution: " + $solutionPath)
Set-Location $rootFolder

297
docs/en/Community-Articles/2023-03-20-Dapper/POST.md

@ -0,0 +1,297 @@
# Using Dapper with the ABP Framework
[Dapper](https://github.com/DapperLib/Dapper) is a simple and lightweight object mapper for .NET. A key feature of Dapper is its [high performance](https://github.com/DapperLib/Dapper#performance) compared to other ORMs. In this article, I will show how to use it in your ABP projects. But, first look at when to use it.
### Source Code
You can find the [full source code of the demo application here](https://github.com/abpframework/abp-samples/tree/master/Dapper).
## When to use Dapper?
In ABP Framework, we suggest to use Dapper in a combination with Entity Framework Core (EF Core) for the following reasons:
* EF Core is much easier to use (you don't need to manually write SQL queries and work with low level database objects).
* EF Core abstracts different DBMS dialects, so it will be easier to change your DBMS later.
* EF Core's change tracking system automatically update changes in the database.
* EF Core is better compatible with Object Oriented Programming (OOP) practices and is more type safe to work with. So, EF Core code is more understandable and maintainable.
In most of your use cases, you typically work with one or a few entities and a maintainable codebase can be chosen instead of a slight performance difference. However, there may be certain places in your application where it matters:
* You may work with a lot of entities, so want to query faster (Indeed, EF Core's [AsNoTracking()](https://learn.microsoft.com/en-us/ef/core/querying/tracking) extension can help in most cases).
* You may be performing too many database operations in a single request.
* EF Core may not create an optimized SQL query and you may want to manually write it for a better performance.
For such cases, Dapper can be a good choice. You can easily write SQL queries and bind the result to your objects.
## Creating a new ABP Solution
To demonstrate the Dapper usage, I've created an ABP solution. You can find the [full source code of the demo application here](https://github.com/abpframework/abp-samples/tree/master/Dapper). If you want to create the same solution from scratch, follow the steps below:
Install the ABP CLI if you haven't installed it before:
````bash
dotnet tool install -g Volo.Abp.Cli
````
Create a new solution with the ABP Framework's non-layered startup template with MVC UI and EF Core database:
````bash
abp new DapperDemo -t app-nolayers
````
> The startup template or a different UI selection don't matter for this article. I selected these options to keep the demo solution simple.
After creating the solution, run the following command to migrate the database (run the command in the folder of the `.csproj` file:
````csharp
dotnet run --migrate-database
````
> If you've created a layered solution, then run the `DbMigrator` application inside the solution. If you have trouble by creating the solution, please refer to the [Quick Start](https://docs.abp.io/en/abp/latest/Tutorials/Todo/Single-Layer/Index) document.
## Setting Up the Entity Framework Core Part
We will use EF Core with Dapper, so we need to configure the EF Core first. I will use the following `Book` entity as an example:
````csharp
public class Book : AuditedAggregateRoot<Guid>
{
public string Name { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
}
````
If you are using a layered solution, entities are located in the `Domain` project. For my demo solution, I just placed it in the `Entities` folder of the single project:
![book-class-in-rider](book-class-in-rider.png)
Once I created the `Book` entity, I should add it to my `DbContext` class:
````csharp
public class DapperDemoDbContext : AbpDbContext<DapperDemoDbContext>
{
// 1: ADD A DBSET PROPERTY
public DbSet<Book> Books { get; set; }
public DapperDemoDbContext(DbContextOptions<DapperDemoDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
//...other code parts
// 2: MAP YOUR ENTITY TO A DATABASE TABLE
builder.Entity<Book>(b =>
{
b.ToTable("Books");
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
}
}
````
Now, we can add a new database migration:
````bash
dotnet ef migrations add Added_Book
````
And apply changes to database:
````bash
dotnet ef database update
````
At this point, you should see the Books table if you check your database:
![book-database-table](book-database-table.png)
> As you see, the `Books` table contains more fields than the `Book` entity's property count. Other properties are inherited from the `AuditedAggregateRoot` table. You could inherit from the `BasicAggregateRoot` class if you don't want these properties.
## Seeding the Database
ABP's [data seeding](https://docs.abp.io/en/abp/latest/Data-Seeding) system is a great way to add some test data to the database. The following class inserts two books to the `Books` table when I migrate the database:
````csharp
public class DapperDemoDataSeederContributor : IDataSeedContributor, ITransientDependency
{
private readonly IRepository<Book, Guid> _bookRepository;
public DapperDemoDataSeederContributor(IRepository<Book, Guid> bookRepository)
{
_bookRepository = bookRepository;
}
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() > 0)
{
return;
}
await _bookRepository.InsertAsync(
new Book
{
Name = "1984",
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
}
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
}
);
}
}
````
After creating the `DapperDemoDataSeederContributor` class, I can re-run the following command:
````bash
dotnet run --migrate-database
````
Now, I can see the records in the database:
![book-table-data](book-table-data.png)
Now, all ready to try Dapper to query from the `Books` table.
## Using Dapper Without the Integration Package
ABP provides an integration package for Dapper. However, first I want to show using Dapper without the integration package.
### Installing the Dapper Package
First, install the [Dapper](https://www.nuget.org/packages/Dapper) package to your project. You can use a command-line terminal, locate the root path of your project (`.csproj` file that you want to install it) and run the following command:
````bash
dotnet add package Dapper
````
> If your application is layered, then we suggest to add the `Dapper` package to your `EntityFrameworkCore` integration project in your solution.
### Executing a Dapper Query
I will query from the `Books` table, but I don't want to use the `Book` entity to map the result (because I don't need to all properties). So, I am creating a new class for the query result:
````csharp
public class BookDataView
{
public Guid Id { get; set; }
public string Name { get; set; }
public float Price { get; set; }
}
````
Now, we can use Dapper's `QueryAsync` extension method as shown below:
````csharp
public class DemoService : ITransientDependency
{
private readonly IRepository<Book, Guid> _bookRepository;
public DemoService(IRepository<Book, Guid> bookRepository)
{
_bookRepository = bookRepository;
}
[UnitOfWork]
public virtual async Task<List<BookDataView>> GetListAsync()
{
var database = (await _bookRepository.GetDbContextAsync()).Database;
var dbConnection = database.GetDbConnection();
var dbTransaction = database.CurrentTransaction?.GetDbTransaction();
var queryResult = await dbConnection.QueryAsync<BookDataView>(
"SELECT Id, Name, Price FROM Books",
transaction: dbTransaction
);
return queryResult.ToList();
}
}
````
Let's examine that class:
* I am injecting ABP's standard [generic repository](https://docs.abp.io/en/abp/latest/Repositories) service in to the `DemoService` constructor.
* `_bookRepository.GetDbContextAsync()` returns the underlying `DbContext` object of EF Core. We need to have the [Volo.Abp.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore) package reference to be able to access that method. If you have created a single-layer solution, the reference does already exists. If you have created a layered solution you may need to manually add this package to the project that contains the `DemoService` class. Because the layered solution isolates the EF Core dependency from rest of the solution.
* Dapper needs a `DbConnection` and a `DbTransaction` object (as optional) to execute a query. We are getting them over the `database` object obtained from the `DbContext`. We suggest to always pass the current `DbTransaction` object while working with Dapper. Because, if there is a database transaction on the same database connection that you execute queries, and you don't pass the transaction object, you get an exception.
* Finally, we can use Dapper's `QueryAsync` extension method to execute the database query.
* Notice that the `GetListAsync` method is made as `virtual` and marked with `UnitOfWork` attribute to enable the [Unit Of Work](https://docs.abp.io/en/abp/latest/Unit-Of-Work) for that method. It ensures the database connection is available in the body of the `GetListAsync` method.
That's all. You can execute any Dapper operation using the `DbConnection` and `DbTransaction` objects obtained from the `_bookRepository` object. Please refer to Dapper's documentation for other operations.
> We've obtained the `DbContext` object from a repository. However, a repository is not required to obtain a `DbContext`. Instead, you could inject the `IDbContextProvider<T>` service (`IDbContextProvider<DapperDemoDbContext>` for this demo) and call its `GetDbContextAsync` method.
## Using the Volo.Abp.Dapper Package
In the previous section, you saw that you don't need to an ABP integration package to be able to use Dapper in your ABP applications. However, there is an integration package here: [Volo.Abp.Dapper](https://www.nuget.org/packages/Volo.Abp.Dapper). Actually, that package doesn't contain much services. It just provides a convenient base class to create Dapper based repository classes.
### Installing the Volo.Abp.Dapper Package
You can use [ABP CLI](https://docs.abp.io/en/abp/latest/CLI#add-package) to easily install ABP packages to your projects. Execute the following command in the folder of `.csproj` you want to install the package:
````
abp add-package Volo.Abp.Dapper
````
> You can check the [ABP Dapper document](https://docs.abp.io/en/abp/latest/Dapper) for alternative installation options.
### Creating a Repository Class
In the `DemoService` example, we have used database objects out of a [repository](https://docs.abp.io/en/abp/latest/Repositories) class. If you want to implement layering your solution and abstracting database operations, it can be better to create a repository class to execute Dapper operations.
Here, a repository class that executes the same database query:
````csharp
public class BookRepository : DapperRepository<DapperDemoDbContext>, ITransientDependency
{
public BookRepository(IDbContextProvider<DapperDemoDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<List<BookDataView>> GetListAsync()
{
var connection = await GetDbConnectionAsync();
var queryResult = await connection.QueryAsync<BookDataView>(
"SELECT Id, Name, Price FROM Books",
transaction: await GetDbTransactionAsync()
);
return queryResult.ToList();
}
}
````
Let's examine that class:
* It inherits from the `DapperRepository` class, which provides useful methods and properties for database operations. It also implements the `IUnitOfWorkEnabled` interface, so ABP makes the database connection (and transaction if requested) available in the method body by implementing dynamic proxies (a.k.a. interception).
* `GetListAsync` method made `virtual`. That's needed to make the interception process working. It wouldn't be needed if we introduce `IBookRepository` for that class and always use it by injecting the `BookRepository` class (in this case, it will use interface proxying - however, this is too much details for the purpose of this article).
* We are using the `GetDbConnectionAsync` and `GetDbTransactionAsync` methods to obtain the current database connection and transaction (that is managed by ABP's [unit of work](https://docs.abp.io/en/abp/latest/Unit-Of-Work) system).
You can then inject the `BookRepository` class when you want to get a list of `BookDataView` whenever it is needed. In the demo project, I used it inside the `IndexModel.cshtml.cs` to show a list of books on the page:
![list-of-books](list-of-books.png)
## Conclusion
In this article, I've explained ABP's Dapper integration and demonstrated how you can execute Dapper operations in your applications. I suggest to use Dapper when it is really needed and adds any significant value (generally a performance gain) to your application. Otherwise, EF Core is much more convenient for most of the database operations and you will have a more maintainable codebase using EF Core.
## Source Code
You can find the [full source code of the demo application here](https://github.com/abpframework/abp-samples/tree/master/Dapper).
## See Also
* [ABP Dapper documentation](https://docs.abp.io/en/abp/latest/Dapper)

BIN
docs/en/Community-Articles/2023-03-20-Dapper/book-class-in-rider.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
docs/en/Community-Articles/2023-03-20-Dapper/book-database-table.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
docs/en/Community-Articles/2023-03-20-Dapper/book-table-data.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
docs/en/Community-Articles/2023-03-20-Dapper/list-of-books.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

372
docs/en/Community-Articles/2023-03-28-Converting-Create-Edit-Modal-To-Page/POST.md

@ -0,0 +1,372 @@
# Convert Create/Edit Modals to Page
In this document we will explain how to convert BookStore's Books create & edit modals to regular blazor pages.
## Before
![bookstore-crud-before](images/old.gif)
## After
![bookstore-crud-after](images/new.gif)
# Books.razor Page
Books.razor page is the main page of the books management. Create & Update operations are done in this page. So we'll remove create & update operations from this page and move a separate blazor component for each operation. Each component will be a page.
- Remove both Create & Update modals.
![remove-all-modals](images/books-remove-modals.png)
- Replace **NewBook** button with a link to **CreateBook** page.
```html
<Button Color="Color.Primary" Type="ButtonType.Link" To="books/new">
@L["NewBook"]
</Button>
```
- Inject `NavigationManager` to `Books.razor` page.
```csharp
@inject NavigationManager NavigationManager
```
- Replace **Edit** button with a link to **UpdateBook** page.
```html
<Button Color="Color.Primary" Type="ButtonType.Link" OnClick="() => NavigateToEdit(book.Id)">
@L["Edit"]
</Button>
```
```csharp
private void NavigateToEdit(Guid id)
{
NavigationManager.NavigateTo($"books/{id}/edit");
}
```
- Remove all methods in the `Books.razor` page except constructor. And add `GoToEditPage` as below:
```csharp
protected void GoToEditPage(BookDto book)
{
NavigationManager.NavigateTo($"books/{book.Id}");
}
```
![bookstore-remove-methods](images/books-remove-methods.png)
- Change Edit button to a link in the table.
```html
<EntityAction TItem="BookDto"
Text="@L["Edit"]"
Visible=HasUpdatePermission
Clicked="() => GoToEditPage(context)" />
```
# CreateBooks Page
Create new `CreateBook.razor` and `CreateBook.razor.cs` files in your project.
- `CreateBook.razor`
```html
@page "/books/new"
@attribute [Authorize(BookStorePermissions.Books.Create)]
@inherits BookStoreComponentBase
@using Acme.BookStore.Books;
@using Acme.BookStore.Localization;
@using Acme.BookStore.Permissions;
@using Microsoft.Extensions.Localization;
@using Volo.Abp.AspNetCore.Components.Web;
@inject IStringLocalizer<BookStoreResource> L
@inject AbpBlazorMessageLocalizerHelper<BookStoreResource> LH
@inject IBookAppService AppService
@inject NavigationManager NavigationManager
<Card>
<CardHeader>
<HeadContent>
<ModalTitle>@L["NewBook"]</ModalTitle>
</HeadContent>
</CardHeader>
<CardBody>
<Validations @ref="@CreateValidationsRef" Model="@NewEntity" ValidateOnLoad="false">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["Author"]</FieldLabel>
<Select TValue="Guid" @bind-SelectedValue="@NewEntity.AuthorId">
@foreach (var author in authorList)
{
<SelectItem TValue="Guid" Value="@author.Id">
@author.Name
</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["Name"]</FieldLabel>
<TextEdit @bind-Text="@NewEntity.Name">
<Feedback>
<ValidationError />
</Feedback>
</TextEdit>
</Field>
</Validation>
<Field>
<FieldLabel>@L["Type"]</FieldLabel>
<Select TValue="BookType" @bind-SelectedValue="@NewEntity.Type">
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
<SelectItem TValue="BookType" Value="@((BookType) bookTypeValue)">
@L[$"Enum:BookType.{bookTypeValue}"]
</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["PublishDate"]</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="NewEntity.PublishDate" />
</Field>
<Field>
<FieldLabel>@L["Price"]</FieldLabel>
<NumericEdit TValue="float" @bind-Value="NewEntity.Price" />
</Field>
</Validations>
</CardBody>
<CardFooter>
<Button Color="Color.Secondary" Type="ButtonType.Link" To="books">
@L["Cancel"]
</Button>
<Button Color="Color.Primary"
Type="@ButtonType.Submit"
PreventDefaultOnSubmit="true"
Clicked="CreateEntityAsync">
@L["Save"]
</Button>
</CardFooter>
</Card>
```
- `CreateBook.razor.cs`
```csharp
using Acme.BookStore.Books;
using Blazorise;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
namespace Acme.BookStore.Blazor.Pages;
public partial class CreateBook
{
protected Validations CreateValidationsRef;
protected CreateUpdateBookDto NewEntity = new();
IReadOnlyList<AuthorLookupDto> authorList = Array.Empty<AuthorLookupDto>();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
authorList = (await AppService.GetAuthorLookupAsync()).Items;
if (!authorList.Any())
{
throw new UserFriendlyException(message: L["AnAuthorIsRequiredForCreatingBook"]);
}
NewEntity.AuthorId = authorList.First().Id;
if (CreateValidationsRef != null)
{
await CreateValidationsRef.ClearAll();
}
}
protected virtual async Task CreateEntityAsync()
{
try
{
var validate = true;
if (CreateValidationsRef != null)
{
validate = await CreateValidationsRef.ValidateAll();
}
if (validate)
{
await AppService.CreateAsync(NewEntity);
NavigationManager.NavigateTo("books");
}
}
catch (Exception ex)
{
await HandleErrorAsync(ex);
}
}
}
```
# EditBooks Page
Create new `EditBook.razor` and `EditBook.razor.cs` files in your project.
- `EditBook.razor`
```html
@page "/books/{Id}"
@attribute [Authorize(BookStorePermissions.Books.Edit)]
@inherits BookStoreComponentBase
@using Acme.BookStore.Books;
@using Acme.BookStore.Localization;
@using Acme.BookStore.Permissions;
@using Microsoft.Extensions.Localization;
@using Volo.Abp.AspNetCore.Components.Web;
@inject IStringLocalizer<BookStoreResource> L
@inject AbpBlazorMessageLocalizerHelper<BookStoreResource> LH
@inject IBookAppService AppService
@inject NavigationManager NavigationManager
<Card>
<CardHeader>
<HeadContent>
<ModalTitle>@EditingEntity.Name</ModalTitle>
</HeadContent>
</CardHeader>
<CardBody>
<Validations @ref="@EditValidationsRef" Model="@EditingEntity" ValidateOnLoad="false">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["Author"]</FieldLabel>
<Select TValue="Guid" @bind-SelectedValue="@EditingEntity.AuthorId">
@foreach (var author in authorList)
{
<SelectItem TValue="Guid" Value="@author.Id">
@author.Name
</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["Name"]</FieldLabel>
<TextEdit @bind-Text="@EditingEntity.Name">
<Feedback>
<ValidationError />
</Feedback>
</TextEdit>
</Field>
</Validation>
<Field>
<FieldLabel>@L["Type"]</FieldLabel>
<Select TValue="BookType" @bind-SelectedValue="@EditingEntity.Type">
@foreach (int bookTypeValue in Enum.GetValues(typeof(BookType)))
{
<SelectItem TValue="BookType" Value="@((BookType) bookTypeValue)">
@L[$"Enum:BookType.{bookTypeValue}"]
</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["PublishDate"]</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="EditingEntity.PublishDate" />
</Field>
<Field>
<FieldLabel>@L["Price"]</FieldLabel>
<NumericEdit TValue="float" @bind-Value="EditingEntity.Price" />
</Field>
</Validations>
</CardBody>
<CardFooter>
<Button Color="Color.Secondary" Type="ButtonType.Link" To="books">
@L["Cancel"]
</Button>
<Button Color="Color.Primary"
Type="@ButtonType.Submit"
PreventDefaultOnSubmit="true"
Clicked="UpdateEntityAsync">
@L["Save"]
</Button>
</CardFooter>
</Card>
```
- `EditBook.razor.cs`
```csharp
using Acme.BookStore.Books;
using Blazorise;
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
namespace Acme.BookStore.Blazor.Pages;
public partial class EditBook
{
protected CreateUpdateBookDto EditingEntity = new();
protected Validations EditValidationsRef;
IReadOnlyList<AuthorLookupDto> authorList = Array.Empty<AuthorLookupDto>();
[Parameter]
public string Id { get; set; }
public Guid EditingEntityId { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
// Blazor can't parse Guid as route constraint currently.
// See https://github.com/dotnet/aspnetcore/issues/19008
EditingEntityId = Guid.Parse(Id);
authorList = (await AppService.GetAuthorLookupAsync()).Items;
if (!authorList.Any())
{
throw new UserFriendlyException(message: L["AnAuthorIsRequiredForCreatingBook"]);
}
var entityDto = await AppService.GetAsync(EditingEntityId);
EditingEntity = ObjectMapper.Map<BookDto,CreateUpdateBookDto>(entityDto);
if (EditValidationsRef != null)
{
await EditValidationsRef.ClearAll();
}
}
protected virtual async Task UpdateEntityAsync()
{
try
{
var validate = true;
if (EditValidationsRef != null)
{
validate = await EditValidationsRef.ValidateAll();
}
if (validate)
{
await AppService.UpdateAsync(EditingEntityId, EditingEntity);
NavigationManager.NavigateTo("books");
}
}
catch (Exception ex)
{
await HandleErrorAsync(ex);
}
}
}
```
You can check the following commit for details:
https://github.com/abpframework/abp-samples/commit/aae61ad6d66ebf6191dd4dcfb4e23d30bd680a4e

BIN
docs/en/Community-Articles/2023-03-28-Converting-Create-Edit-Modal-To-Page/images/books-remove-methods.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
docs/en/Community-Articles/2023-03-28-Converting-Create-Edit-Modal-To-Page/images/books-remove-modals.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
docs/en/Community-Articles/2023-03-28-Converting-Create-Edit-Modal-To-Page/images/new.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

BIN
docs/en/Community-Articles/2023-03-28-Converting-Create-Edit-Modal-To-Page/images/old.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

170
docs/en/UI/Angular/Card-Component.md

@ -1,8 +1,27 @@
# Card Component
The ABP Card Component is a wrapper component for the Bootstrap card class.
It supports all the features that Bootstrap card component provides.
## Usage
ABP Card Component has three main components, `CardHeader`, `CardBody` and `CardFooter`. These components have their own class and style inputs
|Component |Selector |Input Properties |
|--------- |-----------------|------------------------------------|
|CardHeader|`abp-card-header`| `cardHeaderClass`,`cardHeaderStyle`|
|CardBody |`abp-card-body` | `cardBodyClass`,`cardBodyStyle` |
|CardFooter|`abp-card-footer`| `cardFooterClass`,`cardFooterStyle`|
In addition to these components, the Card component provides directives like `CardHeader`,`CardTitle`,`CardSubtitle`,`CardImgTop`.
|Directive |Selector |
|-------------|-------------------------------------------------------------|
|CardHeader |`abp-card-header`,`[abp-card-header]`,`[abpCardHeader]` |
|CardTitle |`abp-card-title`,`[abp-card-title]`,`[abpCardTitle]` |
|CardSubtitle |`abp-card-subtitle`,`[abp-card-subtitle]`,`[abpCardSubtitle]`|
|CardImgTop |`abp-card-img-top`,`[abp-card-img-top]`,`[abpCardImgTop]` |
# Usage
ABP Card Component is a part of the `ThemeSharedModule` module. If you've imported that module into your module, you don't need to import it again. If not, first import it as shown below:
@ -24,29 +43,164 @@ export class MyFeatureModule {}
```
Then, the `abp-card` component can be used. See the example below:
```ts
Then, the `abp-card` component can be used. See the examples below:
## CardBody
```ts
// card-demo.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-card-demo',
template: `
<abp-card [cardStyle]="{width: '18rem'}">
<abp-card-body>This is some text within a card body</abp-card-body>
</abp-card>
`,
})
export class CardDemoComponent { }
```
See the card body result below:
![abp-card-body](./images/abp-card-body.png)
## Titles, Text and Links
```ts
//card-demo.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-card-demo',
template: `
<abp-card [cardStyle]="{width: '18rem'}">
<abp-card-body>
<abp-card-title>Lorem Ipsum</abp-card-title>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla commodo condimentum ligula, sed varius nibh eleifend sit amet. Maecenas facilisis vel arcu nec maximus.
<h5 abpCardTitle>Card Title</h5>
<h6 abpCardSubtitle class="mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
<a href="#" class="card-link" >Card link</a>
<a href="#" class="card-link" >Another link</a>
</abp-card-body>
</abp-card>
`,
})
export class CardDemoComponent { }
```
See the card title, text and link result below:
![abp-card-title-text-link](./images/abp-card-title-text-link.png)
## Images
```ts
//card-demo.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-card-demo',
template: `
<abp-card [cardStyle]="{width:'18rem'}">
<img abpCardImgTop src="..." alt="...">
<abp-card-body>
<p class="card-text" >Some quick example text to build on the card title and make up the bulk of the card's content.</p>
</abp-card-body>
</abp-card>
`,
})
export class CardDemoComponent { }
```
See the card image result below:
![abp-card-image-top](./images/abp-card-image.png)
## List Groups
```ts
//card-demo.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-card-demo',
template: `
<abp-card [cardStyle]="{width:'18rem'}">
<ul class="list-group list-group-flush">
<li class="list-group-item">An item</li>
<li class="list-group-item">A second item</li>
<li class="list-group-item">A third item</li>
</ul>
</abp-card>
`,
})
export class CardDemoComponent { }
```
See the group list result below:
See the result below:
![abp-card-list-group](./images/abp-card-list-group.png)
![abp-card-component](./images/abp-card-component.png)
## Kitchen Sink
```ts
//card-demo.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-card-demo',
template: `
<abp-card [cardStyle]="{width:'18rem'}">
<img abpCardImgTop src="../../assets/thinh-nguyen-aRrS37GKlVA-unsplash.jpg" alt="...">
<abp-card-body>
<h5 abpCardTitle>Card title</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
</abp-card-body>
<ul class="list-group list-group-flush">
<li class="list-group-item">An item</li>
<li class="list-group-item">A second item</li>
<li class="list-group-item">A third item</li>
</ul>
<abp-card-body>
<a href="#" class="card-link">Card link</a>
<a href="#" class="card-link">Another link</a>
</abp-card-body>
</abp-card>
`,
})
export class CardDemoComponent { }
```
See kitchen sink result below:
![abp-card-kitchen-sink](./images/abp-card-kitchen-sink.png)
## Header and Footer
```ts
//card-demo.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-card-demo',
template: `
<abp-card class="text-center">
<abp-card-header>Featured</abp-card-header>
<abp-card-body>
<h5 abpCardTitle>Special title treatment</h5>
<p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
<a class="card-link" href="#" class="btn btn-primary">Go somewhere</a>
</abp-card-body>
<abp-card-footer class="text-muted">
2 days ago
</abp-card-footer>
</abp-card>
`,
})
export class CardDemoComponent { }
```
See the header and footer result below:
As you can see in the example above, you can customize your card component's style with the `cardStyle` input. You can also add your custom classes with the `cardClass` input.
![abp-card-header-footer](./images/abp-card-header-footer.png)

56
docs/en/UI/Angular/Modifying-the-Menu.md

@ -88,14 +88,65 @@ function configureRoutes(routes: RoutesService) {
}
```
We can also define a group for navigation elements. It's an optional property
- **Note:** It'll also include groups that were defined at the modules
```js
// route.provider.ts
import { RoutesService } from '@abp/ng.core';
function configureRoutes(routes: RoutesService) {
return () => {
routes.add([
{
//etc..
group: 'ModuleName::GroupName'
},
{
path: '/your-path/child',
name: 'Your child navigation',
parentName: 'Your navigation',
requiredPolicy: 'permission key here',
order: 1,
},
]);
};
}
```
To get the route items as grouped we can use the `groupedVisible` (or Observable one `groupedVisible$`) getter methods
- It returns `RouteGroup<T>[]` if there is any group in the route tree, otherwise it returns `undefined`
```js
import { ABP, RoutesService, RouteGroup } from "@abp/ng.core";
import { Component } from "@angular/core";
@Component(/* component metadata */)
export class AppComponent {
visible: RouteGroup<ABP.Route>[] | undefined = this.routes.groupedVisible;
//Or
visible$:Observable<RouteGroup<ABP.Route>[] | undefined> = this.routes.groupedVisible$;
constructor(private routes: RoutesService) {}
}
```
...and then in app.module.ts...
- The `groupedVisible` method will return the `Others` group for ungrouped items, the default key is `AbpUi::OthersGroup`, we can change this `key` via the `OTHERS_GROUP` injection token
```js
import { NgModule } from '@angular/core';
import { OTHERS_GROUP } from '@abp/ng.core';
import { APP_ROUTE_PROVIDER } from './route.provider';
@NgModule({
providers: [APP_ROUTE_PROVIDER],
providers: [
APP_ROUTE_PROVIDER,
{
provide: OTHERS_GROUP,
useValue: 'ModuleName::MyOthersGroupKey',
},
],
// imports, declarations, and bootstrap
})
export class AppModule {}
@ -109,8 +160,9 @@ Here is what every property works as:
- `requiredPolicy` is the permission key to access the page. See the [Permission Management document](./Permission-Management.md)
- `order` is the order of the navigation element. "Administration" has an order of `100`, so keep that in mind when ordering top level menu items.
- `iconClass` is the class of the `i` tag, which is placed to the left of the navigation label.
- `layout` defines in which layout the route will be loaded. (default: `eLayoutType.empty`)
- `layout` defines in which layout the route is loaded. (default: `eLayoutType.empty`)
- `invisible` makes the item invisible in the menu. (default: `false`)
- `group` is an optional property that is used to group together related routes in an application. (type: `string`, default: `AbpUi::OthersGroup`)
### Via `routes` Property in `AppRoutingModule`

17
docs/en/UI/Angular/Page-Alerts.md

@ -61,4 +61,21 @@ this.service.show({
![angular-page-alert-with-params-example](./images/page-alert-with-params-example.png)
## Render HTML content
We can pass html content to the `title` and `message` parameters
* It allows static message or localization key
* [abpSafeHtml](https://github.com/abpframework/abp/blob/37b59a7f05202264505d002397dbb27d275740e1/npm/ng-packs/packages/core/src/lib/pipes/safe-html.pipe.ts#L6) pipe will sanitize html values
```typescript
this.service.show({
type: 'success',
title: `New <i><u>blog</u></i> published`,
message: 'AbpApp::HtmlMessageWithParams{0}',
messageLocalizationParams: ['admin'],
});
```
![angular-page-alert-with-html-example](./images/page-alert-with-html-example.png)

BIN
docs/en/UI/Angular/images/abp-card-body.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
docs/en/UI/Angular/images/abp-card-header-footer.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
docs/en/UI/Angular/images/abp-card-header.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/en/UI/Angular/images/abp-card-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

BIN
docs/en/UI/Angular/images/abp-card-kitchen-sink.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

BIN
docs/en/UI/Angular/images/abp-card-list-group.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
docs/en/UI/Angular/images/abp-card-title-text-link.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/en/UI/Angular/images/page-alert-with-html-example.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

223
docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md

@ -58,49 +58,49 @@ Model:
### Attributes
You can set some of the attributes on your c# property, or directly on html tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
#### Property Attributes
- `[TextArea()]`: Converts the input into a text area.
* `[Placeholder()]`: Sets placeholder for input. You can use a localization key directly.
* `[InputInfoText()]`: Sets a small info text for input. You can use a localization key directly.
* `[FormControlSize()]`: Sets size of form-control wrapper element. Available values are
* `[Placeholder()]`: Sets the description are of the input. You can use a localization key directly.
* `[InputInfoText()]`: Sets text for the input. You can directly use a localization key.
* `[FormControlSize()]`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
* `[DisabledInput]` : Input is disabled.
* `[ReadOnlyInput]`: Input is read-only.
* `[DisabledInput]` : Sets the input as disabled.
* `[ReadOnlyInput]`: Sets the input as read-only.
#### Tag Attributes
* `info`: Sets a small info text for input. You can use a localization key directly.
* `auto-focus`: If true, browser auto focuses on the element.
* `size`: Sets size of form-control wrapper element. Available values are
* `info`: Sets text for the input. You can directly use a localization key.
* `auto-focus`: It lets the browser set focus to the element when its value is true.
* `size`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
* `disabled`: Input is disabled.
* `readonly`: Input is read-only.
* `label`: Sets the label for input.
* `display-required-symbol`: Adds the required symbol (*) to label if input is required. Default `True`.
* `disabled`: Sets the input as disabled.
* `readonly`: Sets the input as read-only.
* `label`: Sets the label of input.
* `required-symbol`: Adds the required symbol `(*)` to the label when the input is required. The default value is `True`.
`asp-format`, `name` and `value` attributes of [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-input-tag-helper) are also valid for `abp-input` tag helper.
### Label & Localization
You can set label of your input in different ways:
You can set the label of the input in several ways:
- You can use `Label` attribute and directly set the label. But it doesn't auto localize your localization key. So use it as `label="@L["{LocalizationKey}"].Value"`.
- You can set it using `[Display(name="{LocalizationKey}")]` attribute of Asp.Net Core.
- You can use the `Label` attribute to set the label directly. This property does not automatically localize the text. To localize the label, use `label="@L["{LocalizationKey}"].Value"`.
- You can set it using `[Display(name="{LocalizationKey}")]` attribute of ASP.NET Core.
- You can just let **abp** find the localization key for the property. It will try to find "DisplayName:{PropertyName}" or "{PropertyName}" localization keys, if `label` or `[DisplayName]` attributes are not set.
## abp-select
`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [Asp.Net Core Select Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of Asp.Net Core is also valid for `abp-select`.
`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [ASP.NET Core Select Tag Helper](https://docs.microsoft.com/tr-tr/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of ASP.NET Core is also valid for `abp-select`.
`abp-select` tag needs a list of `Microsoft.AspNetCore.Mvc.Rendering.SelectListItem ` to work. It can be provided by `asp-items` attriube on the tag or `[SelectItems()]` attribute on c# property. (if you are using [abp-dynamic-form](Dynamic-forms.md), c# attribute is the only way.)
@ -170,14 +170,14 @@ Model:
### Attributes
You can set some of the attributes on your c# property, or directly on html tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
#### Property Attributes
* `[SelectItems()]`: Sets the select data. Parameter should be the name of the data list. (see example above)
- `[InputInfoText()]`: Sets a small info text for input. You can use a localization key directly.
- `[FormControlSize()]`: Sets size of form-control wrapper element. Available values are
- `[InputInfoText()]`: Sets text for the input. You can directly use a localization key.
- `[FormControlSize()]`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
@ -186,21 +186,21 @@ You can set some of the attributes on your c# property, or directly on html tag.
#### Tag Attributes
- `asp-items`: Sets the select data. This Should be a list of SelectListItem.
- `info`: Sets a small info text for input. You can use a localization key directly.
- `size`: Sets size of form-control wrapper element. Available values are
- `info`: Sets text for the input. You can directly use a localization key.
- `size`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
- `label`: Sets the label for input.
- `display-required-symbol`: Adds the required symbol (*) to label if input is required. Default `True`.
- `label`: Sets the label of input.
- `required-symbol`: Adds the required symbol `(*)` to the label when the input is required. The default value is `True`.
### Label & Localization
You can set label of your input in different ways:
You can set the label of the input in several ways:
- You can use `Label` attribute and directly set the label. But it doesn't auto localize your localization key. So use it as `label="@L["{LocalizationKey}"].Value".`
- You can set it using `[Display(name="{LocalizationKey}")]` attribute of Asp.Net Core.
- You can set it using `[Display(name="{LocalizationKey}")]` attribute of ASP.NET Core.
- You can just let **abp** find the localization key for the property. It will try to find "DisplayName:{PropertyName}" or "{PropertyName}" localization keys.
Localizations of combobox values are set by `abp-select` for **Enum** property. It searches for "{EnumTypeName}.{EnumPropertyName}" or "{EnumPropertyName}" localization keys. For instance, in the example above, it will use "CarType.StationWagon" or "StationWagon" keys for localization when it localizes combobox values.
@ -251,7 +251,7 @@ Model:
### Attributes
You can set some of the attributes on your c# property, or directly on html tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
#### Property Attributes
@ -261,3 +261,172 @@ You can set some of the attributes on your c# property, or directly on html tag.
- `asp-items`: Sets the select data. This Should be a list of SelectListItem.
- `Inline`: If true, radio buttons will be in single line, next to each other. If false, they will be under each other.
## abp-date-picker & abp-date-range-picker
`abp-date-picker` and `abp-date-range-picker` tags creates a Bootstrap form date picker for a given c# property. `abp-date-picker` is for single date selection, `abp-date-range-picker` is for date range selection. It uses [datepicker](https://www.daterangepicker.com/) jQuery plugin.
Usage:
````xml
<abp-date-picker asp-for="@Model.MyModel.MyDate" />
<abp-date-range-picker asp-for-start="@Model.MyModel.MyDateRangeStart" asp-for-end="@Model.MyModel.MyDateRangeEnd" />
<abp-dynamic-form abp-model="DynamicFormExample"></abp-dynamic-form>
````
Model:
````csharp
public class FormElementsModel : PageModel
{
public SampleModel MyModel { get; set; }
public DynamicForm DynamicFormExample { get; set; }
public void OnGet()
{
MyModel = new SampleModel();
DynamicFormExample = new DynamicForm();
}
public class SampleModel
{
public DateTime MyDate { get; set; }
public DateTime MyDateRangeStart { get; set; }
public DateTime MyDateRangeEnd { get; set; }
}
public class DynamicForm
{
[DateRangePicker("MyPicker",true)]
public DateTime StartDate { get; set; }
[DateRangePicker("MyPicker",false)]
[DatePickerOptions(nameof(DatePickerOptions))]
public DateTime EndDate { get; set; }
public DateTime DateTime { get; set; }
public DynamicForm()
{
StartDate = DateTime.Now;
EndDate = DateTime.Now;
DateTime = DateTime.Now;
}
}
public AbpDatePickerOptions DatePickerOptions { get; set; }
}
````
### Attributes
You can set some of the attributes on your c# property, or directly on HTML tag. If you are going to use this property in a [abp-dynamic-form](Dynamic-forms.md), then you can only set these properties via property attributes.
#### Property Attributes
* `[Placeholder()]`: Sets the description are of the input. You can use a localization key directly.
* `[InputInfoText()]`: Sets text for the input. You can directly use a localization key.
* `[FormControlSize()]`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
* `[DisabledInput]` : Sets the input as disabled.
* `[ReadOnlyInput]`: Sets the input as read-only.
- `[DatePickerOptions()]`: Sets the predefined options of the date picker. Parameter should be the name of the options property (see example above). See the available [datepicker options](https://www.daterangepicker.com/#options). You can use a localization key directly.
##### abp-date-picker
`[DatePicker]` : Sets the input as datepicker. Especially for string properties.
##### abp-date-range-picker
`[DateRangePicker()]` : Sets the picker id for the date range picker. You can set the property as a start date by setting IsStart=true or leave it as default/false to set it as an end date.
#### Tag Attributes
* `info`: Sets text for the input. You can directly use a localization key.
* `auto-focus`: It lets the browser set focus to the element when its value is true.
* `size`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
* `disabled`: Sets the input as disabled.
* `readonly`: Sets the input as read-only.
* `label`: Sets the label of input.
* `required-symbol`: Adds the required symbol `(*)` to the label when the input is required. The default value is `True`.
* `open-button`: A button to open the datepicker will be added when its `True`. The default value is `True`.
* `clear-button`: A button to clear the datepicker will be added when its `True`. The default value is `True`.
* `single-open-and-clear-button`: Shows the open and clear buttons in a single button when it's `True`. The default value is `True`.
* `is-utc`: Converts the date to UTC when its `True`. The default value is `False`.
* `is-iso`: Converts the date to ISO format when its `True`. The default value is `False`.
* `date-format`: Sets the date format of the input. The default format is the user's culture date format. You need to provide a JavaScript date format convention. Eg: `YYYY-MM-DDTHH:MM:SSZ`.
* `date-separator`: Sets a character to separate start and end dates. The default value is `-`
* Other non-mapped attributes will be automatically added to the input element as is. See the available [datepicker options](https://www.daterangepicker.com/#options). Eg: `data-start-date="2020-01-01"`
##### abp-date-picker
* `asp-date`: Sets the date value. This should be a `DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?` or `string` value.
##### abp-date-range-picker
* `asp-for-start`: Sets the start date value. This should be a `DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?` or `string` value.
* `asp-for-end`: Sets the end date value. This should be a `DateTime`, `DateTime?`, `DateTimeOffset`, `DateTimeOffset?` or `string` value.
### Label & Localization
You can set the label of the input in several ways:
- You can use the `Label` attribute to set the label directly. This property does not automatically localize the text. To localize the label, use `label="@L["{LocalizationKey}"].Value"`.
- You can set it using `[Display(name="{LocalizationKey}")]` attribute ASP.NET Core.
- You can just let **abp** find the localization key for the property. If the `label` or `[DisplayName]` attributes are not set, it tries to find the localization by convention. For example `DisplayName:{YourPropertyName}` or `{YourPropertyName}`. If your property name is FullName, it will search for `DisplayName:FullName` or `{FullName}`.
### JavaScript Usage
````javascript
var newPicker = abp.libs.bootstrapDateRangePicker.createDateRangePicker(
{
label: "New Picker",
}
);
newPicker.insertAfter($('body'));
````
````javascript
var newPicker = abp.libs.bootstrapDateRangePicker.createSinglePicker(
{
label: "New Picker",
}
);
newPicker.insertAfter($('body'));
````
#### Options
* `label`: Sets the label of the input.
* `placeholder`: Sets the placeholder of the input.
* `value`: Sets the value of the input.
* `name`: Sets the name of the input.
* `id`: Sets the id of the input.
* `required`: Sets the input as required.
* `disabled`: Sets the input as disabled.
* `readonly`: Sets the input as read-only.
* `size`: Sets the size of the form-control wrapper element. Available values are
- `AbpFormControlSize.Default`
- `AbpFormControlSize.Small`
- `AbpFormControlSize.Medium`
- `AbpFormControlSize.Large`
* `openButton`: A button to open the datepicker will be added when its `True`. The default value is `True`.
* `clearButton`: A button to clear the datepicker will be added when its `True`. The default value is `True`.
* `singleOpenAndClearButton`: Shows the open and clear buttons in a single button when it's `True`. The default value is `True`.
* `isUtc`: Converts the date to UTC when its `True`. The default value is `False`.
* `isIso`: Converts the date to ISO format when its `True`. The default value is `False`.
* `dateFormat`: Sets the date format of the input. The default format is the user's culture date format. You need to provide a JavaScript date format convention. Eg: `YYYY-MM-DDTHH:MM:SSZ`.
* `dateSeparator`: Sets a character to separate start and end dates. The default value is `-`.
* `startDateName`: Sets the name of the hidden start date input.
* `endDateName`: Sets the name of the hidden end date input.
* `dateName`: Sets the name of the hidden date input.
* Other [datepicker options](https://www.daterangepicker.com/#options). Eg: `startDate: "2020-01-01"`.

58
framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyOptions.cs

@ -3,7 +3,10 @@ using System.Globalization;
using System.Net;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.MultiTenancy;
@ -27,28 +30,55 @@ public class AbpAspNetCoreMultiTenancyOptions
TenantKey = TenantResolverConsts.DefaultTenantKey;
MultiTenancyMiddlewareErrorPageBuilder = async (context, exception) =>
{
// Try to delete the tenant's cookie if it does not exist or is inactive.
var isCookieAuthentication = false;
var tenantResolveResult = context.RequestServices.GetRequiredService<ITenantResolveResultAccessor>().Result;
if (tenantResolveResult != null &&
tenantResolveResult.AppliedResolvers.Contains(CookieTenantResolveContributor.ContributorName))
if (tenantResolveResult != null)
{
if (tenantResolveResult.AppliedResolvers.Count == 1 && tenantResolveResult.AppliedResolvers.Contains(CurrentUserTenantResolveContributor.ContributorName))
{
var authenticationType = context.User.Identity?.AuthenticationType;
if (authenticationType != null)
{
var scheme = await context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>().GetHandlerAsync(context, authenticationType);
if (scheme is CookieAuthenticationHandler cookieAuthenticationHandler)
{
// Try to delete the authentication's cookie if it does not exist or is inactive.
await cookieAuthenticationHandler.SignOutAsync(null);
isCookieAuthentication = true;
}
}
}
var options = context.RequestServices.GetRequiredService<IOptions<AbpAspNetCoreMultiTenancyOptions>>().Value;
AbpMultiTenancyCookieHelper.SetTenantCookie(context, null, options.TenantKey);
if (tenantResolveResult.AppliedResolvers.Contains(CookieTenantResolveContributor.ContributorName) ||
context.Request.Cookies.ContainsKey(options.TenantKey))
{
// Try to delete the tenant's cookie if it does not exist or is inactive.
AbpMultiTenancyCookieHelper.SetTenantCookie(context, null, options.TenantKey);
}
}
context.Response.Headers.Add("Abp-Tenant-Resolve-Error", exception.Message);
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
context.Response.ContentType = "text/html";
if (isCookieAuthentication && context.Request.Method.Equals("Get", StringComparison.OrdinalIgnoreCase) && !context.Request.IsAjax())
{
context.Response.Headers.Add("Abp-Tenant-Resolve-Error", exception.Message);
context.Response.Redirect(context.Request.GetEncodedUrl());
}
else
{
context.Response.Headers.Add("Abp-Tenant-Resolve-Error", exception.Message);
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
context.Response.ContentType = "text/html";
var message = exception.Message;
var details = exception is BusinessException businessException ? businessException.Details : string.Empty;
var message = exception.Message;
var details = exception is BusinessException businessException ? businessException.Details : string.Empty;
await context.Response.WriteAsync($"<html lang=\"{HtmlEncoder.Default.Encode(CultureInfo.CurrentCulture.Name)}\"><body>\r\n");
await context.Response.WriteAsync($"<h3>{HtmlEncoder.Default.Encode(message)}</h3>{HtmlEncoder.Default.Encode(details)}<br>\r\n");
await context.Response.WriteAsync("</body></html>\r\n");
await context.Response.WriteAsync($"<html lang=\"{HtmlEncoder.Default.Encode(CultureInfo.CurrentCulture.Name)}\"><body>\r\n");
await context.Response.WriteAsync($"<h3>{HtmlEncoder.Default.Encode(message)}</h3>{HtmlEncoder.Default.Encode(details)}<br>\r\n");
await context.Response.WriteAsync("</body></html>\r\n");
// Note the 500 spaces are to work around an IE 'feature'
await context.Response.WriteAsync(new string(' ', 500));
// Note the 500 spaces are to work around an IE 'feature'
await context.Response.WriteAsync(new string(' ', 500));
}
return true;
};

8
framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/MultiTenancyMiddleware.cs

@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.RequestLocalization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
@ -15,6 +17,8 @@ namespace Volo.Abp.AspNetCore.MultiTenancy;
public class MultiTenancyMiddleware : IMiddleware, ITransientDependency
{
public ILogger<MultiTenancyMiddleware> Logger { get; set; }
private readonly ITenantConfigurationProvider _tenantConfigurationProvider;
private readonly ICurrentTenant _currentTenant;
private readonly AbpAspNetCoreMultiTenancyOptions _options;
@ -26,6 +30,8 @@ public class MultiTenancyMiddleware : IMiddleware, ITransientDependency
IOptions<AbpAspNetCoreMultiTenancyOptions> options,
ITenantResolveResultAccessor tenantResolveResultAccessor)
{
Logger = NullLogger<MultiTenancyMiddleware>.Instance;
_tenantConfigurationProvider = tenantConfigurationProvider;
_currentTenant = currentTenant;
_tenantResolveResultAccessor = tenantResolveResultAccessor;
@ -41,6 +47,8 @@ public class MultiTenancyMiddleware : IMiddleware, ITransientDependency
}
catch (Exception e)
{
Logger.LogException(e);
if (await _options.MultiTenancyMiddlewareErrorPageBuilder(context, e))
{
return;

82
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs

@ -14,6 +14,7 @@ using Microsoft.Extensions.Localization;
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.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
@ -144,6 +145,10 @@ public class AbpDynamicFormTagHelperService : AbpTagHelperService<AbpDynamicForm
{
await ProcessSelectGroupAsync(context, output, model);
}
else if (IsDateGroup(context, model))
{
await ProcessDateGroupAsync(context, output, model);
}
else
{
await ProcessInputGroupAsync(context, output, model);
@ -151,6 +156,64 @@ public class AbpDynamicFormTagHelperService : AbpTagHelperService<AbpDynamicForm
}
}
private async Task ProcessDateGroupAsync(TagHelperContext context, TagHelperOutput output, ModelExpression model)
{
var abpDateTagHelper = GetDateGroupTagHelper(context, output, model);
await abpDateTagHelper.RenderAsync(new TagHelperAttributeList(), context, _htmlEncoder, "div", TagMode.StartTagAndEndTag);
}
private AbpTagHelper GetDateGroupTagHelper(TagHelperContext context, TagHelperOutput output, ModelExpression model)
{
return IsDateRangeGroup(model.ModelExplorer)
? GetAbpDateRangeInputTagHelper(context, output, model)
: GetAbpDateInputTagHelper(model);
}
private AbpTagHelper GetAbpDateInputTagHelper(ModelExpression model)
{
var abpDateInputTagHelper = _serviceProvider.GetRequiredService<AbpDatePickerTagHelper>();
abpDateInputTagHelper.AspFor = model;
abpDateInputTagHelper.ViewContext = TagHelper.ViewContext;
abpDateInputTagHelper.DisplayRequiredSymbol = TagHelper.RequiredSymbols ?? true;
return abpDateInputTagHelper;
}
private AbpTagHelper GetAbpDateRangeInputTagHelper(TagHelperContext context, TagHelperOutput output, ModelExpression model)
{
var modelAttribute = model.ModelExplorer.GetAttribute<DateRangePickerAttribute>();
var pickerId = modelAttribute.PickerId;
var abpDateRangeInputTagHelper = _serviceProvider.GetRequiredService<AbpDateRangePickerTagHelper>();
abpDateRangeInputTagHelper.PickerId = pickerId;
abpDateRangeInputTagHelper.ViewContext = TagHelper.ViewContext;
if (modelAttribute.IsStart)
{
abpDateRangeInputTagHelper.AspForStart = model;
var otherModelExists = TryToGetOtherDateModel(model, pickerId, out var otherModel);
if (otherModelExists && otherModel.GetAttribute<DateRangePickerAttribute>().IsEnd)
{
abpDateRangeInputTagHelper.AspForEnd = ModelExplorerToModelExpressionConverter(otherModel);
}
}
return abpDateRangeInputTagHelper;
}
private bool TryToGetOtherDateModel(ModelExpression model, string pickerId, out ModelExplorer otherModel)
{
otherModel = TagHelper.Model.ModelExplorer.Properties.SingleOrDefault(x => x != model.ModelExplorer && x.GetAttribute<DateRangePickerAttribute>()?.PickerId == pickerId);
return otherModel != null;
}
private static bool IsDateRangeGroup(ModelExplorer modelModelExplorer)
{
return modelModelExplorer.GetAttribute<DateRangePickerAttribute>() != null;
}
protected virtual void RemoveFormGroupItemsNotInModel(TagHelperContext context, TagHelperOutput output, List<FormGroupItem> items)
{
var models = GetModels(context, output);
@ -294,6 +357,25 @@ public class AbpDynamicFormTagHelperService : AbpTagHelperService<AbpDynamicForm
return IsEnum(model.ModelExplorer) || AreSelectItemsProvided(model.ModelExplorer);
}
protected virtual bool IsDateGroup(TagHelperContext context, ModelExpression model)
{
if (model.ModelExplorer.GetAttribute<DatePickerAttribute>() != null ||
model.ModelExplorer.GetAttribute<DateRangePickerAttribute>() != null)
{
return true;
}
if (model.Metadata.ModelType == typeof(DateTime) ||
model.Metadata.ModelType == typeof(DateTime?) ||
model.Metadata.ModelType == typeof(DateTimeOffset) ||
model.Metadata.ModelType == typeof(DateTimeOffset?))
{
return true;
}
return false;
}
protected virtual bool IsEnum(ModelExplorer explorer)
{
return explorer.Metadata.IsEnum;

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs

@ -294,8 +294,10 @@ public class AbpInputTagHelperService : AbpTagHelperService<AbpInputTagHelper>
{
return "";
}
var isHaveRequiredAttribute = context.AllAttributes.Any(a => a.Name == "required");
return TagHelper.AspFor.ModelExplorer.GetAttribute<RequiredAttribute>() != null ? "<span> * </span>" : "";
return TagHelper.AspFor.ModelExplorer.GetAttribute<RequiredAttribute>() != null || isHaveRequiredAttribute ? "<span> * </span>" : "";
}
protected virtual string GetInfoAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag, bool isCheckbox)

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs

@ -197,8 +197,10 @@ public class AbpSelectTagHelperService : AbpTagHelperService<AbpSelectTagHelper>
{
return "";
}
var isHaveRequiredAttribute = context.AllAttributes.Any(a => a.Name == "required");
return TagHelper.AspFor.ModelExplorer.GetAttribute<RequiredAttribute>() != null ? "<span> * </span>" : "";
return TagHelper.AspFor.ModelExplorer.GetAttribute<RequiredAttribute>() != null || isHaveRequiredAttribute ? "<span> * </span>" : "";
}
protected virtual void AddInfoTextId(TagHelperOutput inputTagHelperOutput)

215
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelper.cs

@ -0,0 +1,215 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public abstract class
AbpDatePickerBaseTagHelper<TTagHelper> : AbpTagHelper<TTagHelper, AbpDatePickerBaseTagHelperService<TTagHelper>>, IAbpDatePickerOptions
where TTagHelper : AbpDatePickerBaseTagHelper<TTagHelper>
{
private readonly IAbpDatePickerOptions _abpDatePickerOptionsImplementation;
public string Label { get; set; }
public string LabelTooltip { get; set; }
public string LabelTooltipIcon { get; set; } = "bi-info-circle";
public string LabelTooltipPlacement { get; set; } = "right";
public bool LabelTooltipHtml { get; set; } = false;
[HtmlAttributeName("info")]
public string InfoText { get; set; }
[HtmlAttributeName("disabled")]
public bool IsDisabled { get; set; } = false;
[HtmlAttributeName("readonly")]
public bool? IsReadonly { get; set; } = false;
public bool AutoFocus { get; set; }
public AbpFormControlSize Size { get; set; } = AbpFormControlSize.Default;
[HtmlAttributeName("required-symbol")]
public bool DisplayRequiredSymbol { get; set; } = true;
public string Name { get; set; }
public string Value { get; set; }
public bool SuppressLabel { get; set; }
protected AbpDatePickerBaseTagHelper(AbpDatePickerBaseTagHelperService<TTagHelper> service) : base(service)
{
_abpDatePickerOptionsImplementation = new AbpDatePickerOptions();
}
public string PickerId {
get => _abpDatePickerOptionsImplementation.PickerId;
set => _abpDatePickerOptionsImplementation.PickerId = value;
}
public DateTime? MinDate {
get => _abpDatePickerOptionsImplementation.MinDate;
set => _abpDatePickerOptionsImplementation.MinDate = value;
}
public DateTime? MaxDate {
get => _abpDatePickerOptionsImplementation.MaxDate;
set => _abpDatePickerOptionsImplementation.MaxDate = value;
}
public object MaxSpan {
get => _abpDatePickerOptionsImplementation.MaxSpan;
set => _abpDatePickerOptionsImplementation.MaxSpan = value;
}
public bool? ShowDropdowns {
get => _abpDatePickerOptionsImplementation.ShowDropdowns;
set => _abpDatePickerOptionsImplementation.ShowDropdowns = value;
}
public int? MinYear {
get => _abpDatePickerOptionsImplementation.MinYear;
set => _abpDatePickerOptionsImplementation.MinYear = value;
}
public int? MaxYear {
get => _abpDatePickerOptionsImplementation.MaxYear;
set => _abpDatePickerOptionsImplementation.MaxYear = value;
}
public AbpDatePickerWeekNumbers WeekNumbers {
get => _abpDatePickerOptionsImplementation.WeekNumbers;
set => _abpDatePickerOptionsImplementation.WeekNumbers = value;
}
public bool? TimePicker {
get => _abpDatePickerOptionsImplementation.TimePicker;
set => _abpDatePickerOptionsImplementation.TimePicker = value;
}
public int? TimePickerIncrement {
get => _abpDatePickerOptionsImplementation.TimePickerIncrement;
set => _abpDatePickerOptionsImplementation.TimePickerIncrement = value;
}
public bool? TimePicker24Hour {
get => _abpDatePickerOptionsImplementation.TimePicker24Hour;
set => _abpDatePickerOptionsImplementation.TimePicker24Hour = value;
}
public bool? TimePickerSeconds {
get => _abpDatePickerOptionsImplementation.TimePickerSeconds;
set => _abpDatePickerOptionsImplementation.TimePickerSeconds = value;
}
public List<AbpDatePickerRange> Ranges {
get => _abpDatePickerOptionsImplementation.Ranges;
set => _abpDatePickerOptionsImplementation.Ranges = value;
}
public bool? ShowCustomRangeLabel {
get => _abpDatePickerOptionsImplementation.ShowCustomRangeLabel;
set => _abpDatePickerOptionsImplementation.ShowCustomRangeLabel = value;
}
public bool? AlwaysShowCalendars {
get => _abpDatePickerOptionsImplementation.AlwaysShowCalendars;
set => _abpDatePickerOptionsImplementation.AlwaysShowCalendars = value;
}
public AbpDatePickerOpens Opens {
get => _abpDatePickerOptionsImplementation.Opens;
set => _abpDatePickerOptionsImplementation.Opens = value;
}
public AbpDatePickerDrops Drops {
get => _abpDatePickerOptionsImplementation.Drops;
set => _abpDatePickerOptionsImplementation.Drops = value;
}
public string ButtonClasses {
get => _abpDatePickerOptionsImplementation.ButtonClasses;
set => _abpDatePickerOptionsImplementation.ButtonClasses = value;
}
public string TodayButtonClasses {
get => _abpDatePickerOptionsImplementation.TodayButtonClasses;
set => _abpDatePickerOptionsImplementation.TodayButtonClasses = value;
}
public string ApplyButtonClasses {
get => _abpDatePickerOptionsImplementation.ApplyButtonClasses;
set => _abpDatePickerOptionsImplementation.ApplyButtonClasses = value;
}
public string ClearButtonClasses {
get => _abpDatePickerOptionsImplementation.ClearButtonClasses;
set => _abpDatePickerOptionsImplementation.ClearButtonClasses = value;
}
public object Locale {
get => _abpDatePickerOptionsImplementation.Locale;
set => _abpDatePickerOptionsImplementation.Locale = value;
}
public bool? AutoApply {
get => _abpDatePickerOptionsImplementation.AutoApply;
set => _abpDatePickerOptionsImplementation.AutoApply = value;
}
public bool? LinkedCalendars {
get => _abpDatePickerOptionsImplementation.LinkedCalendars;
set => _abpDatePickerOptionsImplementation.LinkedCalendars = value;
}
public bool? AutoUpdateInput {
get => _abpDatePickerOptionsImplementation.AutoUpdateInput;
set => _abpDatePickerOptionsImplementation.AutoUpdateInput = value;
}
public string ParentEl {
get => _abpDatePickerOptionsImplementation.ParentEl;
set => _abpDatePickerOptionsImplementation.ParentEl = value;
}
public string DateFormat {
get => _abpDatePickerOptionsImplementation.DateFormat;
set => _abpDatePickerOptionsImplementation.DateFormat = value;
}
public bool OpenButton {
get => _abpDatePickerOptionsImplementation.OpenButton;
set => _abpDatePickerOptionsImplementation.OpenButton = value;
}
public bool ClearButton {
get => _abpDatePickerOptionsImplementation.ClearButton;
set => _abpDatePickerOptionsImplementation.ClearButton = value;
}
public bool SingleOpenAndClearButton {
get => _abpDatePickerOptionsImplementation.SingleOpenAndClearButton;
set => _abpDatePickerOptionsImplementation.SingleOpenAndClearButton = value;
}
public bool? IsUtc {
get => _abpDatePickerOptionsImplementation.IsUtc;
set => _abpDatePickerOptionsImplementation.IsUtc = value;
}
public bool? IsIso {
get => _abpDatePickerOptionsImplementation.IsIso;
set => _abpDatePickerOptionsImplementation.IsIso = value;
}
public object Options {
get => _abpDatePickerOptionsImplementation.Options;
set => _abpDatePickerOptionsImplementation.Options = value;
}
}

712
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs

@ -0,0 +1,712 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Localization.Resources.AbpUi;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
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;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public abstract class AbpDatePickerBaseTagHelperService<TTagHelper> : AbpTagHelperService<TTagHelper>
where TTagHelper : AbpDatePickerBaseTagHelper<TTagHelper>
{
protected readonly Dictionary<Type,Func<object,string>> SupportedInputTypes = new() {
{typeof(string), o => DateTime.Parse((string)o).ToString("O")},
{typeof(DateTime), o => ((DateTime) o).ToString("O")},
{typeof(DateTime?), o => ((DateTime?) o)?.ToString("O")},
{typeof(DateTimeOffset), o => ((DateTimeOffset) o).ToString("O")},
{typeof(DateTimeOffset?), o => ((DateTimeOffset?) o)?.ToString("O")}
};
protected readonly IJsonSerializer JsonSerializer;
protected readonly IHtmlGenerator Generator;
protected readonly HtmlEncoder Encoder;
protected readonly IServiceProvider ServiceProvider;
protected readonly IAbpTagHelperLocalizer TagHelperLocalizer;
protected virtual string TagName { get; set; } = "abp-date-picker";
protected IStringLocalizer<AbpUiResource> L { get; }
protected InputTagHelper InputTagHelper { get; set; }
protected abstract TagHelperOutput TagHelperOutput { get; set; }
protected AbpDatePickerBaseTagHelperService(IJsonSerializer jsonSerializer, IHtmlGenerator generator,
HtmlEncoder encoder, IServiceProvider serviceProvider, IStringLocalizer<AbpUiResource> l,
IAbpTagHelperLocalizer tagHelperLocalizer)
{
JsonSerializer = jsonSerializer;
Generator = generator;
Encoder = encoder;
ServiceProvider = serviceProvider;
L = l;
TagHelperLocalizer = tagHelperLocalizer;
InputTagHelper = new InputTagHelper(Generator) { InputTypeName = "text" };
}
protected virtual T GetAttribute<T>() where T : Attribute
{
return GetAttributeAndModelExpression<T>(out _);
}
protected abstract T GetAttributeAndModelExpression<T>(out ModelExpression modelExpression) where T : Attribute;
public async override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
TagHelperOutput = new TagHelperOutput("input", GetInputAttributes(context, output), (_, _) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
InputTagHelper.ViewContext = TagHelper.ViewContext;
if (!TagHelper.Name.IsNullOrEmpty())
{
InputTagHelper.Name = TagHelper.Name;
}
if (!TagHelper.Value.IsNullOrEmpty())
{
InputTagHelper.Value = TagHelper.Value;
}
AddDisabledAttribute(TagHelperOutput);
AddAutoFocusAttribute(TagHelperOutput);
AddFormControls(context, output, TagHelperOutput);
AddReadOnlyAttribute(TagHelperOutput);
AddPlaceholderAttribute(TagHelperOutput);
AddInfoTextId(TagHelperOutput);
// Open and close button
var openButtonContent = TagHelper.OpenButton
? await ProcessButtonAndGetContentAsync(context, output, "calendar", "open")
: "";
var clearButtonContent = TagHelper.ClearButton
? await ProcessButtonAndGetContentAsync(context, output, "times", "clear", visible:!TagHelper.SingleOpenAndClearButton)
: "";
var labelContent = await GetLabelAsHtmlAsync(context, output, TagHelperOutput);
var infoContent = GetInfoAsHtml(context, output, TagHelperOutput);
var validationContent = await GetValidationAsHtmlAsync(context, output);
var inputGroup = new TagHelperOutput("div",
new TagHelperAttributeList(new[] { new TagHelperAttribute("class", "input-group") }),
(_, _) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
inputGroup.Content.AppendHtml(
TagHelperOutput.Render(Encoder) + openButtonContent + clearButtonContent
);
var abpDatePickerTag = new TagHelperOutput(TagName, GetBaseTagAttributes(context, output, TagHelper),
(_, _) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
abpDatePickerTag.Content.AppendHtml(inputGroup.Render(Encoder));
abpDatePickerTag.Content.AppendHtml(validationContent);
abpDatePickerTag.Content.AppendHtml(GetExtraInputHtml(context, output));
var innerHtml = labelContent + abpDatePickerTag.Render(Encoder) + infoContent;
var order = GetOrder();
AddGroupToFormGroupContents(
context,
GetPropertyName(),
SurroundInnerHtmlAndGet(context, output, innerHtml),
order
);
output.TagMode = TagMode.StartTagAndEndTag;
output.TagName = "div";
LeaveOnlyGroupAttributes(context, output);
output.Attributes.AddClass("mb-3");
output.Content.AppendHtml(innerHtml);
}
protected virtual void AddReadOnlyAttribute(TagHelperOutput inputTagHelperOutput)
{
if (inputTagHelperOutput.Attributes.ContainsName("readonly") == false &&
(TagHelper.IsReadonly != false || GetAttribute<ReadOnlyInput>() != null))
{
inputTagHelperOutput.Attributes.Add("readonly", "");
}
}
protected virtual TagHelperAttributeList GetInputAttributes(TagHelperContext context, TagHelperOutput output)
{
var groupPrefix = "group-";
var tagHelperAttributes = output.Attributes.Where(a => !a.Name.StartsWith(groupPrefix)).ToList();
var attrList = new TagHelperAttributeList();
foreach (var tagHelperAttribute in tagHelperAttributes)
{
attrList.Add(tagHelperAttribute);
}
attrList.Add("type", "text");
if (attrList.ContainsName("value"))
{
attrList.Remove(attrList.First(a => a.Name == "value"));
}
if (!TagHelper.Name.IsNullOrEmpty() && !attrList.ContainsName("name"))
{
attrList.Add("name", TagHelper.Name);
}
if (!attrList.ContainsName("autocomplete"))
{
attrList.Add("autocomplete", "off");
}
return attrList;
}
protected virtual void LeaveOnlyGroupAttributes(TagHelperContext context, TagHelperOutput output)
{
var groupPrefix = "group-";
var tagHelperAttributes = output.Attributes.Where(a => a.Name.StartsWith(groupPrefix)).ToList();
output.Attributes.Clear();
foreach (var tagHelperAttribute in tagHelperAttributes)
{
var nameWithoutPrefix = tagHelperAttribute.Name.Substring(groupPrefix.Length);
var newAttribute = new TagHelperAttribute(nameWithoutPrefix, tagHelperAttribute.Value);
output.Attributes.Add(newAttribute);
}
}
protected virtual string SurroundInnerHtmlAndGet(TagHelperContext context, TagHelperOutput output, string innerHtml)
{
return "<div class=\"mb-3\">" +
Environment.NewLine + innerHtml + Environment.NewLine +
"</div>";
}
protected abstract string GetPropertyName();
protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html,
int order)
{
var list = context.GetValue<List<FormGroupItem>>(FormGroupContents) ?? new List<FormGroupItem>();
if (!list.Any(igc => igc.HtmlContent.Contains("id=\"" + propertyName.Replace('.', '_') + "\"")))
{
list.Add(new FormGroupItem { HtmlContent = html, Order = order, PropertyName = propertyName });
}
}
protected abstract int GetOrder();
protected abstract void AddBaseTagAttributes(TagHelperAttributeList attributes);
protected virtual string GetExtraInputHtml(TagHelperContext context, TagHelperOutput output)
{
return string.Empty;
}
protected TagHelperAttributeList ConvertDatePickerOptionsToAttributeList(IAbpDatePickerOptions options)
{
var attrList = new TagHelperAttributeList();
if(options == null)
{
return attrList;
}
if (options.Locale != null)
{
attrList.Add("data-locale", JsonSerializer.Serialize(options.Locale));
}
if (options.MinDate != null)
{
attrList.Add("data-min-date", options.MinDate);
}
if (options.MaxDate != null)
{
attrList.Add("data-max-date", options.MaxDate);
}
if (options.MaxSpan != null)
{
attrList.Add("data-max-span", JsonSerializer.Serialize(options.MaxSpan));
}
if (options.ShowDropdowns == false)
{
attrList.Add("data-show-dropdowns", options.ShowDropdowns.ToString().ToLowerInvariant());
}
if (options.MinYear != null)
{
attrList.Add("data-min-year", options.MinYear);
}
if (options.MaxYear != null)
{
attrList.Add("data-max-year", options.MaxYear);
}
switch (options.WeekNumbers)
{
case AbpDatePickerWeekNumbers.Normal:
attrList.Add("data-show-week-numbers", "true");
break;
case AbpDatePickerWeekNumbers.Iso:
attrList.Add("data-show-i-s-o-week-numbers", "true");
break;
}
if (options.TimePicker != null)
{
attrList.Add("data-time-picker", options.TimePicker.ToString().ToLowerInvariant());
}
if (options.TimePickerIncrement != null)
{
attrList.Add("data-time-picker-increment", options.TimePickerIncrement);
}
if (options.TimePicker24Hour != null)
{
attrList.Add("data-time-picker24-hour", options.TimePicker24Hour.ToString().ToLowerInvariant());
}
if (options.TimePickerSeconds != null)
{
attrList.Add("data-time-picker-seconds", options.TimePickerSeconds.ToString().ToLowerInvariant());
}
if (options.Opens != AbpDatePickerOpens.Center)
{
attrList.Add("data-opens", options.Opens.ToString().ToLowerInvariant());
}
if (options.Drops != AbpDatePickerDrops.Down)
{
attrList.Add("data-drops", options.Drops.ToString().ToLowerInvariant());
}
if (!options.ButtonClasses.IsNullOrEmpty())
{
attrList.Add("data-button-classes", options.ButtonClasses);
}
if (!options.ApplyButtonClasses.IsNullOrEmpty())
{
attrList.Add("data-apply-button-classes", options.ApplyButtonClasses);
}
if (!options.ClearButtonClasses.IsNullOrEmpty())
{
attrList.Add("data-clear-button-classes", options.ClearButtonClasses);
}
if (!options.TodayButtonClasses.IsNullOrEmpty())
{
attrList.Add("data-today-button-classes", options.TodayButtonClasses);
}
if (options.AutoApply != null)
{
attrList.Add("data-auto-apply", options.AutoApply.ToString().ToLowerInvariant());
}
if (options.LinkedCalendars != null)
{
attrList.Add("data-linked-calendars", options.LinkedCalendars.ToString().ToLowerInvariant());
}
if (options.AutoUpdateInput != null)
{
attrList.Add("data-auto-update-input", options.AutoUpdateInput.ToString().ToLowerInvariant());
}
if (!options.ParentEl.IsNullOrEmpty())
{
attrList.Add("data-parent-el", options.ParentEl);
}
if (!options.DateFormat.IsNullOrEmpty())
{
attrList.Add("data-date-format", options.DateFormat);
}
if(options.Ranges != null && options.Ranges.Any())
{
var ranges = options.Ranges.ToDictionary(r => r.Label, r => r.Dates);
attrList.Add("data-ranges", JsonSerializer.Serialize(ranges));
}
if(options.AlwaysShowCalendars != null)
{
attrList.Add("data-always-show-calendars", options.AlwaysShowCalendars.ToString().ToLowerInvariant());
}
if(options.ShowCustomRangeLabel == false)
{
attrList.Add("data-show-custom-range-label", options.ShowCustomRangeLabel.ToString().ToLowerInvariant());
}
if(options.Options != null)
{
attrList.Add("data-options", JsonSerializer.Serialize(options.Options));
}
if (options.IsUtc != null)
{
attrList.Add("data-is-utc", options.IsUtc.ToString().ToLowerInvariant());
}
if (options.IsIso != null)
{
attrList.Add("data-is-iso", options.IsIso.ToString().ToLowerInvariant());
}
if (!options.PickerId.IsNullOrWhiteSpace())
{
attrList.Add("id", options.PickerId);
}
if(!options.SingleOpenAndClearButton)
{
attrList.Add("data-single-open-and-clear-button", options.SingleOpenAndClearButton.ToString().ToLowerInvariant());
}
return attrList;
}
protected TagHelperAttributeList GetBaseTagAttributes(TagHelperContext context, TagHelperOutput output, IAbpDatePickerOptions options)
{
var groupPrefix = "group-";
var tagHelperAttributes = output.Attributes.Where(a => !a.Name.StartsWith(groupPrefix)).ToList();
var attrList = new TagHelperAttributeList();
foreach (var tagHelperAttribute in tagHelperAttributes)
{
attrList.Add(tagHelperAttribute);
}
if (attrList.ContainsName("type"))
{
attrList.Remove(attrList.First(a => a.Name == "type"));
}
if (attrList.ContainsName("name"))
{
attrList.Remove(attrList.First(a => a.Name == "name"));
}
if (attrList.ContainsName("id"))
{
attrList.Remove(attrList.First(a => a.Name == "id"));
}
if (attrList.ContainsName("value"))
{
attrList.Remove(attrList.First(a => a.Name == "value"));
}
foreach (var attr in ConvertDatePickerOptionsToAttributeList(options))
{
attrList.Add(attr);
}
var optionsAttribute = GetAttributeAndModelExpression<DatePickerOptionsAttribute>(out var modelExpression);
if (optionsAttribute != null)
{
foreach (var attr in ConvertDatePickerOptionsToAttributeList(optionsAttribute.GetDatePickerOptions(modelExpression.ModelExplorer)))
{
attrList.Add(attr);
}
}
AddBaseTagAttributes(attrList);
return attrList;
}
protected virtual bool IsOutputHidden(TagHelperOutput inputTag)
{
return inputTag.Attributes.Any(a =>
a.Name.ToLowerInvariant() == "type" && a.Value?.ToString()?.ToLowerInvariant() == "hidden");
}
protected virtual string GetInfoAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag)
{
if (IsOutputHidden(inputTag))
{
return string.Empty;
}
string text;
ModelExplorer modelExplorer = null;
if (!string.IsNullOrEmpty(TagHelper.InfoText))
{
text = TagHelper.InfoText;
}
else
{
var infoAttribute = GetAttributeAndModelExpression<InputInfoText>(out var modelExpression);
if (infoAttribute != null)
{
modelExplorer = modelExpression.ModelExplorer;
text = infoAttribute.Text;
}
else
{
return string.Empty;
}
}
var idAttr = inputTag.Attributes.FirstOrDefault(a => a.Name == "id");
modelExplorer ??= GetModelExpression().ModelExplorer;
var localizedText = TagHelperLocalizer.GetLocalizedText(text, modelExplorer);
var div = new TagBuilder("div");
div.Attributes.Add("id", idAttr?.Value + "InfoText");
div.AddCssClass("form-text");
div.InnerHtml.Append(localizedText);
inputTag.Attributes.Add("aria-describedby", idAttr?.Value + "InfoText");
return div.ToHtmlString();
}
protected virtual async Task<string> GetLabelAsHtmlAsync(TagHelperContext context, TagHelperOutput output,
TagHelperOutput inputTag)
{
if (IsOutputHidden(inputTag) || TagHelper.SuppressLabel)
{
return string.Empty;
}
if (string.IsNullOrEmpty(TagHelper.Label))
{
return await GetLabelAsHtmlUsingTagHelperAsync(context, output) + GetRequiredSymbol(context, output);
}
var label = new TagBuilder("label");
label.Attributes.Add("for", GetIdAttributeValue(inputTag));
label.InnerHtml.AppendHtml(TagHelper.Label);
label.AddCssClass("form-label");
if (!TagHelper.LabelTooltip.IsNullOrEmpty())
{
label.Attributes.Add("data-bs-toggle", "tooltip");
label.Attributes.Add("data-bs-placement", TagHelper.LabelTooltipPlacement);
if (TagHelper.LabelTooltipHtml)
{
label.Attributes.Add("data-bs-html", "true");
}
label.Attributes.Add("title", TagHelper.LabelTooltip);
label.InnerHtml.AppendHtml($" <i class=\"bi {TagHelper.LabelTooltipIcon}\"></i>");
}
return label.ToHtmlString();
}
protected virtual string GetIdAttributeValue(TagHelperOutput inputTag)
{
var idAttr = inputTag.Attributes.FirstOrDefault(a => a.Name == "id");
return idAttr != null ? idAttr.Value.ToString() : string.Empty;
}
protected virtual string GetRequiredSymbol(TagHelperContext context, TagHelperOutput output)
{
if (!TagHelper.DisplayRequiredSymbol)
{
return "";
}
var isHaveRequiredAttribute = context.AllAttributes.Any(a => a.Name == "required");
return GetAttribute<RequiredAttribute>() != null || isHaveRequiredAttribute ? "<span> * </span>" : "";
}
protected abstract ModelExpression GetModelExpression();
protected virtual async Task<string> GetLabelAsHtmlUsingTagHelperAsync(TagHelperContext context,
TagHelperOutput output)
{
var modelExpression = GetModelExpression();
if (modelExpression == null)
{
return string.Empty;
}
var labelTagHelper = new LabelTagHelper(Generator) {
ViewContext = TagHelper.ViewContext,
For = modelExpression
};
var attributeList = new TagHelperAttributeList();
attributeList.AddClass("form-label");
if (!TagHelper.LabelTooltip.IsNullOrEmpty())
{
attributeList.Add("data-bs-toggle", "tooltip");
attributeList.Add("data-bs-placement", TagHelper.LabelTooltipPlacement);
if (TagHelper.LabelTooltipHtml)
{
attributeList.Add("data-bs-html", "true");
}
attributeList.Add("title", TagHelper.LabelTooltip);
}
var innerOutput =
await labelTagHelper.ProcessAndGetOutputAsync(attributeList, context, "label", TagMode.StartTagAndEndTag);
if (!TagHelper.LabelTooltip.IsNullOrEmpty())
{
innerOutput.Content.AppendHtml($" <i class=\"bi {TagHelper.LabelTooltipIcon}\"></i>");
}
return innerOutput.Render(Encoder);
}
protected virtual async Task<string> ProcessButtonAndGetContentAsync(TagHelperContext context,
TagHelperOutput output, string icon, string type, bool visible = true)
{
var abpButtonTagHelper = ServiceProvider.GetRequiredService<AbpButtonTagHelper>();
var attributes =
new TagHelperAttributeList { new("type", "button"), new("tabindex", "-1"), new("data-type", type) };
abpButtonTagHelper.ButtonType = AbpButtonType.Outline_Secondary;
abpButtonTagHelper.Icon = icon;
abpButtonTagHelper.Disabled = TagHelper.IsDisabled;
if (!visible)
{
attributes.AddClass("d-none");
}
return await abpButtonTagHelper.RenderAsync(attributes, context, Encoder, "button", TagMode.StartTagAndEndTag);
}
protected virtual void AddInfoTextId(TagHelperOutput inputTagHelperOutput)
{
if (GetAttribute<InputInfoText>() == null)
{
return;
}
var idAttr = inputTagHelperOutput.Attributes.FirstOrDefault(a => a.Name == "id");
if (idAttr == null)
{
return;
}
inputTagHelperOutput.Attributes.Add("aria-describedby", GetInfoText());
}
public virtual string GetInfoText()
{
var infoAttribute = GetAttributeAndModelExpression<InputInfoText>(out var modelExpression);
if (infoAttribute != null)
{
return TagHelperLocalizer.GetLocalizedText(infoAttribute.Text, modelExpression.ModelExplorer);
}
return string.Empty;
}
protected virtual void AddPlaceholderAttribute(TagHelperOutput inputTagHelperOutput)
{
if (inputTagHelperOutput.Attributes.ContainsName("placeholder"))
{
return;
}
var attribute = GetAttributeAndModelExpression<Placeholder>(out var modelExpression);
if (attribute != null)
{
var placeholderLocalized =
TagHelperLocalizer.GetLocalizedText(attribute.Value, modelExpression.ModelExplorer);
inputTagHelperOutput.Attributes.Add("placeholder", placeholderLocalized);
}
}
protected virtual void AddFormControls(TagHelperContext context, TagHelperOutput output,
TagHelperOutput inputTagHelperOutput)
{
inputTagHelperOutput.Attributes.AddClass("form-control");
var size = GetSize(context, output);
if (!size.IsNullOrEmpty())
{
inputTagHelperOutput.Attributes.AddClass(size);
}
}
protected virtual void AddAutoFocusAttribute(TagHelperOutput inputTagHelperOutput)
{
if (TagHelper.AutoFocus && !inputTagHelperOutput.Attributes.ContainsName("data-auto-focus"))
{
inputTagHelperOutput.Attributes.Add("data-auto-focus", "true");
}
}
protected virtual void AddDisabledAttribute(TagHelperOutput inputTagHelperOutput)
{
if (inputTagHelperOutput.Attributes.ContainsName("disabled") == false &&
(TagHelper.IsDisabled || GetAttribute<DisabledInput>() != null))
{
inputTagHelperOutput.Attributes.Add("disabled", "");
}
}
protected virtual string GetSize(TagHelperContext context, TagHelperOutput output)
{
// TODO: Test this method
var attribute = GetAttribute<FormControlSize>();
if (attribute != null)
{
TagHelper.Size = attribute.Size;
}
return TagHelper.Size switch {
AbpFormControlSize.Small => "form-control-sm",
AbpFormControlSize.Medium => "form-control-md",
AbpFormControlSize.Large => "form-control-lg",
_ => ""
};
}
protected abstract Task<string> GetValidationAsHtmlAsync(TagHelperContext context, TagHelperOutput output);
protected virtual async Task<string> GetValidationAsHtmlByInputAsync(TagHelperContext context,
TagHelperOutput output,
[NotNull]InputTagHelper inputTag)
{
var validationMessageTagHelper =
new ValidationMessageTagHelper(Generator) { For = inputTag.For, ViewContext = TagHelper.ViewContext };
var attributeList = new TagHelperAttributeList { { "class", "text-danger col-auto" } };
return await validationMessageTagHelper.RenderAsync(attributeList, context, Encoder, "span",
TagMode.StartTagAndEndTag);
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerDrops.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public enum AbpDatePickerDrops : byte
{
Down = 1,
Up = 2,
Auto = 3
}

10
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerOpens.cs

@ -0,0 +1,10 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public enum AbpDatePickerOpens : byte
{
Left = 1,
Right = 2,
Center = 3,
}

41
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerOptions.cs

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public class AbpDatePickerOptions : IAbpDatePickerOptions
{
public string PickerId { get; set; }
public DateTime? MinDate { get; set; }
public DateTime? MaxDate { get; set; }
public object MaxSpan { get; set; }
public bool? ShowDropdowns { get; set; }
public int? MinYear { get; set; }
public int? MaxYear { get; set; }
public AbpDatePickerWeekNumbers WeekNumbers { get; set; } = AbpDatePickerWeekNumbers.None;
public bool? TimePicker { get; set; }
public int? TimePickerIncrement { get; set; }
public bool? TimePicker24Hour { get; set; }
public bool? TimePickerSeconds { get; set; }
public List<AbpDatePickerRange> Ranges { get; set; }
public bool? ShowCustomRangeLabel { get; set; }
public bool? AlwaysShowCalendars { get; set; }
public AbpDatePickerOpens Opens { get; set; } = AbpDatePickerOpens.Center;
public AbpDatePickerDrops Drops { get; set; } = AbpDatePickerDrops.Down;
public string ButtonClasses { get; set; }
public string TodayButtonClasses { get; set; }
public string ApplyButtonClasses { get; set; }
public string ClearButtonClasses { get; set; }
public object Locale { get; set; }
public bool? AutoApply { get; set; }
public bool? LinkedCalendars { get; set; }
public bool? AutoUpdateInput { get; set; }
public string ParentEl { get; set; }
public string DateFormat { get; set; }
public bool OpenButton { get; set; } = true;
public bool ClearButton { get; set; } = true;
public bool SingleOpenAndClearButton { get; set; } = true;
public bool? IsUtc { get; set; }
public bool? IsIso { get; set; }
public object Options { get; set; }
}

90
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerRange.cs

@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public class AbpDatePickerRange
{
private readonly List<string> _dates = new List<string>();
public string Label { get; set; }
public IReadOnlyList<string> Dates => _dates;
public AbpDatePickerRange()
{
}
public AbpDatePickerRange(string label, DateTime start, DateTime end)
{
Label = label;
AddDate(start);
AddDate(end);
}
public AbpDatePickerRange(string label, DateTime date)
{
Label = label;
AddDate(date);
}
public AbpDatePickerRange(string label, DateTimeOffset start, DateTimeOffset end)
{
Label = label;
AddDate(start);
AddDate(end);
}
public AbpDatePickerRange(string label, DateTimeOffset date)
{
Label = label;
AddDate(date);
}
public AbpDatePickerRange(string label, string start, string end)
{
Label = label;
AddDate(start);
AddDate(end);
}
public AbpDatePickerRange(string label, string date)
{
Label = label;
AddDate(date);
}
public void AddDate(string date)
{
_dates.Add(DateTime.Parse(date).ToString("O"));
}
public void AddDate(DateTime date)
{
_dates.Add(date.ToString("O"));
}
public void AddDate(DateTimeOffset date)
{
_dates.Add(date.ToString("O"));
}
public void AddDate(DateTime? date)
{
if (date.HasValue)
{
_dates.Add(date.Value.ToString("O"));
}
}
public void AddDate(DateTimeOffset? date)
{
if (date.HasValue)
{
_dates.Add(date.Value.ToString("O"));
}
}
public void AddDate(string date, string format)
{
_dates.Add(DateTime.ParseExact(date, format, null).ToString("O"));
}
}

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelper.cs

@ -0,0 +1,16 @@
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
[HtmlTargetElement("abp-date-picker", TagStructure = TagStructure.NormalOrSelfClosing)]
public class AbpDatePickerTagHelper : AbpDatePickerBaseTagHelper<AbpDatePickerTagHelper>
{
[CanBeNull]
public ModelExpression AspFor { get; set; }
public AbpDatePickerTagHelper(AbpDatePickerTagHelperService service) : base(service)
{
}
}

88
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelperService.cs

@ -0,0 +1,88 @@
using System;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Localization.Resources.AbpUi;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions;
using Volo.Abp.Json;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public class AbpDatePickerTagHelperService : AbpDatePickerBaseTagHelperService<AbpDatePickerTagHelper>
{
public AbpDatePickerTagHelperService(IJsonSerializer jsonSerializer, IHtmlGenerator generator, HtmlEncoder encoder, IServiceProvider serviceProvider, IStringLocalizer<AbpUiResource> l, IAbpTagHelperLocalizer tagHelperLocalizer) : base(jsonSerializer, generator, encoder, serviceProvider, l, tagHelperLocalizer)
{
}
protected override TagHelperOutput TagHelperOutput { get; set; }
[CanBeNull]
protected virtual InputTagHelper DateTagHelper { get; set; }
[CanBeNull]
protected virtual TagHelperOutput DateTagHelperOutput { get; set; }
protected override string GetPropertyName()
{
return TagHelper.AspFor?.Name ?? string.Empty;
}
protected override T GetAttributeAndModelExpression<T>(out ModelExpression modelExpression)
{
modelExpression = TagHelper.AspFor;
return modelExpression?.ModelExplorer.GetAttribute<T>();
}
public async override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (TagHelper.AspFor != null)
{
DateTagHelper = new InputTagHelper(Generator)
{
InputTypeName = "hidden",
ViewContext = TagHelper.ViewContext,
For = TagHelper.AspFor,
};
var attributes = new TagHelperAttributeList { { "data-date", "true" }, { "type", "hidden" } };
DateTagHelperOutput = await DateTagHelper.ProcessAndGetOutputAsync(attributes, context, "input");
}
await base.ProcessAsync(context, output);
}
protected override int GetOrder()
{
return TagHelper.AspFor?.Metadata.Order ?? 0;
}
protected override void AddBaseTagAttributes(TagHelperAttributeList attributes)
{
if (TagHelper.AspFor != null &&
TagHelper.AspFor.Model != null &&
SupportedInputTypes.TryGetValue(TagHelper.AspFor.Metadata.ModelType, out var convertFunc))
{
attributes.Add("data-date", convertFunc(TagHelper.AspFor.Model));
}
}
protected override ModelExpression GetModelExpression()
{
return TagHelper.AspFor;
}
protected async override Task<string> GetValidationAsHtmlAsync(TagHelperContext context, TagHelperOutput output)
{
return DateTagHelper != null ? await GetValidationAsHtmlByInputAsync(context, output, DateTagHelper) : string.Empty;
}
protected override string GetExtraInputHtml(TagHelperContext context, TagHelperOutput output)
{
return DateTagHelperOutput?.Render(Encoder);
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerWeekNumbers.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public enum AbpDatePickerWeekNumbers : byte
{
None = 0,
Normal = 1,
Iso = 2
}

20
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDateRangePickerTagHelper.cs

@ -0,0 +1,20 @@
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
[HtmlTargetElement("abp-date-range-picker", TagStructure = TagStructure.NormalOrSelfClosing)]
public class AbpDateRangePickerTagHelper : AbpDatePickerBaseTagHelper<AbpDateRangePickerTagHelper>
{
[CanBeNull]
public ModelExpression AspForStart { get; set; }
[CanBeNull]
public ModelExpression AspForEnd { get; set; }
public AbpDateRangePickerTagHelper(AbpDateRangePickerTagHelperService tagHelperService) :
base(tagHelperService)
{
}
}

132
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDateRangePickerTagHelperService.cs

@ -0,0 +1,132 @@
using System;
using System.Linq;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Localization.Resources.AbpUi;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions;
using Volo.Abp.Json;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public class AbpDateRangePickerTagHelperService : AbpDatePickerBaseTagHelperService<AbpDateRangePickerTagHelper>
{
public AbpDateRangePickerTagHelperService(IJsonSerializer jsonSerializer, IHtmlGenerator generator,
HtmlEncoder encoder, IServiceProvider serviceProvider, IStringLocalizer<AbpUiResource> l,
IAbpTagHelperLocalizer tagHelperLocalizer) :
base(jsonSerializer, generator, encoder, serviceProvider, l,
tagHelperLocalizer)
{
}
protected override string TagName { get; set; } = "abp-date-range-picker";
protected override T GetAttributeAndModelExpression<T>(out ModelExpression modelExpression)
{
modelExpression = new[] { TagHelper.AspForStart, TagHelper.AspForEnd }.FirstOrDefault(x => x?.ModelExplorer?.GetAttribute<T>() != null);
return modelExpression?.ModelExplorer.GetAttribute<T>();
}
public async override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (TagHelper.AspForStart != null)
{
var startDateAttributes = new TagHelperAttributeList { { "data-start-date", "true" }, { "type", "hidden" } };
StartDateTagHelper = new InputTagHelper(Generator)
{
ViewContext = TagHelper.ViewContext,
For = TagHelper.AspForStart,
InputTypeName = "hidden"
};
StartDateTagHelperOutput = await StartDateTagHelper.ProcessAndGetOutputAsync(startDateAttributes, context, "input");
}
if (TagHelper.AspForEnd != null)
{
var endDateAttributes = new TagHelperAttributeList { { "data-end-date", "true" }, { "type", "hidden" } };
EndDateTagHelper = new InputTagHelper(Generator)
{
ViewContext = TagHelper.ViewContext,
For = TagHelper.AspForEnd,
InputTypeName = "hidden"
};
EndDateTagHelperOutput = await EndDateTagHelper.ProcessAndGetOutputAsync(endDateAttributes, context, "input");
}
await base.ProcessAsync(context, output);
}
protected override TagHelperOutput TagHelperOutput { get; set; }
[CanBeNull]
protected virtual InputTagHelper StartDateTagHelper { get; set; }
[CanBeNull]
protected virtual TagHelperOutput StartDateTagHelperOutput { get; set; }
[CanBeNull]
protected virtual InputTagHelper EndDateTagHelper { get; set; }
[CanBeNull]
protected virtual TagHelperOutput EndDateTagHelperOutput { get; set; }
protected override string GetPropertyName()
{
return TagHelper.AspForStart?.Name ?? string.Empty;
}
protected override int GetOrder()
{
return TagHelper.Order;
}
protected override void AddBaseTagAttributes(TagHelperAttributeList attributes)
{
if (TagHelper.AspForStart != null &&
TagHelper.AspForStart.Model != null &&
SupportedInputTypes.TryGetValue(TagHelper.AspForStart.Metadata.ModelType, out var convertFuncStart))
{
attributes.Add("data-start-date", convertFuncStart(TagHelper.AspForStart.Model));
}
if (TagHelper.AspForEnd != null &&
TagHelper.AspForEnd.Model != null &&
SupportedInputTypes.TryGetValue(TagHelper.AspForEnd.Metadata.ModelType, out var convertFuncEnd))
{
attributes.Add("data-end-date", convertFuncEnd(TagHelper.AspForEnd.Model));
}
}
protected override string GetExtraInputHtml(TagHelperContext context, TagHelperOutput output)
{
return StartDateTagHelperOutput?.Render(Encoder) + EndDateTagHelperOutput?.Render(Encoder);
}
protected override ModelExpression GetModelExpression()
{
return TagHelper.AspForStart;
}
protected async override Task<string> GetValidationAsHtmlAsync(TagHelperContext context, TagHelperOutput output)
{
var validationHtml = string.Empty;
if (StartDateTagHelper != null)
{
validationHtml += await GetValidationAsHtmlByInputAsync(context, output, StartDateTagHelper);
}
if (EndDateTagHelper != null)
{
validationHtml += await GetValidationAsHtmlByInputAsync(context, output, EndDateTagHelper);
}
return validationHtml;
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/DatePickerAttribute.cs

@ -0,0 +1,8 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
[AttributeUsage(AttributeTargets.Property)]
public class DatePickerAttribute : Attribute
{
}

34
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/DatePickerOptionsAttribute.cs

@ -0,0 +1,34 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
[AttributeUsage(AttributeTargets.Property)]
public class DatePickerOptionsAttribute : Attribute
{
public string DatePickerOptionsPropertyName { get; set; }
public DatePickerOptionsAttribute(string datePickerOptionsPropertyName)
{
DatePickerOptionsPropertyName = datePickerOptionsPropertyName;
}
public IAbpDatePickerOptions GetDatePickerOptions(ModelExplorer explorer)
{
var properties = explorer.Container.Properties.Where(p => p.Metadata.PropertyName != null && p.Metadata.PropertyName.Equals(DatePickerOptionsPropertyName)).ToList();
while (properties.Count == 0)
{
explorer = explorer.Container;
if(explorer.Container == null)
{
return null;
}
properties = explorer.Container.Properties.Where(p => p.Metadata.PropertyName != null && p.Metadata.PropertyName.Equals(DatePickerOptionsPropertyName)).ToList();
}
return properties.FirstOrDefault(p=> p.Model is IAbpDatePickerOptions)?.Model as IAbpDatePickerOptions;
}
}

19
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/DateRangePickerAttribute.cs

@ -0,0 +1,19 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
[AttributeUsage(AttributeTargets.Property)]
public class DateRangePickerAttribute : Attribute
{
public string PickerId { get; set; }
public bool IsStart { get; set; }
public bool IsEnd => !IsStart;
public DateRangePickerAttribute(string pickerId, bool isStart = false)
{
PickerId = pickerId;
IsStart = isStart;
}
}

160
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/IAbpDatePickerOptions.cs

@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form.DatePicker;
public interface IAbpDatePickerOptions
{
public string PickerId { get; set; }
/// <summary>
/// Min date allowed
/// </summary>
DateTime? MinDate { get; set; }
/// <summary>
/// Max date allowed
/// </summary>
DateTime? MaxDate { get; set; }
/// <summary>
/// The maximum span between the selected start and end dates. Can have any property you can add to a moment object (i.e. days, months)
/// </summary>
object MaxSpan { get; set; }
/// <summary>
/// Show year and month select boxes above calendars to jump to a specific month and year.
/// </summary>
bool? ShowDropdowns { get; set; }
/// <summary>
/// The minimum year shown in the dropdowns when showDropdowns is set to true.
/// </summary>
int? MinYear { get; set; }
/// <summary>
/// The maximum year shown in the dropdowns when showDropdowns is set to true.
/// </summary>
int? MaxYear { get; set; }
/// <summary>
/// Show week numbers at the start of each week on the calendars.
/// </summary>
AbpDatePickerWeekNumbers WeekNumbers { get; set; }
/// <summary>
/// Adds select boxes to choose times in addition to dates.
/// </summary>
bool? TimePicker { get; set; }
/// <summary>
/// Increment of the minutes selection list for times (i.e. 30 to allow only selection of times ending in 0 or 30).
/// </summary>
int? TimePickerIncrement { get; set; }
/// <summary>
/// Use 24-hour instead of 12-hour times, removing the AM/PM selection.
/// </summary>
bool? TimePicker24Hour { get; set; }
/// <summary>
/// Show seconds in the timePicker.
/// </summary>
bool? TimePickerSeconds { get; set; }
/// <summary>
/// Set predefined date ranges the user can select from. Each key is the label for the range, and its value an array with two dates representing the bounds of the range.
/// </summary>
List<AbpDatePickerRange> Ranges { get; set; }
/// <summary>
/// Displays "Custom Range" at the end of the list of predefined ranges, when the ranges option is used. This option will be highlighted whenever the current date range selection does not match one of the predefined ranges. Clicking it will display the calendars to select a new range.
/// </summary>
bool? ShowCustomRangeLabel { get; set; }
/// <summary>
/// Normally, if you use the ranges option to specify pre-defined date ranges, calendars for choosing a custom date range are not shown until the user clicks "Custom Range". When this option is set to true, the calendars for choosing a custom date range are always shown instead.
/// </summary>
bool? AlwaysShowCalendars { get; set; }
/// <summary>
/// Whether the picker appears aligned to the left, to the right, or centered under the HTML element it's attached to.
/// </summary>
AbpDatePickerOpens Opens { get; set; }
/// <summary>
/// Whether the picker appears below (default) or above the HTML element it's attached to.
/// </summary>
AbpDatePickerDrops Drops { get; set; }
/// <summary>
/// CSS class names that will be added to both the today, clear, and apply buttons.
/// </summary>
[CanBeNull]
string ButtonClasses { get; set; }
/// <summary>
/// CSS class names that will be added only to the today button.
/// </summary>
[CanBeNull]
string TodayButtonClasses { get; set; }
/// <summary>
/// CSS class names that will be added only to the apply button.
/// </summary>
[CanBeNull]
string ApplyButtonClasses { get; set; }
/// <summary>
/// CSS class names that will be added only to the clear button.
/// </summary>
[CanBeNull]
string ClearButtonClasses { get; set; }
/// <summary>
/// Allows you to provide localized strings for buttons and labels, customize the date format, and change the first day of week for the calendars.
/// </summary>
[CanBeNull]
object Locale { get; set; }
/// <summary>
/// Hide the apply button, and automatically apply a new date range as soon as two dates are clicked.
/// </summary>
bool? AutoApply { get; set; }
/// <summary>
/// When enabled, the two calendars displayed will always be for two sequential months (i.e. January and February), and both will be advanced when clicking the left or right arrows above the calendars. When disabled, the two calendars can be individually advanced and display any month/year.
/// </summary>
bool? LinkedCalendars { get; set; }
/// <summary>
/// Indicates whether the date range picker should automatically update the value of the input element it's attached to at initialization and when the selected dates change.
/// </summary>
bool? AutoUpdateInput { get; set; }
/// <summary>
/// jQuery selector of the parent element that the date range picker will be added to, if not provided this will be 'body'
/// </summary>
[CanBeNull]
string ParentEl { get; set; }
[CanBeNull]
string DateFormat { get; set; }
bool OpenButton { get; set; }
bool ClearButton { get; set; }
bool SingleOpenAndClearButton { get; set; }
bool? IsUtc { get; set; }
bool? IsIso { get; set; }
/// <summary>
/// Other non-mapped attributes will be automatically added to the input element as is.
/// </summary>
[CanBeNull]
object Options { get; set; }
}

562
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js

@ -193,6 +193,567 @@
});
}
abp.libs.bootstrapDateRangePicker = {
packageName: "bootstrap-daterangepicker",
createDateRangePicker: function (options) {
options = options || {};
options.singleDatePicker = false;
return this.createDatePicker(options);
},
createSinglePicker: function (options) {
options = options || {};
options.singleDatePicker = true;
return this.createDatePicker(options);
},
createDatePicker: function (options) {
var $container = $('<div class="mb-3"></div>')
var label = $('<label class="form-label"></label>')
if (options.label) {
label.text(options.label)
}
$container.append(label)
var $datePicker = options.singleDatePicker ? $('<abp-date-picker></abp-date-picker>') : $('<abp-date-range-picker></abp-date-range-picker>');
$container.append($datePicker)
var $inputGroup = $('<div class="input-group"></div>');
var $dateInput = $('<input type="text" autocomplete="off" class="form-control" />');
if (options.placeholder) {
$dateInput.attr('placeholder', options.placeholder)
}
if (options.value) {
$dateInput.val(options.value)
}
if (options.name) {
$dateInput.attr('name', options.name)
}
if (options.id) {
$dateInput.attr('id', options.id)
}
if (options.required) {
$dateInput.attr('required', true)
}
if (options.disabled) {
$dateInput.attr('disabled', true)
}
if (options.readonly) {
$dateInput.attr('readonly', true)
}
if(options.placeholder) {
$dateInput.attr('placeholder', options.placeholder)
}
if (options.size) {
switch (options.size) {
case 'Small':
$dateInput.addClass('form-control-sm')
break;
case 'Medium':
$dateInput.addClass('form-control-md')
break;
case 'Large':
$dateInput.addClass('form-control-lg')
break;
default:
break;
}
}
$inputGroup.append($dateInput);
if (options.openButton !== false) {
var $openButton = $('<button type="button" class="btn btn-outline-secondary" tabindex="-1" data-type="open"><i class="fa fa-calendar"></i></button>');
$inputGroup.append($openButton);
if(options.disabled) {
$openButton.attr('disabled', 'disabled')
}
}
if (options.clearButton !== false) {
var $clearButton = $('<button type="button" class="btn btn-outline-secondary" tabindex="-1" data-type="clear"><i class="fa fa-times"></i></button>');
$inputGroup.append($clearButton);
if(options.disabled) {
$clearButton.attr('disabled', 'disabled')
}
}
$datePicker.append($inputGroup);
if (options.startDateName) {
var $hiddenStartDateElement = $('<input type="hidden" data-start-date="true" name="' + options.startDateName + '" />');
$datePicker.append($hiddenStartDateElement);
}
if (options.endDateName) {
var $hiddenEndDateElement = $('<input type="hidden" data-end-date="true" name="' + options.endDateName + '" />');
$datePicker.append($hiddenEndDateElement);
}
if (options.dateName) {
var $hiddenDateElement = $('<input type="hidden" data-date="true" name="' + options.dateName + '" />');
$datePicker.append($hiddenDateElement);
}
$datePicker.data('options', options);
abp.dom.initializers.initializeDateRangePickers($datePicker);
$container[0].datePicker = $datePicker[0];
return $container;
}
};
abp.dom.initializers.initializeDateRangePickers = function ($rootElement) {
function setOptions (options, $datePickerElement, singleDatePicker) {
options.singleDatePicker = singleDatePicker;
var defaultOptions = {
showDropdowns: true,
opens: "center",
drops: "down",
autoApply: true,
autoUpdateInput: false,
showTodayButton: true,
showClearButton: true,
minYear: parseInt(moment().subtract(100, 'year').locale('en').format('YYYY')),
maxYear: parseInt(moment().add(100, 'year').locale('en').format('YYYY')),
locale: {
direction: abp.localization.currentCulture.isRightToLeft ? 'rtl' : 'ltr',
todayLabel: abp.localization.localize('Today', 'AbpUi'),
clearLabel: abp.localization.localize('Clear', 'AbpUi'),
applyLabel: abp.localization.localize('Apply', 'AbpUi'),
},
singleOpenAndClearButton: true
};
var locale = defaultOptions.locale;
$.extend(options, defaultOptions);
$.extend(options, $datePickerElement.data());
if ($.isEmptyObject(options.locale)) {
options.locale = locale;
} else {
locale = options.locale;
}
$.extend(options, $datePickerElement.data("options"));
if ($.isEmptyObject(options.locale)) {
options.locale = locale;
}
if (options.timePicker && options.timePicker24Hour === undefined) {
options.timePicker24Hour = moment.localeData().longDateFormat('LT').indexOf('A') < 1;
}
if (options.dateFormat) {
options.locale = options.locale || {};
options.locale.format = options.dateFormat;
}
if (options.separator) {
options.locale = options.locale || {};
options.locale.separator = options.separator;
}
if (options.ranges) {
$.each(options.ranges, function (key, value) {
let start = value[0];
let end;
if (value.length > 1) {
end = value[1];
} else {
end = value[0];
}
options.ranges[key] = [getMoment(start, options), getMoment(end, options)];
});
}
if (typeof options.template !== 'string' && !(options.template instanceof $))
options.template =
'<div class="daterangepicker">' +
'<div class="ranges"></div>' +
'<div class="drp-calendar left">' +
'<div class="calendar-table"></div>' +
'<div class="calendar-time"></div>' +
'</div>' +
'<div class="drp-calendar right">' +
'<div class="calendar-table"></div>' +
'<div class="calendar-time"></div>' +
'</div>' +
'<div class="drp-buttons">' +
'<button class="applyBtn" disabled="disabled" type="button"></button> ' +
'</div>' +
'</div>';
}
function replaceOpenButton (hasDate,singleOpenAndClearButton, $openButton, $clearButton) {
var hiddenClass = 'd-none';
if (singleOpenAndClearButton) {
if (hasDate) {
$openButton.addClass(hiddenClass);
$clearButton.removeClass(hiddenClass);
$clearButton.insertAfter($openButton);
} else {
$openButton.removeClass(hiddenClass);
$clearButton.addClass(hiddenClass);
$openButton.insertAfter($clearButton);
}
}
}
function getMoment (date, options, dateFormat) {
var isUtc = options.isUtc;
dateFormat = dateFormat || options.dateFormat;
if (!date) {
return isUtc ? moment.utc() : moment();
}
if (isUtc) {
return moment.utc(date, dateFormat);
} else {
return moment(date, dateFormat);
}
}
function getStartDate(options, startDate){
startDate = startDate ? startDate : options.startDate;
startDate = startDate ? getMoment(startDate, options) : null;
if (options.singleDatePicker && !startDate) {
startDate = options.date ? getMoment(options.date, options) : null;
}
if(startDate && startDate.isValid()){
return startDate;
}
return null;
}
function getEndDate(options, endDate){
if (options.singleDatePicker) {
return null;
}
endDate = endDate ? endDate : options.endDate;
endDate = endDate ? getMoment(endDate, options) : null;
if(endDate && endDate.isValid()){
return endDate;
}
return null;
}
function getTodayButton(options, $input){
if (options.showTodayButton) {
var $todayBtn = $('<button type="button" class="btn btn-sm today-btn" data-action="today">' + options.locale.todayLabel + '</button>');
if(options.todayButtonClasses){
$todayBtn.addClass(options.todayButtonClasses);
}else{
$todayBtn.addClass('btn-default');
}
$todayBtn.on('click', function () {
var today = getMoment(undefined, options);
$input.data('daterangepicker').setStartDate(today);
$input.data('daterangepicker').setEndDate(today);
$input.data('daterangepicker').clickApply();
});
return $todayBtn;
}
return null;
}
function getClearButton(options, $input, $dateRangePicker){
if (options.showClearButton) {
var $clearBtn = $('<button type="button" class="btn btn-sm clear-btn" data-action="clear">' + options.locale.clearLabel + '</button>');
if(options.clearButtonClasses){
$clearBtn.addClass(options.clearButtonClasses);
}else{
$clearBtn.addClass('btn-default');
}
$clearBtn.on('click', function () {
$input.val('').trigger('change');
$dateRangePicker.hide();
});
return $clearBtn;
}
return null;
}
function addExtraButtons(options, $dateRangePicker, $input) {
var extraButtons = [getTodayButton(options, $input), getClearButton(options, $input, $dateRangePicker)];
if ($dateRangePicker.container.hasClass('auto-apply')) {
var $div = $('<div class="drp-buttons extra-buttons"></div>');
$div.css('display', 'block');
$.each(extraButtons, function (index, value) {
$div.prepend(value);
});
$div.insertAfter($dateRangePicker.container.find('.drp-buttons'));
} else {
$.each(extraButtons, function (index, value) {
$dateRangePicker.container.find('.drp-buttons').prepend(value);
});
}
}
function addOpenButtonClick($openButton, $dateRangePicker){
if(!$openButton){
return;
}
$dateRangePicker.outsideClick = function (e) {
var target = $(e.target);
// if the page is clicked anywhere except within the daterangerpicker/button itself then call this.hide()
if (
// ie modal dialog fix
e.type == "focusin" ||
target.closest(this.element).length ||
target.closest(this.container).length ||
target.closest('.calendar-table').length ||
target.closest($openButton).length
) {
return;
}
this.hide();
this.element.trigger('outsideClick.daterangepicker', this);
};
$openButton.on('click', function () {
$dateRangePicker.toggle();
});
}
function extendDateFormat (format,options) {
if (options.timePicker) {
if (options.timePicker24Hour) {
if (options.timePickerSeconds) {
format += " HH:mm:ss";
} else {
format += " HH:mm";
}
} else {
if (options.timePickerSeconds) {
format += ' ' + " hh:mm:ss A"
} else {
format += " hh:mm A";
}
}
}
return format;
}
function fillInput($input, startDate, endDate, options) {
if (options.singleDatePicker) {
if (startDate) {
$input.val(startDate.format(options.dateFormat));
}
} else {
if (startDate && endDate) {
$input.val(startDate.format(options.dateFormat) + options.separator + endDate.format(options.dateFormat));
}
}
}
$rootElement
.findWithSelf('abp-date-picker,abp-date-range-picker')
.each(function () {
var $this = $(this);
var $input = $this.find('.input-group input[type="text"]')
if ($input.data('daterangepicker')) {
return;
}
var $openButton = $this.find('button[data-type="open"]')
var $clearButton = $this.find('button[data-type="clear"]')
var singleDatePicker = $this.is('abp-date-picker')
var options = {};
setOptions(options, $this, singleDatePicker);
var isIso = options.isIso;
var dateFormat = options.dateFormat;
var separator = options.separator;
var defaultDateFormat = extendDateFormat("YYYY-MM-DD", options);
var singleOpenAndClearButton = options.singleOpenAndClearButton && $clearButton.length > 0 && $openButton.length > 0;
var startDate = getStartDate(options);
var endDate = getEndDate(options);
if (startDate) {
options.startDate = startDate;
}
if (endDate) {
options.endDate = endDate;
}
$input.daterangepicker(options);
var $dateRangePicker = $input.data('daterangepicker');
addExtraButtons(options, $dateRangePicker, $input);
addOpenButtonClick($openButton, $dateRangePicker);
if (!dateFormat) {
dateFormat = extendDateFormat(moment.localeData().longDateFormat('L'), options);
options.dateFormat = dateFormat;
}
if (!separator) {
separator = $dateRangePicker.locale.separator;
options.separator = separator;
}
if(options.autoUpdateInput){
fillInput($input, startDate, endDate, options);
}
$input.on('apply.daterangepicker', function (ev, picker) {
if (singleDatePicker) {
$(this).val(picker.startDate.format(dateFormat));
} else {
$(this).val(picker.startDate.format(dateFormat) + separator + picker.endDate.format(dateFormat));
}
$(this).trigger('change');
});
$input.on('cancel.daterangepicker', function (ev, picker) {
$(this).val('');
$(this).trigger('change');
});
$input.on('show.daterangepicker', function (ev, picker) {
var momentStartDate = getMoment(startDate, options);
var momentEndDate = getMoment(endDate, options);
if (momentStartDate.isValid()) {
picker.setStartDate(momentStartDate);
}
if (momentEndDate.isValid()) {
picker.setEndDate(momentEndDate);
}
});
$clearButton.on('click', function () {
$input.val('');
$input.trigger('change');
});
var firstTrigger = true;
$input.on('change', function () {
if ($(this).val() !== '') {
replaceOpenButton(true, singleOpenAndClearButton, $openButton, $clearButton);
} else {
replaceOpenButton(false, singleOpenAndClearButton, $openButton, $clearButton);
}
var dates = $(this).val().split(separator);
if (dates.length === 2) {
startDate = formatDate(getStartDate(options, dates[0]));
endDate = formatDate(getEndDate(options, dates[1]));
} else {
if (dates[0]) {
startDate = formatDate(getStartDate(options, dates[0]));
}
else {
if(!firstTrigger){
startDate = null;
}
}
if(!firstTrigger){
endDate = null;
}
}
if (!startDate) {
replaceOpenButton(false, singleOpenAndClearButton, $openButton, $clearButton);
$(this).val('');
}
if (!singleDatePicker) {
var input1 = $this.find("input[data-start-date]")
input1.val(startDate);
var input2 = $this.find("input[data-end-date]")
input2.val(endDate);
} else {
var input = $this.find("input[data-date]")
input.val(startDate);
}
if (singleDatePicker) {
$this.data('date', startDate);
$input.data('date', startDate);
} else {
$this.data('startDate', startDate);
$this.data('endDate', endDate);
$input.data('startDate', startDate);
$input.data('endDate', endDate);
}
firstTrigger = false;
});
function formatDate(date) {
if (date) {
if (isIso) {
return date.locale('en').format();
}
return date.locale('en').format(defaultDateFormat)
}
return null;
}
function getFormattedStartDate() {
if (startDate) {
return getMoment(startDate, options).format(dateFormat);
}
return null;
}
function getFormattedEndDate() {
if (endDate) {
return getMoment(endDate, options).format(dateFormat);
}
return null;
}
if (singleDatePicker) {
$this[0].getFormattedDate = getFormattedStartDate;
$input[0].getFormattedDate = getFormattedStartDate;
$dateRangePicker.getFormattedDate = getFormattedStartDate;
} else {
$dateRangePicker.getFormattedStartDate = getFormattedStartDate;
$dateRangePicker.getFormattedEndDate = getFormattedEndDate;
$this[0].getFormattedStartDate = getFormattedStartDate;
$this[0].getFormattedEndDate = getFormattedEndDate;
$input[0].getFormattedStartDate = getFormattedStartDate;
$input[0].getFormattedEndDate = getFormattedEndDate;
}
$input.trigger('change');
});
}
abp.dom.onNodeAdded(function (args) {
abp.dom.initializers.initializeToolTips(args.$el.findWithSelf('[data-toggle="tooltip"]'));
abp.dom.initializers.initializePopovers(args.$el.findWithSelf('[data-toggle="popover"]'));
@ -217,6 +778,7 @@
abp.dom.initializers.initializePopovers($('[data-toggle="popover"]'));
abp.dom.initializers.initializeTimeAgos($('.timeago'));
abp.dom.initializers.initializeDatepickers($(document));
abp.dom.initializers.initializeDateRangePickers($(document));
abp.dom.initializers.initializeForms($('form'));
abp.dom.initializers.initializeAutocompleteSelects($('.auto-complete-select'));
$('[data-auto-focus="true"]').first().findWithSelf('input,select').focus();

7
framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor

@ -34,15 +34,16 @@
{
if (column.Actions.Any())
{
var entityActionType = column.Actions.Count == 1 ? ActionType.Button : ActionType.Dropdown;
<DataGridEntityActionsColumn TItem="TItem" @ref="ActionColumns[column.Title]" Caption="@column.Title">
<DisplayTemplate>
<EntityActions TItem="TItem" EntityActionsColumn="ActionColumns[column.Title]">
<EntityActions Type="entityActionType" TItem="TItem" EntityActionsColumn="ActionColumns[column.Title]">
@foreach (var action in column.Actions)
{
if (action.ConfirmationMessage != null)
{
<EntityAction TItem="TItem"
Color="@(action.Color != null ? (Blazorise.Color) action.Color : Blazorise.Color.Primary)"
Color="@(action.Color != null ? (Blazorise.Color)action.Color : Blazorise.Color.Primary)"
Icon="@action.Icon"
Clicked="async () => await action.Clicked(context)"
ConfirmationMessage="() => action.ConfirmationMessage.Invoke(context)"
@ -55,7 +56,7 @@
{
<EntityAction TItem="TItem"
Clicked="async () => await action.Clicked(context)"
Color="@(action.Color != null ? (Blazorise.Color) action.Color : Blazorise.Color.Default)"
Color="@(action.Color != null ? (Blazorise.Color)action.Color : Blazorise.Color.Primary)"
Icon="@action.Icon"
Visible="@(action.Visible != null ? action.Visible(context) : true)"
Text="@action.Text"

12
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs

@ -4,11 +4,13 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Storage;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.DependencyInjection;
@ -73,6 +75,16 @@ public class EfCoreRepository<TDbContext, TEntity> : RepositoryBase<TEntity>, IE
{
return (await GetDbContextAsync()).Set<TEntity>();
}
protected async Task<IDbConnection> GetDbConnectionAsync()
{
return (await GetDbContextAsync()).Database.GetDbConnection();
}
protected async Task<IDbTransaction> GetDbTransactionAsync()
{
return (await GetDbContextAsync()).Database.CurrentTransaction?.GetDbTransaction();
}
protected virtual AbpEntityOptions<TEntity> AbpEntityOptions => _entityOptionsLazy.Value;

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json

@ -48,6 +48,7 @@
"Search": "بحث",
"ItemWillBeDeletedMessageWithFormat": "سيتم حذف {0}!",
"ItemWillBeDeletedMessage": "سوف يتم حذف هذا البند!",
"ManageYourAccount": "إدارة حسابك"
"ManageYourAccount": "إدارة حسابك",
"OthersGroup": "آخرون"
}
}

5
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json

@ -48,6 +48,7 @@
"Search": "Vyhledávání",
"ItemWillBeDeletedMessageWithFormat": "{0} bude smazáno!",
"ItemWillBeDeletedMessage": "Tato položka bude smazána!",
"ManageYourAccount": "Spravujte svůj účet"
"ManageYourAccount": "Spravujte svůj účet",
"OthersGroup": "Jiný"
}
}
}

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/de.json

@ -48,6 +48,7 @@
"Search": "Suche",
"ItemWillBeDeletedMessageWithFormat": "{0} wird gelöscht!",
"ItemWillBeDeletedMessage": "Dieses Element wird gelöscht!",
"ManageYourAccount": "Verwalten Sie Ihr Benutzerkonto"
"ManageYourAccount": "Verwalten Sie Ihr Benutzerkonto",
"OthersGroup":"Andere"
}
}

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/el.json

@ -48,6 +48,7 @@
"Search": "Αναζήτηση",
"ItemWillBeDeletedMessageWithFormat": "Το {0} θα διαγραφεί",
"ItemWillBeDeletedMessage": "Αυτό το στοιχείο θα διαγραφεί!",
"ManageYourAccount": "Διαχείριση Λογαριασμού"
"ManageYourAccount": "Διαχείριση Λογαριασμού",
"OthersGroup":"άλλος"
}
}

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en-GB.json

@ -48,6 +48,7 @@
"Search": "Search",
"ItemWillBeDeletedMessageWithFormat": "{0} will be deleted!",
"ItemWillBeDeletedMessage": "This item will be deleted!",
"ManageYourAccount": "Manage your account"
"ManageYourAccount": "Manage your account",
"OthersGroup": "Other"
}
}

5
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json

@ -48,6 +48,9 @@
"Search": "Search",
"ItemWillBeDeletedMessageWithFormat": "{0} will be deleted!",
"ItemWillBeDeletedMessage": "This item will be deleted!",
"ManageYourAccount": "Manage your account"
"ManageYourAccount": "Manage your account",
"OthersGroup": "Other",
"Today": "Today",
"Apply": "Apply"
}
}

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json

@ -48,6 +48,7 @@
"Search": "Buscar",
"ItemWillBeDeletedMessageWithFormat": "{0} serán borrados!",
"ItemWillBeDeletedMessage": "Este elemento será borrado",
"ManageYourAccount": "Administrar cuenta"
"ManageYourAccount": "Administrar cuenta",
"OthersGroup": "Otra"
}
}

5
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fa.json

@ -48,6 +48,7 @@
"Search": "جستجو",
"ItemWillBeDeletedMessageWithFormat": "{0} حذف خواهد شد!",
"ItemWillBeDeletedMessage": "این مورد حذف خواهد شد!",
"ManageYourAccount": "حساب خود را مدیریت کنید"
"ManageYourAccount": "حساب خود را مدیریت کنید",
"OthersGroup": "دیگر"
}
}
}

5
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fi.json

@ -48,6 +48,7 @@
"Search": "Hae",
"ItemWillBeDeletedMessageWithFormat": "{0} poistetaan!",
"ItemWillBeDeletedMessage": "Tämä kohde poistetaan!",
"ManageYourAccount": "Hallitse tiliäsi"
"ManageYourAccount": "Hallitse tiliäsi",
"OthersGroup": "Muut"
}
}
}

5
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fr.json

@ -48,6 +48,7 @@
"Search": "Recherche",
"ItemWillBeDeletedMessageWithFormat": "{0} sera supprimé!",
"ItemWillBeDeletedMessage": "Cet objet va être supprimé!",
"ManageYourAccount": "Gérer votre compte"
"ManageYourAccount": "Gérer votre compte",
"OthersGroup": "Autre"
}
}
}

5
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hi.json

@ -48,6 +48,7 @@
"Search": "खोज",
"ItemWillBeDeletedMessageWithFormat": "{0} हटा दिया जाएगा!",
"ItemWillBeDeletedMessage": "यह आइटम हटा दिया जाएगा!",
"ManageYourAccount": "अपने खाते का प्रबंधन"
"ManageYourAccount": "अपने खाते का प्रबंधन",
"OthersGroup": "अन्य"
}
}
}

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hr.json

@ -48,6 +48,7 @@
"Search": "Pretraga",
"ItemWillBeDeletedMessageWithFormat": "{0} zapis će biti obrisan!",
"ItemWillBeDeletedMessage": "Ovaj zapis će biti obrisan!",
"ManageYourAccount": "Upravljaj korisničkim računom"
"ManageYourAccount": "Upravljaj korisničkim računom",
"OthersGroup": "Drugi"
}
}

5
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hu.json

@ -48,6 +48,7 @@
"Search": "Keresés",
"ItemWillBeDeletedMessageWithFormat": "{0} törlésre kerül!",
"ItemWillBeDeletedMessage": "Ez az elem törlődik!",
"ManageYourAccount": "Kezelje fiókját"
"ManageYourAccount": "Kezelje fiókját",
"OthersGroup": "Egyéb"
}
}
}

5
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/is.json

@ -48,6 +48,7 @@
"Search": "Leita",
"ItemWillBeDeletedMessageWithFormat": "{0} verður eytt!",
"ItemWillBeDeletedMessage": "Þessum lið verður eytt!",
"ManageYourAccount": "Stillingar notandaaðgangs"
"ManageYourAccount": "Stillingar notandaaðgangs",
"OthersGroup": "Annað"
}
}
}

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/it.json

@ -48,6 +48,7 @@
"Search": "Ricerca",
"ItemWillBeDeletedMessageWithFormat": "{0} sarà eliminato!",
"ItemWillBeDeletedMessage": "Questo elemento sarà eliminato!",
"ManageYourAccount": "Gestisci il tuo account"
"ManageYourAccount": "Gestisci il tuo account",
"OthersGroup": "Altra"
}
}

5
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/nl.json

@ -48,6 +48,7 @@
"Search": "Zoeken",
"ItemWillBeDeletedMessageWithFormat": "{0} wordt verwijderd!",
"ItemWillBeDeletedMessage": "Dit item wordt verwijderd!",
"ManageYourAccount": "Beheer uw account"
"ManageYourAccount": "Beheer uw account",
"OthersGroup": "Ander"
}
}
}

5
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl-PL.json

@ -48,6 +48,7 @@
"Search": "Szukaj",
"ItemWillBeDeletedMessageWithFormat": "{0} zostanie usunięty!",
"ItemWillBeDeletedMessage": "Ten element zostanie usunięty!",
"ManageYourAccount": "Zarządzaj kontem"
"ManageYourAccount": "Zarządzaj kontem",
"OthersGroup": "Inny"
}
}
}

5
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json

@ -48,6 +48,7 @@
"Search": "Procurar",
"ItemWillBeDeletedMessageWithFormat": "{0} será excluído!",
"ItemWillBeDeletedMessage": "Este item será excluído!",
"ManageYourAccount": "Gerenciar sua conta"
"ManageYourAccount": "Gerenciar sua conta",
"OthersGroup": "Outra"
}
}
}

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ro-RO.json

@ -48,6 +48,7 @@
"Search": "Caută",
"ItemWillBeDeletedMessageWithFormat": "{0} va fi şters!",
"ItemWillBeDeletedMessage": "Acest articol va fi şters!",
"ManageYourAccount": "Administraţi-vă contul"
"ManageYourAccount": "Administraţi-vă contul",
"OthersGroup": "Alte"
}
}

5
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ru.json

@ -48,6 +48,7 @@
"Search": "поиск",
"ItemWillBeDeletedMessageWithFormat": "{0} будет удален!",
"ItemWillBeDeletedMessage": "Этот предмет будет удален!",
"ManageYourAccount": "Настройте свой аккаунт"
"ManageYourAccount": "Настройте свой аккаунт",
"OthersGroup": "Другой"
}
}
}

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

Loading…
Cancel
Save