From b8969371f04bb3a3de7adef493ae2df0a139a69c Mon Sep 17 00:00:00 2001 From: Shaun Meyer Date: Sat, 22 Dec 2018 02:21:09 +0200 Subject: [PATCH 1/6] MySQL Install --- src/Libraries/SmartStore.Core/BaseEntity.cs | 28 +- .../SmartStore.Core/Data/DataSettings.cs | 37 +- .../SmartStore.Core/Data/DbQuerySettings.cs | 7 +- .../Domain/Catalog/CatalogSettings.cs | 35 +- .../SmartStore.Core/Domain/Catalog/Product.cs | 6 +- .../Catalog/ProductVariantAttributeValue.cs | 4 +- .../Domain/Common/CommonSettings.cs | 2 +- .../Domain/Localization/LocalizedProperty.cs | 1 - .../Extensions/EnumerableExtensions.cs | 30 - .../Extensions/MiscExtensions.cs | 49 +- .../Extensions/StreamExtensions.cs | 7 +- .../IO/LockFile/LockFileManager.cs | 5 +- .../SmartStore.Core/Infrastructure/Guard.cs | 2 +- .../Infrastructure/Singleton.cs | 2 +- src/Libraries/SmartStore.Core/PagedList`T.cs | 55 +- .../Search/AcquireWriterContext.cs | 9 +- .../SmartStore.Core/Search/IIndexStore.cs | 20 +- .../SmartStore.Core/Search/ISearchBits.cs | 12 + .../SmartStore.Core/Search/ISearchEngine.cs | 6 + .../SmartStore.Core/Search/IndexInfo.cs | 23 +- .../SmartStore.Core/SmartStore.Core.csproj | 15 +- src/Libraries/SmartStore.Core/app.config | 11 +- src/Libraries/SmartStore.Core/packages.config | 4 +- .../SmartStore.Data/EfDataProviderFactory.cs | 2 + .../Mapping/Tasks/ScheduleTaskHistoryMap.cs | 2 +- .../Migrations/201403112331027_Initial.cs | 321 +- .../201502202248081_NewIndexesV22.cs | 4 +- ...06211043073_PaymentShippingRestrictions.cs | 4 +- .../201507141647299_CustomerTablePerf.cs | 4 +- .../201508142203054_CronExpressions.cs | 3 +- .../Migrations/201609201852449_log4net.cs | 4 + .../Migrations/201705281903241_MoreIndexes.cs | 12 +- ...01802081830029_ShippingMethodMultistore.cs | 26 +- .../201806231547270_ScheduleTaskHistory.cs | 3 +- .../201811061745204_IsSystemProductIndex.cs | 26 +- .../201812212031185_UpdateToMySQL.Designer.cs | 29 + .../201812212031185_UpdateToMySQL.cs | 534 + .../201812212031185_UpdateToMySQL.resx | 126 + .../Migrations/Configuration.cs | 23 + .../Migrations/MigrationsConfiguration.cs | 147 +- .../SmartStore.Data/MySQLDataProvider.cs | 46 + .../SmartStore.Data/ObjectContextBase.cs | 10 +- .../Setup/DbSeedingMigrator.cs | 11 +- .../Setup/InstallDatabaseInitializer.cs | 4 +- .../Setup/MigrateDatabaseInitializer.cs | 3 +- .../Setup/SeedData/InvariantSeedData.cs | 2 +- .../SmartStore.Data/SmartObjectContext.cs | 3 +- .../SmartStore.Data/SmartStore.Data.csproj | 37 +- .../SmartStore.Data/Sql/Indexes - Copy.sql | 170 + .../Sql/Indexes.Inverse - Copy.sql | 170 + .../Sql/Indexes.SqlServer - Copy.sql | 11 + .../Sql/Indexes.SqlServer.Inverse - Copy.sql | 11 + .../Sql/Indexes.SqlServer.Inverse.sql | 2 +- .../SmartStore.Data/Sql/Indexes.SqlServer.sql | 2 +- .../Sql/StoredProcedures - Copy.sql | 89 + .../Sql/StoredProcedures.Inverse - Copy.sql | 13 + .../SmartStore.Data/Utilities/DataMigrator.cs | 76 +- src/Libraries/SmartStore.Data/app.config | 13 +- src/Libraries/SmartStore.Data/packages.config | 4 +- .../Catalog/CategoryTreeChangeHandler.cs | 12 +- .../Catalog/CopyProductService.cs | 3 +- .../Catalog/IProductService.cs | 3 +- .../Catalog/Importer/ProductImporter.cs | 6 +- .../Catalog/PriceCalculationContext.cs | 3 +- .../Catalog/PriceCalculationService.cs | 29 +- .../Catalog/ProductService.cs | 8 +- .../Catalog/ProductTagService.cs | 74 +- .../Common/MaintenanceService.cs | 2 +- .../Customers/CustomerReportService.cs | 5 +- .../Customers/CustomerService.cs | 18 +- .../DataExchange/Export/DataExportResult.cs | 19 - .../DataExchange/Export/DataExporter.cs | 850 +- .../Deployment/FileSystemFilePublisher.cs | 2 +- .../Deployment/PublicFolderPublisher.cs | 4 +- .../Export/DynamicEntityHelper.cs | 549 +- .../Export/ExportDataSegmenter.cs | 184 +- .../DataExchange/Export/ExportExtensions.cs | 12 +- .../DataExchange/Export/ExportXmlHelper.cs | 10 +- .../DataExchange/Export/IDataExporter.cs | 4 +- .../Export/Internal/DataExporterContext.cs | 64 +- .../DataExchange/ISyncMappingService.cs | 4 +- .../DataExchange/SyncMappingService.cs | 2 +- .../Discounts/DiscountService.cs | 6 + .../Forums/ForumService.cs | 3 +- .../Localization/ILocalizedEntityService.cs | 43 +- .../Localization/LocalizationExtensions.cs | 14 +- .../Localization/LocalizedEntityService.cs | 129 +- .../Localization/LocalizedValue.cs | 7 +- .../Media/PictureService.cs | 16 +- .../Messages/MessageFactory.cs | 2 +- .../Orders/OrderReportService.cs | 143 +- .../Orders/OrderService.cs | 189 +- .../SmartStore.Services/ScopedServiceBase.cs | 13 +- .../Search/Catalog/CatalogSearchQuery.cs | 32 +- .../Search/Catalog/CatalogSearchResult.cs | 22 +- .../Search/Catalog/CatalogSearchService.cs | 2 +- .../Catalog/LinqCatalogSearchService.cs | 58 +- .../Modelling/CatalogSearchQueryFactory.cs | 16 +- .../Search/Forum/LinqForumSearchService.cs | 9 +- .../Security/AclService.cs | 10 +- .../Security/IAclService.cs | 6 +- .../Seo/IUrlRecordService.cs | 33 - .../Seo/IXmlSitemapGenerator.cs | 33 +- .../SmartStore.Services/Seo/SeoExtensions.cs | 8 +- .../Seo/Tasks/RebuildXmlSitemapTask.cs | 31 +- .../Seo/UrlRecordService.cs | 202 +- .../Seo/XmlSitemapGenerator.cs | 794 +- .../SmartStore.Services.csproj | 13 +- .../Stores/IStoreMappingService.cs | 6 +- .../Stores/StoreMappingService.cs | 4 +- .../Tasks/ScheduleTaskService.cs | 32 +- .../Tasks/TaskExecutionContext.cs | 11 +- .../SmartStore.Services/Tasks/TaskExecutor.cs | 2 +- src/Libraries/SmartStore.Services/app.config | 9 +- .../SmartStore.Services/packages.config | 4 +- .../SmartStore.AmazonPay.csproj | 26 +- .../Views/AmazonPay/Configure.cshtml | 4 +- .../AmazonPayCheckout/PaymentMethod.cshtml | 2 +- .../SmartStore.AmazonPay/packages.config | 7 +- src/Plugins/SmartStore.AmazonPay/web.config | 19 +- .../SmartStore.Clickatell/AdminMenu.cs | 2 +- .../SmartStore.Clickatell.csproj | 22 +- .../SmartStore.Clickatell/packages.config | 5 +- src/Plugins/SmartStore.Clickatell/web.config | 19 +- src/Plugins/SmartStore.DevTools/AdminMenu.cs | 4 +- .../SmartStore.DevTools.csproj | 8 + .../Views/DevTools/BackendExtension.cshtml | 2 +- src/Plugins/SmartStore.DevTools/Web.config | 13 +- .../SmartStore.DevTools/packages.config | 2 + .../SmartStore.DiscountRules.csproj | 22 +- .../SmartStore.DiscountRules/packages.config | 5 +- .../SmartStore.DiscountRules/web.config | 19 +- .../SmartStore.FacebookAuth.csproj | 22 +- .../ExternalAuthFacebook/PublicInfo.cshtml | 2 +- .../SmartStore.FacebookAuth/packages.config | 5 +- .../SmartStore.FacebookAuth/web.config | 20 +- .../SmartStore.GoogleAnalytics.csproj | 18 + .../packages.config | 3 + .../SmartStore.GoogleAnalytics/web.config | 19 +- .../AdminMenu.cs | 2 +- .../Data/Migrations/Configuration.cs | 4 +- .../SmartStore.GoogleMerchantCenter/Events.cs | 2 +- .../SmartStore.GoogleMerchantCenter.csproj | 13 +- .../packages.config | 4 +- .../web.config | 13 +- .../SmartStore.OfflinePayment.csproj | 21 +- .../SmartStore.OfflinePayment/packages.config | 5 +- .../SmartStore.OfflinePayment/web.config | 19 +- .../SmartStore.PayPal.csproj | 21 +- .../Views/PayPalPlus/Configure.cshtml | 6 +- src/Plugins/SmartStore.PayPal/packages.config | 5 +- src/Plugins/SmartStore.PayPal/web.config | 17 +- .../Data/Migrations/Configuration.cs | 4 +- .../SmartStore.Shipping.csproj | 9 + .../SmartStore.Shipping/packages.config | 2 + src/Plugins/SmartStore.Shipping/web.config | 13 +- .../Data/Migrations/Configuration.cs | 2 + .../SmartStore.ShippingByWeight.csproj | 9 + .../packages.config | 2 + .../SmartStore.ShippingByWeight/web.config | 13 +- .../Data/Migrations/Configuration.cs | 4 +- .../SmartStore.Tax/SmartStore.Tax.csproj | 13 +- src/Plugins/SmartStore.Tax/packages.config | 4 +- src/Plugins/SmartStore.Tax/web.config | 13 +- .../DependencyRegistrar.cs | 1 - .../Extensions/HtmlExtensions.cs | 29 +- .../Extensions/HtmlZoneExtensions.cs | 22 +- .../Filters/CompressAttribute.cs | 4 +- .../Filters/CookieConsentFilter.cs | 37 +- .../Filters/HandleInstallFilter.cs | 5 +- .../Localization/LocalizedRoute.cs | 9 +- .../Seo/GenericPathRoute.cs | 17 +- .../SmartStore.Web.Framework.csproj | 14 +- .../Theming/WebViewPage.cs | 41 - .../UI/Blocks/IBlockEntity.cs | 1 - .../Components/TabStrip/TabStripRenderer.cs | 23 +- .../UI/WidgetProvider.cs | 16 +- .../WebStoreContext.cs | 11 +- .../SmartStore.Web.Framework/app.config | 13 +- .../SmartStore.Web.Framework/packages.config | 4 +- .../Administration/Content/_admin.scss | 11 +- .../Administration/Content/_variables.scss | 21 +- .../Content/filemanager/js/file.js | 4 +- .../Content/filemanager/js/utils.js | 30 +- .../Administration/Content/theme.scss | 15 +- .../Controllers/CommonController.cs | 5 +- .../Controllers/ExportController.cs | 93 +- .../Controllers/LanguageController.cs | 10 +- .../Controllers/ProductController.cs | 2 +- .../Models/DataExchange/ExportPreviewModel.cs | 1 + .../Models/Orders/OrderModel.cs | 4 +- .../Models/Settings/CatalogSettingsModel.cs | 9 - .../Administration/SmartStore.Admin.csproj | 11 +- .../Views/ActivityLog/ListLogs.cshtml | 4 +- .../Views/Affiliate/Edit.cshtml | 2 +- .../Administration/Views/Blog/Edit.cshtml | 2 +- .../Administration/Views/Blog/List.cshtml | 2 +- .../Administration/Views/Campaign/Edit.cshtml | 4 +- .../Administration/Views/Category/Edit.cshtml | 4 +- .../Views/Category/_CreateOrUpdate.cshtml | 6 +- .../Views/CheckoutAttribute/Edit.cshtml | 2 +- .../Views/Common/CheckUpdate.cshtml | 4 +- .../Views/Common/Maintenance.cshtml | 6 +- .../Administration/Views/Country/Edit.cshtml | 2 +- .../Administration/Views/Currency/Edit.cshtml | 2 +- .../Administration/Views/Currency/List.cshtml | 4 +- .../Views/Currency/_CreateOrUpdate.cshtml | 4 +- .../Administration/Views/Customer/Edit.cshtml | 2 +- .../Views/Customer/Reports.cshtml | 2 +- .../Views/Customer/_CreateOrUpdate.cshtml | 4 +- .../Views/CustomerRole/Edit.cshtml | 2 +- .../Views/DeliveryTime/Edit.cshtml | 2 +- .../Views/DeliveryTime/List.cshtml | 2 +- .../Administration/Views/Discount/Edit.cshtml | 2 +- .../Views/Discount/_CreateOrUpdate.cshtml | 2 +- .../Views/EmailAccount/Edit.cshtml | 2 +- .../Administration/Views/Export/Edit.cshtml | 8 +- .../Views/Export/EditDeployment.cshtml | 2 +- .../Views/Export/Preview.cshtml | 20 +- .../Views/Export/ProfileFileDetails.cshtml | 2 +- .../Export/_CreateOrUpdate.Deployment.cshtml | 2 +- .../Views/Export/_ProfileList.cshtml | 2 +- .../Views/Export/_Tab.Deployment.cshtml | 4 +- .../Views/Forum/EditForum.cshtml | 2 +- .../Views/Forum/EditForumGroup.cshtml | 2 +- .../Administration/Views/GiftCard/Edit.cshtml | 2 +- .../Views/GiftCard/_CreateOrUpdate.cshtml | 2 +- .../Administration/Views/Home/Index.cshtml | 2 +- .../Administration/Views/Home/UaTester.cshtml | 2 +- .../Administration/Views/Import/Edit.cshtml | 6 +- .../Administration/Views/Import/List.cshtml | 2 +- .../Views/Import/_ColumnMappings.cshtml | 6 +- .../Views/Import/_Update.cshtml | 8 +- .../Administration/Views/Language/Edit.cshtml | 2 +- .../Administration/Views/Language/List.cshtml | 15 +- .../Partials/AvailableLanguages.cshtml | 2 +- .../Administration/Views/Log/List.cshtml | 4 +- .../Administration/Views/Log/View.cshtml | 2 +- .../Views/Manufacturer/Edit.cshtml | 4 +- .../Views/Manufacturer/List.cshtml | 2 +- .../Views/MessageTemplate/Edit.cshtml | 8 +- .../Views/MessageTemplate/List.cshtml | 2 +- .../Administration/Views/News/Edit.cshtml | 2 +- .../Administration/Views/News/List.cshtml | 2 +- .../Views/NewsLetterSubscription/List.cshtml | 2 +- .../Views/OnlineCustomer/List.cshtml | 2 +- .../Administration/Views/Order/Edit.cshtml | 4 +- .../Administration/Views/Order/List.cshtml | 6 +- .../Views/Order/ShipmentDetails.cshtml | 4 +- .../Order/_Edit.BillingAndShipment.cshtml | 6 +- .../Views/Order/_Edit.Info.cshtml | 6 +- .../Views/Payment/Providers.cshtml | 2 +- .../Views/Plugin/ConfigurePlugin.cshtml | 2 +- .../Views/Plugin/ConfigureProvider.cshtml | 2 +- .../Administration/Views/Plugin/List.cshtml | 8 +- .../Administration/Views/Poll/Edit.cshtml | 2 +- .../Administration/Views/Product/Edit.cshtml | 8 +- .../Administration/Views/Product/List.cshtml | 2 +- .../Views/Product/LowStockReport.cshtml | 2 +- .../Product/_CreateOrUpdate.Attributes.cshtml | 4 +- .../Product/_CreateOrUpdate.Downloads.cshtml | 2 +- .../Views/Product/_CreateOrUpdate.cshtml | 10 +- .../Views/ProductAttribute/Edit.cshtml | 2 +- .../Views/ProductReview/Edit.cshtml | 2 +- .../Views/ProductReview/List.cshtml | 2 +- .../Views/QuantityUnit/Edit.cshtml | 2 +- .../Views/QueuedEmail/Edit.cshtml | 4 +- .../Views/QueuedEmail/List.cshtml | 6 +- .../Views/RecurringPayment/Edit.cshtml | 2 +- .../Views/RecurringPayment/List.cshtml | 2 +- .../Views/ReturnRequest/Edit.cshtml | 2 +- .../Views/RoxyFileManager/Index.cshtml | 24 +- .../Views/ScheduleTask/Edit.cshtml | 8 +- .../Views/ScheduleTask/List.cshtml | 2 +- .../Views/ScheduleTask/_LastRun.cshtml | 2 +- .../ScheduleTask/_MinimalTaskWidget.cshtml | 4 +- .../Administration/Views/Setting/Blog.cshtml | 2 +- .../Views/Setting/Catalog.cshtml | 320 +- .../Views/Setting/DataExchange.cshtml | 2 +- .../Views/Setting/GeneralCommon.cshtml | 2 +- .../Administration/Views/Setting/Media.cshtml | 4 +- .../Administration/Views/Setting/Order.cshtml | 2 +- .../Views/Setting/Payment.cshtml | 2 +- .../Shared/EditorTemplates/Download.cshtml | 4 +- .../Views/Shared/Layouts/_AdminRoot.cshtml | 4 +- .../Views/Shared/Partials/Menu.cshtml | 7 +- .../Views/Shared/Partials/Navbar.cshtml | 14 +- .../Views/Shared/Partials/_Providers.cshtml | 4 +- .../Views/Shipping/EditMethod.cshtml | 2 +- .../ShoppingCart/CurrentWishlists.cshtml | 2 +- .../Views/SpecificationAttribute/Edit.cshtml | 2 +- .../Views/SpecificationAttribute/List.cshtml | 2 +- .../Administration/Views/Store/Edit.cshtml | 2 +- .../Views/Theme/Configure.cshtml | 6 +- .../Administration/Views/Theme/List.cshtml | 8 +- .../Views/Theme/PreviewTool.cshtml | 4 +- .../Administration/Views/Topic/Edit.cshtml | 2 +- .../Administration/Views/Topic/List.cshtml | 2 +- .../Views/UrlRecord/Edit.cshtml | 4 +- .../Views/UrlRecord/List.cshtml | 2 +- .../SmartStore.Web/Administration/Web.config | 13 +- .../Administration/packages.config | 4 +- .../Administration/sitemap.config | 133 +- .../Installation/installation.de.xml | 5 +- .../Installation/installation.en.xml | 5 +- .../Config/ConnectionStrings.config | 2 + .../SmartStore.Web/Content/Site.css | 18 + .../SmartStore.Web/Content/bootstrap.css | 6816 ++++++++++ .../SmartStore.Web/Content/bootstrap.min.css | 20 + .../Content/editors/summernote/globalinit.js | 14 +- .../summernote/plugins/smartstore.cssclass.js | 4 +- .../summernote/plugins/smartstore.image.js | 4 +- .../SmartStore.Web/Content/shared/_alert.scss | 2 +- .../Content/shared/_buttons.scss | 2 +- .../Content/shared/_choice.scss | 3 +- .../Content/shared/_dropdown.scss | 10 +- .../SmartStore.Web/Content/shared/_fa.scss | 4 +- .../SmartStore.Web/Content/shared/_forms.scss | 16 - .../Content/shared/_mixins.scss | 67 +- .../SmartStore.Web/Content/shared/_modal.scss | 39 +- .../SmartStore.Web/Content/shared/_nav.scss | 6 +- .../Content/shared/_offcanvas.scss | 1 + .../Content/shared/_star-rating.scss | 6 +- .../SmartStore.Web/Content/shared/_typo.scss | 4 +- .../SmartStore.Web/Content/shared/_utils.scss | 30 - .../Content/shared/_variables-shared.scss | 178 - .../Content/skinning/_photoswipe.scss | 2 +- .../Content/vendors/aos/scss/_core.scss | 5 +- .../Content/vendors/aos/scss/_easing.scss | 3 +- .../jquery.fileupload-single-ui.js | 4 +- .../vendors/font-awesome/font-awesome.css | 2 +- .../vendors/font-awesome/font-awesome.min.css | 2 +- .../Content/vendors/moment/moment.js | 8 +- .../Controllers/CatalogController.cs | 114 +- .../Controllers/CatalogHelper.MapProduct.cs | 186 +- .../Controllers/CatalogHelper.cs | 15 +- .../Controllers/CheckoutController.cs | 11 +- .../Controllers/CommonController.cs | 2 +- .../Controllers/CustomerController.cs | 22 +- .../Controllers/EntityController.cs | 10 +- .../Controllers/HomeController.cs | 24 +- .../Controllers/InstallController.cs | 155 +- .../Controllers/MediaController.cs | 39 +- .../Controllers/SearchController.cs | 36 +- .../Controllers/TestController.cs | 45 + src/Presentation/SmartStore.Web/Global.asax | 2 +- .../SmartStore.Web/Global.asax.cs | 183 +- .../Installation/InstallDataSeeder.cs | 4 +- .../Infrastructure/Routes/1_StoreRoutes.cs | 4 +- .../Infrastructure/Routes/2_DefaultRoutes.cs | 58 +- .../Migrations/Configuration.cs | 23 + .../Models/Checkout/CheckoutProgressModel.cs | 2 + .../Models/Install/InstallModel.cs | 8 +- .../SmartStore.Web/Scripts/_references.js | Bin 2526 -> 1313 bytes .../SmartStore.Web/Scripts/bootstrap.js | 2014 +++ .../SmartStore.Web/Scripts/bootstrap.min.js | 21 + .../Scripts/jquery-1.10.2.intellisense.js | 2671 ++++ .../SmartStore.Web/Scripts/jquery-1.10.2.js | 9803 +++++++++++++++ .../Scripts/jquery-1.10.2.min.js | 23 + .../Scripts/jquery-1.10.2.min.map | 1 + .../SmartStore.Web/Scripts/modernizr-2.6.2.js | 1416 +++ .../SmartStore.Web/Scripts/public.common.js | 2 +- .../Scripts/smartstore.common.js | 44 +- .../Scripts/smartstore.offcanvas.js | 5 +- .../Scripts/smartstore.parallax.js | 40 +- .../Scripts/smartstore.selectwrapper.js | 49 +- .../SmartStore.Web/SmartStore.Web.csproj | 93 +- .../Themes/Flex/Content/_footer.scss | 1 + .../Themes/Flex/Content/_layout.scss | 24 +- .../Themes/Flex/Content/_megamenu.scss | 6 +- .../Themes/Flex/Content/_product.scss | 3 +- .../Themes/Flex/Content/_search.scss | 4 +- .../Themes/Flex/Content/_shopbar.scss | 5 +- .../Themes/Flex/Content/_variables.scss | 20 +- .../Themes/Flex/Content/theme.scss | 3 +- .../SmartStore.Web/Themes/Flex/theme.config | 1 - .../Themes/FlexBlue/theme.config | 6 +- .../SmartStore.Web/Themes/Web.config | 3 +- .../Views/Boards/Partials/_ForumPost.cshtml | 2 +- .../SmartStore.Web/Views/Boards/Topic.cshtml | 6 +- ...egoryTemplate.ProductsInGridOrLines.cshtml | 2 +- .../Views/Catalog/CompareProducts.cshtml | 4 +- ...turerTemplate.ProductsInGridOrLines.cshtml | 2 +- .../Catalog/Partials/OffCanvasCompare.cshtml | 4 +- .../Views/Checkout/Completed.cshtml | 2 +- .../Common/Partials/AccountDropdown.cshtml | 12 +- .../Views/Common/Partials/Breadcrumb.cshtml | 2 +- .../Views/Common/Partials/Footer.cshtml | 14 +- .../Views/Common/Partials/Menu.cshtml | 10 +- .../Views/Customer/Orders.cshtml | 6 +- .../Customer/Partials/MyAccountMenu.cshtml | 2 +- .../Views/Entity/Partials/Picker.List.cshtml | 2 +- .../SmartStore.Web/Views/Home/Index.cshtml | 34 +- .../SmartStore.Web/Views/Install/Index.cshtml | 30 +- .../SmartStore.Web/Views/Order/Details.cshtml | 2 +- .../PrivateMessages/Partials/Inbox.cshtml | 2 +- .../PrivateMessages/Partials/SentItems.cshtml | 2 +- .../Views/PrivateMessages/View.cshtml | 2 +- .../Product/Partials/Product.Picture.cshtml | 52 +- .../Views/Product/Product.cshtml | 2 +- .../Shared/Components/FileUploader.cshtml | 28 +- .../Views/Shared/EditorTemplates/Byte.cshtml | 22 +- .../Shared/EditorTemplates/DateTime.cshtml | 2 +- .../Shared/EditorTemplates/Decimal.cshtml | 22 +- .../Shared/EditorTemplates/Double.cshtml | 22 +- .../EditorTemplates/FontAwesomeIcon.cshtml | 45 + .../Views/Shared/EditorTemplates/Html.cshtml | 2 +- .../Views/Shared/EditorTemplates/Int32.cshtml | 22 +- .../Shared/EditorTemplates/QtyInput.cshtml | 2 +- .../Views/Shared/EditorTemplates/Range.cshtml | 12 +- .../Views/Shared/EditorTemplates/Time.cshtml | 2 +- .../Views/Shared/Layouts/_Document.cshtml | 113 +- .../Views/Shared/Layouts/_Layout.cshtml | 104 +- .../Partials/Product.List.FilterSort.cshtml | 4 +- .../Partials/Product.List.Item.Buttons.cshtml | 2 +- .../Shared/Partials/Product.List.Pager.cshtml | 4 +- .../Views/Shared/_Layout.cshtml | 40 + .../ShoppingCart/Partials/CartItems.cshtml | 2 +- .../Partials/OffCanvasShoppingCart.cshtml | 4 +- .../Partials/OffCanvasWishlist.cshtml | 6 +- .../Views/ShoppingCart/Wishlist.cshtml | 2 +- .../SmartStore.Web/Views/Test/Index.cshtml | 14 + .../Views/Topic/TopicDetails.cshtml | 60 +- .../SmartStore.Web/Views/Web.config | 4 +- .../SmartStore.Web/Views/_ViewStart.cshtml | 1 + src/Presentation/SmartStore.Web/Web.config | 40 +- .../fonts/glyphicons-halflings-regular.eot | Bin 0 -> 14079 bytes .../fonts/glyphicons-halflings-regular.svg | 228 + .../fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 29512 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 16448 bytes .../SmartStore.Web/packages.config | 8 +- .../App_Start/BundleConfig.cs | 30 + .../App_Start/FilterConfig.cs | 13 + .../App_Start/RouteConfig.cs | 23 + src/SmartStore.TestStuff/Content/Site.css | 24 + .../Content/bootstrap-theme.css | 587 + .../Content/bootstrap-theme.css.map | 1 + .../Content/bootstrap-theme.min.css | 6 + .../Content/bootstrap-theme.min.css.map | 1 + .../Content/bootstrap.css | 6757 ++++++++++ .../Content/bootstrap.css.map | 1 + .../Content/bootstrap.min.css | 6 + .../Content/bootstrap.min.css.map | 1 + .../Controllers/HomeController.cs | 50 + src/SmartStore.TestStuff/Global.asax | 1 + src/SmartStore.TestStuff/Global.asax.cs | 21 + .../Properties/AssemblyInfo.cs | 35 + src/SmartStore.TestStuff/Scripts/bootstrap.js | 2377 ++++ .../Scripts/bootstrap.min.js | 7 + .../Scripts/jquery-3.3.1.intellisense.js | 2670 ++++ .../Scripts/jquery-3.3.1.js | 10364 ++++++++++++++++ .../Scripts/jquery-3.3.1.min.js | 2 + .../Scripts/jquery-3.3.1.min.map | 1 + .../Scripts/jquery-3.3.1.slim.js | 8269 ++++++++++++ .../Scripts/jquery-3.3.1.slim.min.js | 2 + .../Scripts/jquery-3.3.1.slim.min.map | 1 + .../Scripts/jquery.validate-vsdoc.js | 1288 ++ .../Scripts/jquery.validate.js | 1601 +++ .../Scripts/jquery.validate.min.js | 4 + .../Scripts/jquery.validate.unobtrusive.js | 429 + .../jquery.validate.unobtrusive.min.js | 33 + .../Scripts/modernizr-2.8.3.js | 1406 +++ .../SmartStore.TestStuff.csproj | 250 + .../Views/Home/About.cshtml | 7 + .../Views/Home/Contact.cshtml | 17 + .../Views/Home/Index.cshtml | 8 + .../Views/Shared/Error.cshtml | 13 + .../Views/Shared/_Layout.cshtml | 42 + src/SmartStore.TestStuff/Views/Web.config | 43 + .../Views/_ViewStart.cshtml | 3 + src/SmartStore.TestStuff/Web.Debug.config | 30 + src/SmartStore.TestStuff/Web.Release.config | 31 + src/SmartStore.TestStuff/Web.config | 87 + src/SmartStore.TestStuff/favicon.ico | Bin 0 -> 32038 bytes .../fonts/glyphicons-halflings-regular.eot | Bin 0 -> 20127 bytes .../fonts/glyphicons-halflings-regular.svg | 288 + .../fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 45404 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23424 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 0 -> 18028 bytes src/SmartStore.TestStuff/packages.config | 24 + src/SmartStoreNET.sln | 7 +- src/Tests/SmartStore.Core.Tests/App.config | 9 +- .../Data/Hooks/DefaultDbHookHandlerTests.cs | 3 +- .../SmartStore.Core.Tests.csproj | 10 +- .../SmartStore.Core.Tests/packages.config | 4 +- src/Tests/SmartStore.Data.Tests/App.config | 4 +- .../SmartStore.Data.Tests.csproj | 6 + .../SmartStore.Data.Tests/packages.config | 2 + .../SmartStore.Services.Tests/App.config | 9 +- .../SmartStore.Services.Tests.csproj | 6 + .../SmartStore.Services.Tests/packages.config | 2 + src/Tests/SmartStore.Tests/App.config | 11 +- .../SmartStore.Tests/SmartStore.Tests.csproj | 6 + src/Tests/SmartStore.Tests/packages.config | 2 + src/Tests/SmartStore.Web.MVC.Tests/App.config | 17 +- .../Public/Infrastructure/RoutesTests.cs | 2 +- .../SmartStore.Web.MVC.Tests.csproj | 13 + .../SmartStore.Web.MVC.Tests/packages.config | 3 + src/Tools/SmartStore.Packager/App.config | 53 +- .../SmartStore.Packager.csproj | 14 + src/Tools/SmartStore.Packager/packages.config | 6 + src/Tools/SmartStore.WebApi.Client/App.config | 91 +- .../SmartStore.WebApi.Client.csproj | 18 +- .../SmartStore.WebApi.Client/packages.config | 5 +- 504 files changed, 65655 insertions(+), 4917 deletions(-) create mode 100644 src/Libraries/SmartStore.Core/Search/ISearchBits.cs create mode 100644 src/Libraries/SmartStore.Data/Migrations/201812212031185_UpdateToMySQL.Designer.cs create mode 100644 src/Libraries/SmartStore.Data/Migrations/201812212031185_UpdateToMySQL.cs create mode 100644 src/Libraries/SmartStore.Data/Migrations/201812212031185_UpdateToMySQL.resx create mode 100644 src/Libraries/SmartStore.Data/Migrations/Configuration.cs create mode 100644 src/Libraries/SmartStore.Data/MySQLDataProvider.cs create mode 100644 src/Libraries/SmartStore.Data/Sql/Indexes - Copy.sql create mode 100644 src/Libraries/SmartStore.Data/Sql/Indexes.Inverse - Copy.sql create mode 100644 src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer - Copy.sql create mode 100644 src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer.Inverse - Copy.sql create mode 100644 src/Libraries/SmartStore.Data/Sql/StoredProcedures - Copy.sql create mode 100644 src/Libraries/SmartStore.Data/Sql/StoredProcedures.Inverse - Copy.sql create mode 100644 src/Presentation/SmartStore.Web/Content/Site.css create mode 100644 src/Presentation/SmartStore.Web/Content/bootstrap.css create mode 100644 src/Presentation/SmartStore.Web/Content/bootstrap.min.css create mode 100644 src/Presentation/SmartStore.Web/Controllers/TestController.cs create mode 100644 src/Presentation/SmartStore.Web/Migrations/Configuration.cs create mode 100644 src/Presentation/SmartStore.Web/Scripts/bootstrap.js create mode 100644 src/Presentation/SmartStore.Web/Scripts/bootstrap.min.js create mode 100644 src/Presentation/SmartStore.Web/Scripts/jquery-1.10.2.intellisense.js create mode 100644 src/Presentation/SmartStore.Web/Scripts/jquery-1.10.2.js create mode 100644 src/Presentation/SmartStore.Web/Scripts/jquery-1.10.2.min.js create mode 100644 src/Presentation/SmartStore.Web/Scripts/jquery-1.10.2.min.map create mode 100644 src/Presentation/SmartStore.Web/Scripts/modernizr-2.6.2.js create mode 100644 src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/FontAwesomeIcon.cshtml create mode 100644 src/Presentation/SmartStore.Web/Views/Shared/_Layout.cshtml create mode 100644 src/Presentation/SmartStore.Web/Views/Test/Index.cshtml create mode 100644 src/Presentation/SmartStore.Web/Views/_ViewStart.cshtml create mode 100644 src/Presentation/SmartStore.Web/fonts/glyphicons-halflings-regular.eot create mode 100644 src/Presentation/SmartStore.Web/fonts/glyphicons-halflings-regular.svg create mode 100644 src/Presentation/SmartStore.Web/fonts/glyphicons-halflings-regular.ttf create mode 100644 src/Presentation/SmartStore.Web/fonts/glyphicons-halflings-regular.woff create mode 100644 src/SmartStore.TestStuff/App_Start/BundleConfig.cs create mode 100644 src/SmartStore.TestStuff/App_Start/FilterConfig.cs create mode 100644 src/SmartStore.TestStuff/App_Start/RouteConfig.cs create mode 100644 src/SmartStore.TestStuff/Content/Site.css create mode 100644 src/SmartStore.TestStuff/Content/bootstrap-theme.css create mode 100644 src/SmartStore.TestStuff/Content/bootstrap-theme.css.map create mode 100644 src/SmartStore.TestStuff/Content/bootstrap-theme.min.css create mode 100644 src/SmartStore.TestStuff/Content/bootstrap-theme.min.css.map create mode 100644 src/SmartStore.TestStuff/Content/bootstrap.css create mode 100644 src/SmartStore.TestStuff/Content/bootstrap.css.map create mode 100644 src/SmartStore.TestStuff/Content/bootstrap.min.css create mode 100644 src/SmartStore.TestStuff/Content/bootstrap.min.css.map create mode 100644 src/SmartStore.TestStuff/Controllers/HomeController.cs create mode 100644 src/SmartStore.TestStuff/Global.asax create mode 100644 src/SmartStore.TestStuff/Global.asax.cs create mode 100644 src/SmartStore.TestStuff/Properties/AssemblyInfo.cs create mode 100644 src/SmartStore.TestStuff/Scripts/bootstrap.js create mode 100644 src/SmartStore.TestStuff/Scripts/bootstrap.min.js create mode 100644 src/SmartStore.TestStuff/Scripts/jquery-3.3.1.intellisense.js create mode 100644 src/SmartStore.TestStuff/Scripts/jquery-3.3.1.js create mode 100644 src/SmartStore.TestStuff/Scripts/jquery-3.3.1.min.js create mode 100644 src/SmartStore.TestStuff/Scripts/jquery-3.3.1.min.map create mode 100644 src/SmartStore.TestStuff/Scripts/jquery-3.3.1.slim.js create mode 100644 src/SmartStore.TestStuff/Scripts/jquery-3.3.1.slim.min.js create mode 100644 src/SmartStore.TestStuff/Scripts/jquery-3.3.1.slim.min.map create mode 100644 src/SmartStore.TestStuff/Scripts/jquery.validate-vsdoc.js create mode 100644 src/SmartStore.TestStuff/Scripts/jquery.validate.js create mode 100644 src/SmartStore.TestStuff/Scripts/jquery.validate.min.js create mode 100644 src/SmartStore.TestStuff/Scripts/jquery.validate.unobtrusive.js create mode 100644 src/SmartStore.TestStuff/Scripts/jquery.validate.unobtrusive.min.js create mode 100644 src/SmartStore.TestStuff/Scripts/modernizr-2.8.3.js create mode 100644 src/SmartStore.TestStuff/SmartStore.TestStuff.csproj create mode 100644 src/SmartStore.TestStuff/Views/Home/About.cshtml create mode 100644 src/SmartStore.TestStuff/Views/Home/Contact.cshtml create mode 100644 src/SmartStore.TestStuff/Views/Home/Index.cshtml create mode 100644 src/SmartStore.TestStuff/Views/Shared/Error.cshtml create mode 100644 src/SmartStore.TestStuff/Views/Shared/_Layout.cshtml create mode 100644 src/SmartStore.TestStuff/Views/Web.config create mode 100644 src/SmartStore.TestStuff/Views/_ViewStart.cshtml create mode 100644 src/SmartStore.TestStuff/Web.Debug.config create mode 100644 src/SmartStore.TestStuff/Web.Release.config create mode 100644 src/SmartStore.TestStuff/Web.config create mode 100644 src/SmartStore.TestStuff/favicon.ico create mode 100644 src/SmartStore.TestStuff/fonts/glyphicons-halflings-regular.eot create mode 100644 src/SmartStore.TestStuff/fonts/glyphicons-halflings-regular.svg create mode 100644 src/SmartStore.TestStuff/fonts/glyphicons-halflings-regular.ttf create mode 100644 src/SmartStore.TestStuff/fonts/glyphicons-halflings-regular.woff create mode 100644 src/SmartStore.TestStuff/fonts/glyphicons-halflings-regular.woff2 create mode 100644 src/SmartStore.TestStuff/packages.config create mode 100644 src/Tools/SmartStore.Packager/packages.config diff --git a/src/Libraries/SmartStore.Core/BaseEntity.cs b/src/Libraries/SmartStore.Core/BaseEntity.cs index 3fd0016909..b591189f8d 100644 --- a/src/Libraries/SmartStore.Core/BaseEntity.cs +++ b/src/Libraries/SmartStore.Core/BaseEntity.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using System.Data.Entity.Core.Objects; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.Serialization; @@ -21,27 +20,18 @@ public abstract partial class BaseEntity : IEquatable [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } - [SuppressMessage("ReSharper", "PossibleNullReferenceException")] - public virtual string GetEntityName() - { - return GetUnproxiedType().Name; - } - + [SuppressMessage("ReSharper", "PossibleNullReferenceException")] public Type GetUnproxiedType() { - #region Old - //var t = GetType(); - //if (t.AssemblyQualifiedName.StartsWith("System.Data.Entity.")) - //{ - // // it's a proxied type - // t = t.BaseType; - //} - - //return t; - #endregion + var t = GetType(); + if (t.AssemblyQualifiedName.StartsWith("System.Data.Entity.")) + { + // it's a proxied type + t = t.BaseType; + } - return ObjectContext.GetObjectType(GetType()); - } + return t; + } /// /// Transient objects are not associated with an item already in storage. For instance, diff --git a/src/Libraries/SmartStore.Core/Data/DataSettings.cs b/src/Libraries/SmartStore.Core/Data/DataSettings.cs index 146cc96bcb..fdec473bf9 100644 --- a/src/Libraries/SmartStore.Core/Data/DataSettings.cs +++ b/src/Libraries/SmartStore.Core/Data/DataSettings.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using System.Web.Hosting; +using MySql.Data.MySqlClient; using SmartStore.Utilities; using SmartStore.Utilities.Threading; @@ -145,8 +146,11 @@ public string ProviderInvariantName if (this.DataProvider.HasValue() && this.DataProvider.IsCaseInsensitiveEqual("sqlserver")) return "System.Data.SqlClient"; - // SqlCe should always be the default provider - return "System.Data.SqlServerCe.4.0"; + if (this.DataProvider.HasValue() && this.DataProvider.IsCaseInsensitiveEqual("mysql")) + return "MySQL.Data.MySqlClient"; + + // SqlCe should always be the default provider + return "System.Data.SqlServerCe.4.0"; } } @@ -157,8 +161,10 @@ public string ProviderFriendlyName if (this.DataProvider.HasValue() && this.DataProvider.IsCaseInsensitiveEqual("sqlserver")) return "SQL Server"; - // SqlCe should always be the default provider - return "SQL Server Compact (SQL CE)"; + if (this.DataProvider.HasValue() && this.DataProvider.IsCaseInsensitiveEqual("mysql")) + return "MySQL Server"; + // SqlCe should always be the default provider + return "SQL Server Compact (SQL CE)"; } } @@ -170,6 +176,13 @@ public bool IsSqlServer } } + public bool IsMySqlServer + { + get + { + return this.DataProvider.HasValue() && this.DataProvider.IsCaseInsensitiveEqual("mysql"); + } + } public string DataConnectionString { get; @@ -220,9 +233,18 @@ public virtual bool Load() if (settings.ContainsKey("DataProvider")) { this.DataProvider = settings["DataProvider"]; - this.DataConnectionType = this.IsSqlServer - ? typeof(SqlConnection).AssemblyQualifiedName - : typeof(SqlCeConnection).AssemblyQualifiedName; + if(this.IsSqlServer) + { + this.DataConnectionType = typeof(SqlConnection).AssemblyQualifiedName; + } + else if(this.IsMySqlServer) + { + this.DataConnectionType = typeof(MySqlConnection).AssemblyQualifiedName; + } + else + { + this.DataConnectionType = typeof(SqlCeConnection).AssemblyQualifiedName; + } } if (settings.ContainsKey("DataConnectionString")) { @@ -283,6 +305,7 @@ public virtual bool Save() protected virtual string ResolveTenant() { var tenantsBaseDir = CommonHelper.MapPath("~/App_Data/Tenants"); + var curTenantFile = Path.Combine(tenantsBaseDir, "current.txt"); string curTenant = null; diff --git a/src/Libraries/SmartStore.Core/Data/DbQuerySettings.cs b/src/Libraries/SmartStore.Core/Data/DbQuerySettings.cs index 3e2318e4f0..d1e22d6965 100644 --- a/src/Libraries/SmartStore.Core/Data/DbQuerySettings.cs +++ b/src/Libraries/SmartStore.Core/Data/DbQuerySettings.cs @@ -4,6 +4,8 @@ namespace SmartStore.Core.Data { public class DbQuerySettings { + private readonly static DbQuerySettings s_default = new DbQuerySettings(false, false); + public DbQuerySettings(bool ignoreAcl, bool ignoreMultiStore) { this.IgnoreAcl = ignoreAcl; @@ -13,6 +15,9 @@ public DbQuerySettings(bool ignoreAcl, bool ignoreMultiStore) public bool IgnoreAcl { get; private set; } public bool IgnoreMultiStore { get; private set; } - public static DbQuerySettings Default { get; } = new DbQuerySettings(false, false); + public static DbQuerySettings Default + { + get { return s_default; } + } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs index c6d208659f..0be6f2d778 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs @@ -188,21 +188,11 @@ public CatalogSettings() /// Gets or sets a value indicating whether and where to display a list of subcategories /// public SubCategoryDisplayType SubCategoryDisplayType { get; set; } - - /// - /// An option indicating whether sub pages should display the subcategories - /// - public bool ShowSubCategoriesInSubPages { get; set; } - - /// - /// An option indicating whether sub pages should display the category/manufacturer description - /// - public bool ShowDescriptionInSubPages { get; set; } - - /// - /// Gets or sets a value indicating whether a 'Share button' is enabled - /// - public bool ShowShareButton { get; set; } + + /// + /// Gets or sets a value indicating whether a 'Share button' is enabled + /// + public bool ShowShareButton { get; set; } /// /// Gets or sets a share code (e.g. AddThis button code) @@ -450,16 +440,11 @@ public CatalogSettings() /// An option indicating whether products on category and manufacturer pages should include featured products as well /// public bool IncludeFeaturedProductsInNormalLists { get; set; } - - /// - /// An option indicating whether products on category and manufacturer pages should include featured products in sub pages as well - /// - public bool IncludeFeaturedProductsInSubPages { get; set; } - - /// - /// Gets or sets a value indicating whether tier prices should be displayed with applied discounts (if available) - /// - public bool DisplayTierPricesWithDiscounts { get; set; } + + /// + /// Gets or sets a value indicating whether tier prices should be displayed with applied discounts (if available) + /// + public bool DisplayTierPricesWithDiscounts { get; set; } /// /// Gets or sets a value indicating whether to ignore discounts (side-wide) diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/Product.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/Product.cs index 6f18a1722d..3d7f68fad0 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/Product.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/Product.cs @@ -711,14 +711,14 @@ public decimal Height /// [DataMember] [Index("IX_Product_Published_Deleted_IsSystemProduct", 1)] - public bool Published { get; set; } + public bool Published { get; set; } /// /// Gets or sets a value indicating whether the entity has been deleted /// [Index] [Index("IX_Product_Published_Deleted_IsSystemProduct", 2)] - public bool Deleted { get; set; } + public bool Deleted { get; set; } /// /// Gets or sets a value indicating whether the entity is a system product. @@ -726,7 +726,7 @@ public decimal Height [DataMember] [Index("IX_Product_SystemName_IsSystemProduct", 2)] [Index("IX_Product_Published_Deleted_IsSystemProduct", 3)] - public bool IsSystemProduct { get; set; } + public bool IsSystemProduct { get; set; } /// /// Gets or sets the product system name. diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeValue.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeValue.cs index f6a9c32444..e0340196ad 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeValue.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeValue.cs @@ -15,7 +15,7 @@ public partial class ProductVariantAttributeValue : BaseEntity, ILocalizedEntity /// Gets or sets the product variant attribute mapping identifier /// [DataMember] - [Index("IX_ProductVariantAttributeValue_ProductVariantAttributeId_DisplayOrder", 1)] + [Index("IX_ProductVAV_ProductVAId_DO", 1)] public int ProductVariantAttributeId { get; set; } /// @@ -65,7 +65,7 @@ public partial class ProductVariantAttributeValue : BaseEntity, ILocalizedEntity /// Gets or sets the display order /// [DataMember] - [Index("IX_ProductVariantAttributeValue_ProductVariantAttributeId_DisplayOrder", 2)] + [Index("xxx", 2)] public int DisplayOrder { get; set; } /// diff --git a/src/Libraries/SmartStore.Core/Domain/Common/CommonSettings.cs b/src/Libraries/SmartStore.Core/Domain/Common/CommonSettings.cs index 46d94f8c98..a00fc31da7 100644 --- a/src/Libraries/SmartStore.Core/Domain/Common/CommonSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Common/CommonSettings.cs @@ -6,7 +6,7 @@ public class CommonSettings : ISettings { public CommonSettings() { - UseStoredProceduresIfSupported = true; + UseStoredProceduresIfSupported = true; AutoUpdateEnabled = true; EntityPickerPageSize = 48; MaxScheduleHistoryAgeInDays = 30; diff --git a/src/Libraries/SmartStore.Core/Domain/Localization/LocalizedProperty.cs b/src/Libraries/SmartStore.Core/Domain/Localization/LocalizedProperty.cs index 6996cf8549..f0d0406fb4 100644 --- a/src/Libraries/SmartStore.Core/Domain/Localization/LocalizedProperty.cs +++ b/src/Libraries/SmartStore.Core/Domain/Localization/LocalizedProperty.cs @@ -28,7 +28,6 @@ public partial class LocalizedProperty : BaseEntity /// [DataMember] [Index("IX_LocalizedProperty_Compound", Order = 3)] - [Index("IX_LocalizedProperty_LocaleKeyGroup")] public string LocaleKeyGroup { get; set; } /// diff --git a/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs index a711c68aa7..f345fa1b19 100644 --- a/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs @@ -394,35 +394,5 @@ public static string BuildQueryString(this NameValueCollection nvc, Encoding enc } #endregion - - #region List - - /// - /// Safe way to remove selected entries from a list. - /// - /// To be used for materialized lists only, not IEnumerable or similar. - /// Object type. - /// List. - /// Selector for the entries to be removed. - /// Number of removed entries. - public static int Remove(this IList list, Func selector) - { - Guard.NotNull(list, nameof(list)); - Guard.NotNull(selector, nameof(selector)); - - var count = 0; - for (var i = list.Count - 1; i >= 0; i--) - { - if (selector(list[i])) - { - list.RemoveAt(i); - ++count; - } - } - - return count; - } - - #endregion } } diff --git a/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs index 3241c7d8fa..dac874caae 100644 --- a/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs @@ -3,7 +3,7 @@ using System.Text; namespace SmartStore -{ +{ public static class MiscExtensions { public static void Dump(this Exception exception) @@ -16,7 +16,7 @@ public static void Dump(this Exception exception) catch { } } - public static string ToAllMessages(this Exception exception, bool includeStackTrace = false) + public static string ToAllMessages(this Exception exception) { var sb = new StringBuilder(); @@ -24,21 +24,8 @@ public static string ToAllMessages(this Exception exception, bool includeStackTr { if (!sb.ToString().EmptyNull().Contains(exception.Message)) { - if (includeStackTrace) - { - if (sb.Length > 0) - { - sb.AppendLine(); - sb.AppendLine(); - } - sb.AppendLine(exception.ToString()); - } - else - { - sb.Grow(exception.Message, " * "); - } + sb.Grow(exception.Message, " * "); } - exception = exception.InnerException; } @@ -65,10 +52,8 @@ public static bool IsNullOrDefault(this T? value) where T : struct /// public static string ToHexString(this byte[] bytes, int length = 0) { - if (bytes == null || bytes.Length <= 0) - { - return ""; - } + if (bytes == null || bytes.Length <= 0) + return ""; var sb = new StringBuilder(); @@ -76,12 +61,9 @@ public static string ToHexString(this byte[] bytes, int length = 0) { sb.Append(b.ToString("x2")); - if (length > 0 && sb.Length >= length) - { - break; - } + if (length > 0 && sb.Length >= length) + break; } - return sb.ToString(); } @@ -93,24 +75,21 @@ public static string ToHexString(this byte[] bytes, int length = 0) /// Delimiter to use public static void Grow(this StringBuilder sb, string grow, string delimiter) { - Guard.NotNull(delimiter, nameof(delimiter)); + if (delimiter == null) + throw new ArgumentNullException(nameof(delimiter)); if (!string.IsNullOrWhiteSpace(grow)) { - if (sb.Length <= 0) - { - sb.Append(grow); - } - else - { - sb.AppendFormat("{0}{1}", delimiter, grow); - } + if (sb.Length <= 0) + sb.Append(grow); + else + sb.AppendFormat("{0}{1}", delimiter, grow); } } public static string SafeGet(this string[] arr, int index) { - return arr != null && index < arr.Length ? arr[index] : ""; + return (arr != null && index < arr.Length ? arr[index] : ""); } } } diff --git a/src/Libraries/SmartStore.Core/Extensions/StreamExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/StreamExtensions.cs index 6b286561a0..e67a92a8a1 100644 --- a/src/Libraries/SmartStore.Core/Extensions/StreamExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/StreamExtensions.cs @@ -68,8 +68,7 @@ public static bool ContentsEqual(this Stream src, Stream other) if (src.Length != other.Length) return false; - const int intSize = sizeof(Int64); - const int bufferSize = 2048; + const int bufferSize = 2048; var buffer1 = new byte[bufferSize]; var buffer2 = new byte[bufferSize]; @@ -78,7 +77,7 @@ public static bool ContentsEqual(this Stream src, Stream other) int len1 = src.Read(buffer1, 0, bufferSize); int len2 = other.Read(buffer2, 0, bufferSize); - if (len1 != len2) + if (len1 != len2) return false; if (len1 == 0) @@ -87,7 +86,7 @@ public static bool ContentsEqual(this Stream src, Stream other) int iterations = (int)Math.Ceiling((double)len1 / sizeof(Int64)); for (int i = 0; i < iterations; i++) { - if (BitConverter.ToInt64(buffer1, i * intSize) != BitConverter.ToInt64(buffer2, i * intSize)) + if (BitConverter.ToInt64(buffer1, i * sizeof(Int64)) != BitConverter.ToInt64(buffer2, i * sizeof(Int64))) { return false; } diff --git a/src/Libraries/SmartStore.Core/IO/LockFile/LockFileManager.cs b/src/Libraries/SmartStore.Core/IO/LockFile/LockFileManager.cs index 20d00e3441..ab48a467b8 100644 --- a/src/Libraries/SmartStore.Core/IO/LockFile/LockFileManager.cs +++ b/src/Libraries/SmartStore.Core/IO/LockFile/LockFileManager.cs @@ -42,7 +42,7 @@ public bool TryAcquireLock(string path, out ILockFile lockFile) { return false; } - + lockFile = new LockFile(_env.TenantFolder, path, DateTime.UtcNow.ToString("u"), _rwLock); return true; } @@ -80,7 +80,8 @@ private bool IsLockedInternal(string path) { var content = _env.TenantFolder.ReadFile(path); - if (DateTime.TryParse(content, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var creationUtc)) + DateTime creationUtc; + if (DateTime.TryParse(content, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out creationUtc)) { // if expired the file is not removed // it should be automatically as there is a finalizer in LockFile diff --git a/src/Libraries/SmartStore.Core/Infrastructure/Guard.cs b/src/Libraries/SmartStore.Core/Infrastructure/Guard.cs index a1d8f6c73a..d49aa6c074 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/Guard.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/Guard.cs @@ -43,7 +43,7 @@ public static void NotEmpty(string arg, string argName) public static void NotEmpty(ICollection arg, string argName) { if (arg == null || !arg.Any()) - throw Error.Argument(argName, "Collection cannot be null and must contain at least one item."); + throw Error.Argument(argName, "Collection cannot be null and must have at least one item."); } [DebuggerStepThrough] diff --git a/src/Libraries/SmartStore.Core/Infrastructure/Singleton.cs b/src/Libraries/SmartStore.Core/Infrastructure/Singleton.cs index 66c50bd5fa..9307146c93 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/Singleton.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/Singleton.cs @@ -9,7 +9,7 @@ namespace SmartStore.Core.Infrastructure /// sense of the word as a standardized way to store single instances. /// /// The type of object to store. - /// Access to the instance is not synchronized. + /// Access to the instance is not synchrnoized. public class Singleton : Singleton { static T instance; diff --git a/src/Libraries/SmartStore.Core/PagedList`T.cs b/src/Libraries/SmartStore.Core/PagedList`T.cs index f7e951d047..dcebc38642 100644 --- a/src/Libraries/SmartStore.Core/PagedList`T.cs +++ b/src/Libraries/SmartStore.Core/PagedList`T.cs @@ -10,8 +10,11 @@ namespace SmartStore.Core { public class PagedList : IPagedList, IReadOnlyList, IReadOnlyCollection { - private bool _isEfQuery; + private IQueryable _query; private bool _queryIsPagedAlready; + + private int _pageIndex; + private int _pageSize; private int? _totalCount; private List _list; @@ -37,13 +40,12 @@ private void Init(IQueryable source, int pageIndex, int pageSize, int? totalC Guard.NotNull(source, nameof(source)); Guard.PagingArgsValid(pageIndex, pageSize, "pageIndex", "pageSize"); - SourceQuery = source; - PageIndex = pageIndex; - PageSize = pageSize; + _query = source; + _queryIsPagedAlready = totalCount.HasValue; + _pageIndex = pageIndex; + _pageSize = pageSize; _totalCount = totalCount; - _queryIsPagedAlready = totalCount.HasValue; - _isEfQuery = source.Provider is IDbAsyncQueryProvider; } private void EnsureIsLoaded() @@ -52,49 +54,52 @@ private void EnsureIsLoaded() { if (_totalCount == null) { - _totalCount = SourceQuery.Count(); + _totalCount = _query.Count(); } if (_queryIsPagedAlready) { - _list = SourceQuery.ToList(); + _list = _query.ToList(); } else { - _list = ApplyPaging(SourceQuery).ToList(); + _list = ApplyPaging(_query).ToList(); } } } #region IPageable Members - public IQueryable SourceQuery { get; private set; } + public IQueryable SourceQuery + { + get { return _query; } + } public IPagedList AlterQuery(Func, IQueryable> alterer) { - var result = alterer?.Invoke(SourceQuery); - SourceQuery = result ?? throw new InvalidOperationException("The '{0}' delegate must not return NULL.".FormatInvariant(nameof(alterer))); + var result = alterer?.Invoke(_query); + _query = result ?? throw new InvalidOperationException("The '{0}' delegate must not return NULL.".FormatInvariant(nameof(alterer))); return this; } public IQueryable ApplyPaging(IQueryable query) { - if (PageIndex == 0 && PageSize == int.MaxValue) + if (_pageIndex == 0 && _pageSize == int.MaxValue) { // Paging unnecessary return query; } else { - var skip = PageIndex * PageSize; - if (_isEfQuery) + var skip = _pageIndex * _pageSize; + if (query.Provider is IDbAsyncQueryProvider) { - return query.Skip(() => skip).Take(() => PageSize); + return query.Skip(() => skip).Take(() => _pageSize); } else { - return query.Skip(skip).Take(PageSize); + return query.Skip(skip).Take(_pageSize); } } } @@ -112,17 +117,25 @@ public IPagedList Load(bool force = false) return this; } - public int PageIndex { get; set; } + public int PageIndex + { + get { return _pageIndex; } + set { _pageIndex = value; } + } - public int PageSize { get; set; } + public int PageSize + { + get { return _pageSize; } + set { _pageSize = value; } + } - public int TotalCount + public int TotalCount { get { if (!_totalCount.HasValue) { - _totalCount = SourceQuery.Count(); + _totalCount = _query.Count(); } return _totalCount.Value; diff --git a/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs b/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs index 23cada0323..819b14a08d 100644 --- a/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs +++ b/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using SmartStore.Collections; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Directory; @@ -28,7 +27,6 @@ public AcquireWriterContext(AcquirementReason reason) DeliveryTimes = new Dictionary(); Manufacturers = new Dictionary(); Categories = new Dictionary(); - Translations = new Dictionary(StringComparer.OrdinalIgnoreCase); CustomProperties = new Dictionary(); } @@ -82,11 +80,6 @@ public AcquireWriterContext(AcquirementReason reason) /// public Dictionary DeliveryTimes { get; set; } - /// - /// All translations for global scopes (like Category, Manufacturer etc.) - /// - public Dictionary Translations { get; set; } - /// /// Use this dictionary for any custom data required along indexing /// diff --git a/src/Libraries/SmartStore.Core/Search/IIndexStore.cs b/src/Libraries/SmartStore.Core/Search/IIndexStore.cs index 36e7fe6a23..237eb50524 100644 --- a/src/Libraries/SmartStore.Core/Search/IIndexStore.cs +++ b/src/Libraries/SmartStore.Core/Search/IIndexStore.cs @@ -25,21 +25,11 @@ public interface IIndexStore /// bool Exists { get; } - /// - /// Gets the size of the index in bytes. - /// - long IndexSize { get; } - - /// - /// The identifier of the last added document. - /// - int LastAddedDocumentId { get; } - - /// - /// Gets the total number of indexed documents - /// - /// Type of document, use null to get all documents - int GetDocumentCount(SearchDocumentType? documentType); + /// + /// Gets the total number of indexed documents + /// + /// Type of document, use null to get all documents + int GetDocumentCount(SearchDocumentType? documentType); /// /// Returns every field's name available in the index diff --git a/src/Libraries/SmartStore.Core/Search/ISearchBits.cs b/src/Libraries/SmartStore.Core/Search/ISearchBits.cs new file mode 100644 index 0000000000..c9ba405ef8 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Search/ISearchBits.cs @@ -0,0 +1,12 @@ +using System; + +namespace SmartStore.Core.Search +{ + public interface ISearchBits + { + ISearchBits And(ISearchBits other); + ISearchBits Or(ISearchBits other); + ISearchBits Xor(ISearchBits other); + long Count(); + } +} diff --git a/src/Libraries/SmartStore.Core/Search/ISearchEngine.cs b/src/Libraries/SmartStore.Core/Search/ISearchEngine.cs index 12235f2d44..2a17f673b0 100644 --- a/src/Libraries/SmartStore.Core/Search/ISearchEngine.cs +++ b/src/Libraries/SmartStore.Core/Search/ISearchEngine.cs @@ -17,6 +17,12 @@ public interface ISearchEngine /// Search hit ISearchHit Get(int id); + /// + /// Get search bits + /// + /// Search bits + ISearchBits GetBits(); + /// /// Get total number of search hits /// diff --git a/src/Libraries/SmartStore.Core/Search/IndexInfo.cs b/src/Libraries/SmartStore.Core/Search/IndexInfo.cs index 7389b7a5cf..468754317f 100644 --- a/src/Libraries/SmartStore.Core/Search/IndexInfo.cs +++ b/src/Libraries/SmartStore.Core/Search/IndexInfo.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -using SmartStore.Utilities; namespace SmartStore.Core.Search { @@ -26,9 +25,7 @@ public IndexInfo(string scope) public string Scope { get; private set; } public int DocumentCount { get; set; } - public long IndexSize { get; set; } - public int LastAddedDocumentId { get; set; } - public IEnumerable Fields { get; set; } + public IEnumerable Fields { get; set; } // loaded from status file public DateTime? LastIndexedUtc { get; set; } @@ -54,9 +51,7 @@ public string ToXml() new XElement("error", Error), new XElement("should-rebuild", ShouldRebuild ? "true" : "false"), new XElement("document-count", DocumentCount), - new XElement("index-size", IndexSize), - new XElement("last-added-document-id", LastAddedDocumentId), - new XElement("fields", string.Join(", ", Fields ?? Enumerable.Empty())) + new XElement("fields", string.Join(", ", Fields ?? Enumerable.Empty())) )).ToString(); } @@ -104,19 +99,7 @@ public static IndexInfo FromXml(string xml, string scope) info.DocumentCount = documentCount.ToInt(); } - var indexSize = doc.Descendants("index-size").FirstOrDefault()?.Value; - if (indexSize.HasValue() && CommonHelper.TryConvert(indexSize, out long size)) - { - info.IndexSize = size; - } - - var lastAddedDocumentId = doc.Descendants("last-added-document-id").FirstOrDefault()?.Value; - if (lastAddedDocumentId.HasValue()) - { - info.LastAddedDocumentId = lastAddedDocumentId.ToInt(); - } - - var fields = doc.Descendants("fields").FirstOrDefault()?.Value; + var fields = doc.Descendants("fields").FirstOrDefault()?.Value; if (fields.HasValue()) { info.Fields = fields.SplitSafe(", "); diff --git a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj index df978dc456..99999c691d 100644 --- a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj +++ b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj @@ -93,9 +93,14 @@ ..\..\packages\Microsoft.Web.Xdt.2.1.1\lib\net40\Microsoft.Web.XmlTransform.dll True - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - True + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll ..\..\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll @@ -216,7 +221,6 @@ - @@ -237,7 +241,6 @@ - @@ -247,7 +250,6 @@ - @@ -469,6 +471,7 @@ + diff --git a/src/Libraries/SmartStore.Core/app.config b/src/Libraries/SmartStore.Core/app.config index 8c178073c2..f91df2dce1 100644 --- a/src/Libraries/SmartStore.Core/app.config +++ b/src/Libraries/SmartStore.Core/app.config @@ -16,4 +16,13 @@ - + + + + + + + + + + diff --git a/src/Libraries/SmartStore.Core/packages.config b/src/Libraries/SmartStore.Core/packages.config index 4820921437..0b9845c03e 100644 --- a/src/Libraries/SmartStore.Core/packages.config +++ b/src/Libraries/SmartStore.Core/packages.config @@ -11,7 +11,9 @@ - + + + diff --git a/src/Libraries/SmartStore.Data/EfDataProviderFactory.cs b/src/Libraries/SmartStore.Data/EfDataProviderFactory.cs index 4f7652f4ab..ccef9148f1 100644 --- a/src/Libraries/SmartStore.Data/EfDataProviderFactory.cs +++ b/src/Libraries/SmartStore.Data/EfDataProviderFactory.cs @@ -30,6 +30,8 @@ public override IDataProvider LoadDataProvider() return new SqlServerDataProvider(); case "sqlce": return new SqlCeDataProvider(); + case "mysql": + return new MySQLDataProvider(); default: throw new SmartException(string.Format("Unsupported dataprovider name: {0}", providerName)); } diff --git a/src/Libraries/SmartStore.Data/Mapping/Tasks/ScheduleTaskHistoryMap.cs b/src/Libraries/SmartStore.Data/Mapping/Tasks/ScheduleTaskHistoryMap.cs index 817cabd4e6..8a715d40a8 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Tasks/ScheduleTaskHistoryMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Tasks/ScheduleTaskHistoryMap.cs @@ -10,7 +10,7 @@ public ScheduleTaskHistoryMap() ToTable("ScheduleTaskHistory"); HasKey(x => x.Id); Property(x => x.MachineName).IsRequired().HasMaxLength(400); - Property(x => x.Error); + Property(x => x.Error).HasMaxLength(1000); Property(x => x.ProgressMessage).HasMaxLength(1000); HasRequired(x => x.ScheduleTask) diff --git a/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.cs b/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.cs index de838e8eca..21e35cc411 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.cs @@ -28,33 +28,33 @@ public override void Up() .PrimaryKey(t => t.Id) .ForeignKey("dbo.ProductBundleItem", t => t.BundleItemId, cascadeDelete: true) .Index(t => t.BundleItemId); - + CreateTable( "dbo.ProductBundleItem", c => new - { - Id = c.Int(nullable: false, identity: true), - ProductId = c.Int(nullable: false), - BundleProductId = c.Int(nullable: false), - Quantity = c.Int(nullable: false), - Discount = c.Decimal(precision: 18, scale: 4), - DiscountPercentage = c.Boolean(nullable: false), - Name = c.String(maxLength: 400), - ShortDescription = c.String(), - FilterAttributes = c.Boolean(nullable: false), - HideThumbnail = c.Boolean(nullable: false), - Visible = c.Boolean(nullable: false), - Published = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - UpdatedOnUtc = c.DateTime(nullable: false), - }) + { + Id = c.Int(nullable: false, identity: true), + ProductId = c.Int(nullable: false), + BundleProductId = c.Int(nullable: false), + Quantity = c.Int(nullable: false), + Discount = c.Decimal(precision: 18, scale: 4), + DiscountPercentage = c.Boolean(nullable: false), + Name = c.String(maxLength: 400), + ShortDescription = c.String(), + FilterAttributes = c.Boolean(nullable: false), + HideThumbnail = c.Boolean(nullable: false), + Visible = c.Boolean(nullable: false), + Published = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + }) .PrimaryKey(t => t.Id) .ForeignKey("dbo.Product", t => t.BundleProductId, cascadeDelete: true) .ForeignKey("dbo.Product", t => t.ProductId) .Index(t => t.BundleProductId) .Index(t => t.ProductId); - + CreateTable( "dbo.Product", c => new @@ -351,19 +351,19 @@ public override void Up() { Id = c.Int(nullable: false, identity: true), CustomerGuid = c.Guid(nullable: false), - Username = c.String(maxLength: 1000), - Email = c.String(maxLength: 1000), - Password = c.String(maxLength: 4000), + Username = c.String(maxLength: 400), + Email = c.String(maxLength: 400), + Password = c.String(maxLength: 400), PasswordFormatId = c.Int(nullable: false), - PasswordSalt = c.String(maxLength: 4000), - AdminComment = c.String(maxLength: 4000), + PasswordSalt = c.String(maxLength: 400), + AdminComment = c.String(maxLength: 400), IsTaxExempt = c.Boolean(nullable: false), AffiliateId = c.Int(nullable: false), Active = c.Boolean(nullable: false), Deleted = c.Boolean(nullable: false), IsSystemAccount = c.Boolean(nullable: false), - SystemName = c.String(maxLength: 4000), - LastIpAddress = c.String(maxLength: 4000), + SystemName = c.String(maxLength: 40), + LastIpAddress = c.String(maxLength: 40), CreatedOnUtc = c.DateTime(nullable: false), LastLoginDateUtc = c.DateTime(), LastActivityDateUtc = c.DateTime(nullable: false), @@ -381,18 +381,18 @@ public override void Up() c => new { Id = c.Int(nullable: false, identity: true), - FirstName = c.String(maxLength: 4000), - LastName = c.String(maxLength: 4000), - Email = c.String(maxLength: 4000), - Company = c.String(maxLength: 4000), + FirstName = c.String(maxLength: 400), + LastName = c.String(maxLength: 400), + Email = c.String(maxLength: 400), + Company = c.String(maxLength: 400), CountryId = c.Int(), StateProvinceId = c.Int(), - City = c.String(maxLength: 4000), - Address1 = c.String(maxLength: 4000), - Address2 = c.String(maxLength: 4000), - ZipPostalCode = c.String(maxLength: 4000), - PhoneNumber = c.String(maxLength: 4000), - FaxNumber = c.String(maxLength: 4000), + City = c.String(maxLength: 400), + Address1 = c.String(maxLength: 400), + Address2 = c.String(maxLength: 400), + ZipPostalCode = c.String(maxLength: 400), + PhoneNumber = c.String(maxLength: 400), + FaxNumber = c.String(maxLength: 400), CreatedOnUtc = c.DateTime(nullable: false), }) .PrimaryKey(t => t.Id) @@ -590,8 +590,9 @@ public override void Up() { Id = c.Int(nullable: false, identity: true), CustomerId = c.Int(nullable: false), - Email = c.String(maxLength: 4000), - ExternalIdentifier = c.String(maxLength: 4000), + //Email = c.String(maxLength: 4000), MOD: + Email = c.String(maxLength: 100), + ExternalIdentifier = c.String(maxLength: 4000), ExternalDisplayIdentifier = c.String(maxLength: 4000), OAuthToken = c.String(maxLength: 4000), OAuthAccessToken = c.String(maxLength: 4000), @@ -681,69 +682,131 @@ public override void Up() "dbo.Order", c => new { - Id = c.Int(nullable: false, identity: true), - OrderNumber = c.String(maxLength: 4000), - OrderGuid = c.Guid(nullable: false), - StoreId = c.Int(nullable: false), - CustomerId = c.Int(nullable: false), - BillingAddressId = c.Int(nullable: false), - ShippingAddressId = c.Int(), - OrderStatusId = c.Int(nullable: false), - ShippingStatusId = c.Int(nullable: false), - PaymentStatusId = c.Int(nullable: false), - PaymentMethodSystemName = c.String(maxLength: 4000), - CustomerCurrencyCode = c.String(maxLength: 4000), - CurrencyRate = c.Decimal(nullable: false, precision: 18, scale: 8), - CustomerTaxDisplayTypeId = c.Int(nullable: false), - VatNumber = c.String(maxLength: 4000), - OrderSubtotalInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - OrderSubtotalExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - OrderSubTotalDiscountInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - OrderSubTotalDiscountExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - OrderShippingInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - OrderShippingExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - PaymentMethodAdditionalFeeInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - PaymentMethodAdditionalFeeExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - TaxRates = c.String(maxLength: 4000), - OrderTax = c.Decimal(nullable: false, precision: 18, scale: 4), - OrderDiscount = c.Decimal(nullable: false, precision: 18, scale: 4), - OrderTotal = c.Decimal(nullable: false, precision: 18, scale: 4), - RefundedAmount = c.Decimal(nullable: false, precision: 18, scale: 4), - RewardPointsWereAdded = c.Boolean(nullable: false), - CheckoutAttributeDescription = c.String(maxLength: 4000), - CheckoutAttributesXml = c.String(maxLength: 4000), - CustomerLanguageId = c.Int(nullable: false), - AffiliateId = c.Int(nullable: false), - CustomerIp = c.String(maxLength: 4000), - AllowStoringCreditCardNumber = c.Boolean(nullable: false), - CardType = c.String(maxLength: 4000), - CardName = c.String(maxLength: 4000), - CardNumber = c.String(maxLength: 4000), - MaskedCreditCardNumber = c.String(maxLength: 4000), - CardCvv2 = c.String(maxLength: 4000), - CardExpirationMonth = c.String(maxLength: 4000), - CardExpirationYear = c.String(maxLength: 4000), - AllowStoringDirectDebit = c.Boolean(nullable: false), - DirectDebitAccountHolder = c.String(maxLength: 4000), - DirectDebitAccountNumber = c.String(maxLength: 4000), - DirectDebitBankCode = c.String(maxLength: 4000), - DirectDebitBankName = c.String(maxLength: 4000), - DirectDebitBIC = c.String(maxLength: 4000), - DirectDebitCountry = c.String(maxLength: 4000), - DirectDebitIban = c.String(maxLength: 4000), - AuthorizationTransactionId = c.String(maxLength: 4000), - AuthorizationTransactionCode = c.String(maxLength: 4000), - AuthorizationTransactionResult = c.String(maxLength: 4000), - CaptureTransactionId = c.String(maxLength: 4000), - CaptureTransactionResult = c.String(maxLength: 4000), - SubscriptionTransactionId = c.String(maxLength: 4000), - PurchaseOrderNumber = c.String(maxLength: 4000), - PaidDateUtc = c.DateTime(), - ShippingMethod = c.String(maxLength: 4000), - ShippingRateComputationMethodSystemName = c.String(maxLength: 4000), - Deleted = c.Boolean(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - }) + //Id = c.Int(nullable: false, identity: true), + //OrderNumber = c.String(maxLength: 4000), + //OrderGuid = c.Guid(nullable: false), + //StoreId = c.Int(nullable: false), + //CustomerId = c.Int(nullable: false), + //BillingAddressId = c.Int(nullable: false), + //ShippingAddressId = c.Int(), + //OrderStatusId = c.Int(nullable: false), + //ShippingStatusId = c.Int(nullable: false), + //PaymentStatusId = c.Int(nullable: false), + //PaymentMethodSystemName = c.String(maxLength: 4000), + //CustomerCurrencyCode = c.String(maxLength: 4000), + //CurrencyRate = c.Decimal(nullable: false, precision: 18, scale: 8), + //CustomerTaxDisplayTypeId = c.Int(nullable: false), + //VatNumber = c.String(maxLength: 4000), + //OrderSubtotalInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + //OrderSubtotalExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + //OrderSubTotalDiscountInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + //OrderSubTotalDiscountExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + //OrderShippingInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + //OrderShippingExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + //PaymentMethodAdditionalFeeInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + //PaymentMethodAdditionalFeeExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + //TaxRates = c.String(maxLength: 4000), + //OrderTax = c.Decimal(nullable: false, precision: 18, scale: 4), + //OrderDiscount = c.Decimal(nullable: false, precision: 18, scale: 4), + //OrderTotal = c.Decimal(nullable: false, precision: 18, scale: 4), + //RefundedAmount = c.Decimal(nullable: false, precision: 18, scale: 4), + //RewardPointsWereAdded = c.Boolean(nullable: false), + //CheckoutAttributeDescription = c.String(maxLength: 4000), + //CheckoutAttributesXml = c.String(maxLength: 4000), + //CustomerLanguageId = c.Int(nullable: false), + //AffiliateId = c.Int(nullable: false), + //CustomerIp = c.String(maxLength: 4000), + //AllowStoringCreditCardNumber = c.Boolean(nullable: false), + //CardType = c.String(maxLength: 4000), + //CardName = c.String(maxLength: 4000), + //CardNumber = c.String(maxLength: 4000), + //MaskedCreditCardNumber = c.String(maxLength: 4000), + //CardCvv2 = c.String(maxLength: 4000), + //CardExpirationMonth = c.String(maxLength: 4000), + //CardExpirationYear = c.String(maxLength: 4000), + //AllowStoringDirectDebit = c.Boolean(nullable: false), + //DirectDebitAccountHolder = c.String(maxLength: 4000), + //DirectDebitAccountNumber = c.String(maxLength: 4000), + //DirectDebitBankCode = c.String(maxLength: 4000), + //DirectDebitBankName = c.String(maxLength: 4000), + //DirectDebitBIC = c.String(maxLength: 4000), + //DirectDebitCountry = c.String(maxLength: 4000), + //DirectDebitIban = c.String(maxLength: 4000), + //AuthorizationTransactionId = c.String(maxLength: 4000), + //AuthorizationTransactionCode = c.String(maxLength: 4000), + //AuthorizationTransactionResult = c.String(maxLength: 4000), + //CaptureTransactionId = c.String(maxLength: 4000), + //CaptureTransactionResult = c.String(maxLength: 4000), + //SubscriptionTransactionId = c.String(maxLength: 4000), + //PurchaseOrderNumber = c.String(maxLength: 4000), + //PaidDateUtc = c.DateTime(), + //ShippingMethod = c.String(maxLength: 4000), + //ShippingRateComputationMethodSystemName = c.String(maxLength: 4000), + //Deleted = c.Boolean(nullable: false), + //CreatedOnUtc = c.DateTime(nullable: false), + Id = c.Int(nullable: false, identity: true), + OrderNumber = c.String(maxLength: 400), + OrderGuid = c.Guid(nullable: false), + StoreId = c.Int(nullable: false), + CustomerId = c.Int(nullable: false), + BillingAddressId = c.Int(nullable: false), + ShippingAddressId = c.Int(), + OrderStatusId = c.Int(nullable: false), + ShippingStatusId = c.Int(nullable: false), + PaymentStatusId = c.Int(nullable: false), + PaymentMethodSystemName = c.String(maxLength: 400), + CustomerCurrencyCode = c.String(maxLength: 400), + CurrencyRate = c.Decimal(nullable: false, precision: 18, scale: 8), + CustomerTaxDisplayTypeId = c.Int(nullable: false), + VatNumber = c.String(maxLength: 400), + OrderSubtotalInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + OrderSubtotalExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + OrderSubTotalDiscountInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + OrderSubTotalDiscountExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + OrderShippingInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + OrderShippingExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + PaymentMethodAdditionalFeeInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + PaymentMethodAdditionalFeeExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + TaxRates = c.String(maxLength: 400), + OrderTax = c.Decimal(nullable: false, precision: 18, scale: 4), + OrderDiscount = c.Decimal(nullable: false, precision: 18, scale: 4), + OrderTotal = c.Decimal(nullable: false, precision: 18, scale: 4), + RefundedAmount = c.Decimal(nullable: false, precision: 18, scale: 4), + RewardPointsWereAdded = c.Boolean(nullable: false), + CheckoutAttributeDescription = c.String(maxLength: 400), + CheckoutAttributesXml = c.String(maxLength: 400), + CustomerLanguageId = c.Int(nullable: false), + AffiliateId = c.Int(nullable: false), + CustomerIp = c.String(maxLength: 400), + AllowStoringCreditCardNumber = c.Boolean(nullable: false), + CardType = c.String(maxLength: 400), + CardName = c.String(maxLength: 400), + CardNumber = c.String(maxLength: 400), + MaskedCreditCardNumber = c.String(maxLength: 400), + CardCvv2 = c.String(maxLength: 400), + CardExpirationMonth = c.String(maxLength: 400), + CardExpirationYear = c.String(maxLength: 400), + AllowStoringDirectDebit = c.Boolean(nullable: false), + DirectDebitAccountHolder = c.String(maxLength: 400), + DirectDebitAccountNumber = c.String(maxLength: 400), + DirectDebitBankCode = c.String(maxLength: 400), + DirectDebitBankName = c.String(maxLength: 400), + DirectDebitBIC = c.String(maxLength: 400), + DirectDebitCountry = c.String(maxLength: 400), + DirectDebitIban = c.String(maxLength: 400), + AuthorizationTransactionId = c.String(maxLength: 400), + AuthorizationTransactionCode = c.String(maxLength: 400), + AuthorizationTransactionResult = c.String(maxLength: 400), + CaptureTransactionId = c.String(maxLength: 400), + CaptureTransactionResult = c.String(maxLength: 400), + SubscriptionTransactionId = c.String(maxLength: 400), + PurchaseOrderNumber = c.String(maxLength: 400), + PaidDateUtc = c.DateTime(), + ShippingMethod = c.String(maxLength: 400), + ShippingRateComputationMethodSystemName = c.String(maxLength: 400), + Deleted = c.Boolean(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + }) .PrimaryKey(t => t.Id) .ForeignKey("dbo.Address", t => t.BillingAddressId) .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) @@ -792,12 +855,12 @@ public override void Up() GiftCardTypeId = c.Int(nullable: false), Amount = c.Decimal(nullable: false, precision: 18, scale: 4), IsGiftCardActivated = c.Boolean(nullable: false), - GiftCardCouponCode = c.String(maxLength: 4000), - RecipientName = c.String(maxLength: 4000), - RecipientEmail = c.String(maxLength: 4000), - SenderName = c.String(maxLength: 4000), - SenderEmail = c.String(maxLength: 4000), - Message = c.String(maxLength: 4000), + GiftCardCouponCode = c.String(maxLength: 400), + RecipientName = c.String(maxLength: 400), + RecipientEmail = c.String(maxLength: 400), + SenderName = c.String(maxLength: 400), + SenderEmail = c.String(maxLength: 400), + Message = c.String(maxLength: 1000), IsRecipientNotified = c.Boolean(nullable: false), CreatedOnUtc = c.DateTime(nullable: false), }) @@ -978,7 +1041,7 @@ public override void Up() c => new { Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 4000), + Name = c.String(nullable: false, maxLength: 400), DisplayOrder = c.Int(nullable: false), }) .PrimaryKey(t => t.Id); @@ -1043,8 +1106,8 @@ public override void Up() { Id = c.Int(nullable: false, identity: true), Alias = c.String(maxLength: 100), - Name = c.String(nullable: false, maxLength: 4000), - Description = c.String(maxLength: 4000), + Name = c.String(nullable: false, maxLength: 400), + Description = c.String(maxLength: 400), }) .PrimaryKey(t => t.Id); @@ -1055,7 +1118,7 @@ public override void Up() Id = c.Int(nullable: false, identity: true), ProductVariantAttributeId = c.Int(nullable: false), Alias = c.String(maxLength: 100), - Name = c.String(maxLength: 4000), + Name = c.String(maxLength: 400), ColorSquaresRgb = c.String(maxLength: 100), PriceAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), WeightAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), @@ -1076,11 +1139,11 @@ public override void Up() Id = c.Int(nullable: false, identity: true), DownloadGuid = c.Guid(nullable: false), UseDownloadUrl = c.Boolean(nullable: false), - DownloadUrl = c.String(maxLength: 4000), + DownloadUrl = c.String(maxLength: 400), DownloadBinary = c.Binary(), - ContentType = c.String(maxLength: 4000), + ContentType = c.String(maxLength: 400), Filename = c.String(maxLength: 4000), - Extension = c.String(maxLength: 4000), + Extension = c.String(maxLength: 400), IsNew = c.Boolean(nullable: false), }) .PrimaryKey(t => t.Id); @@ -1588,18 +1651,18 @@ public override void Up() c => new { Id = c.Int(nullable: false, identity: true), - SystemName = c.String(maxLength: 4000), + SystemName = c.String(maxLength: 400), IncludeInSitemap = c.Boolean(nullable: false), IsPasswordProtected = c.Boolean(nullable: false), - Password = c.String(maxLength: 4000), - Title = c.String(maxLength: 4000), + Password = c.String(maxLength: 400), + Title = c.String(maxLength: 400), Body = c.String(maxLength: 4000), - MetaKeywords = c.String(maxLength: 4000), - MetaDescription = c.String(maxLength: 4000), - MetaTitle = c.String(maxLength: 4000), + MetaKeywords = c.String(maxLength: 400), + MetaDescription = c.String(maxLength: 400), + MetaTitle = c.String(maxLength: 400), LimitedToStores = c.Boolean(nullable: false), RenderAsWidget = c.Boolean(nullable: false), - WidgetZone = c.String(maxLength: 4000), + WidgetZone = c.String(maxLength: 400), WidgetShowTitle = c.Boolean(nullable: false), WidgetBordered = c.Boolean(nullable: false), Priority = c.Int(nullable: false), @@ -1774,14 +1837,14 @@ public override void Up() #endregion #region Custom - - this.SqlFileOrResource("Indexes.sql"); - if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) - { - // do not execute in unit tests - this.SqlFileOrResource("Indexes.SqlServer.sql"); - this.SqlFileOrResource("StoredProcedures.sql"); - } + // MOD: + //this.SqlFileOrResource("Indexes.sql"); + //if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) + //{ + // // do not execute in unit tests + // this.SqlFileOrResource("Indexes.SqlServer.sql"); + // this.SqlFileOrResource("StoredProcedures.sql"); + //} #endregion } diff --git a/src/Libraries/SmartStore.Data/Migrations/201502202248081_NewIndexesV22.cs b/src/Libraries/SmartStore.Data/Migrations/201502202248081_NewIndexesV22.cs index 3a5409115b..e1ed9a5190 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201502202248081_NewIndexesV22.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201502202248081_NewIndexesV22.cs @@ -10,14 +10,14 @@ public override void Up() DropIndex("dbo.Product_ProductAttribute_Mapping", new[] { "ProductId" }); DropIndex("dbo.ProductVariantAttributeValue", new[] { "ProductVariantAttributeId" }); CreateIndex("dbo.Product_ProductAttribute_Mapping", new[] { "ProductId", "DisplayOrder" }, name: "IX_Product_ProductAttribute_Mapping_ProductId_DisplayOrder"); - CreateIndex("dbo.ProductVariantAttributeValue", new[] { "ProductVariantAttributeId", "DisplayOrder" }, name: "IX_ProductVariantAttributeValue_ProductVariantAttributeId_DisplayOrder"); + CreateIndex("dbo.ProductVariantAttributeValue", new[] { "ProductVariantAttributeId", "DisplayOrder" }, name: "IX_ProductVAV_ProductVAId_DO"); CreateIndex("dbo.NewsLetterSubscription", new[] { "Email", "StoreId" }, name: "IX_NewsletterSubscription_Email_StoreId"); } public override void Down() { DropIndex("dbo.NewsLetterSubscription", "IX_NewsletterSubscription_Email_StoreId"); - DropIndex("dbo.ProductVariantAttributeValue", "IX_ProductVariantAttributeValue_ProductVariantAttributeId_DisplayOrder"); + DropIndex("dbo.ProductVariantAttributeValue", "xx"); DropIndex("dbo.Product_ProductAttribute_Mapping", "IX_Product_ProductAttribute_Mapping_ProductId_DisplayOrder"); CreateIndex("dbo.ProductVariantAttributeValue", "ProductVariantAttributeId"); CreateIndex("dbo.Product_ProductAttribute_Mapping", "ProductId"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.cs b/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.cs index 7f4f7300a3..5742652cfe 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.cs @@ -13,9 +13,9 @@ public override void Up() c => new { Id = c.Int(nullable: false, identity: true), - PaymentMethodSystemName = c.String(nullable: false, maxLength: 4000), + PaymentMethodSystemName = c.String(nullable: false, maxLength: 400), ExcludedCustomerRoleIds = c.String(maxLength: 500), - ExcludedCountryIds = c.String(maxLength: 2000), + ExcludedCountryIds = c.String(maxLength: 200), ExcludedShippingMethodIds = c.String(maxLength: 500), CountryExclusionContextId = c.Int(nullable: false), MinimumOrderAmount = c.Decimal(precision: 18, scale: 4), diff --git a/src/Libraries/SmartStore.Data/Migrations/201507141647299_CustomerTablePerf.cs b/src/Libraries/SmartStore.Data/Migrations/201507141647299_CustomerTablePerf.cs index 982225727d..fe6f088766 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201507141647299_CustomerTablePerf.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201507141647299_CustomerTablePerf.cs @@ -8,8 +8,8 @@ public partial class CustomerTablePerf : DbMigration public override void Up() { // without dropping the indexes we cannot adjust column lengths - DropIndex("dbo.Customer", "IX_Customer_Email"); - DropIndex("dbo.Customer", "IX_Customer_Username"); + //DropIndex("dbo.Customer", "IX_Customer_Email"); + //DropIndex("dbo.Customer", "IX_Customer_Username"); AlterColumn("dbo.Customer", "Username", c => c.String(maxLength: 500)); AlterColumn("dbo.Customer", "Email", c => c.String(maxLength: 500)); diff --git a/src/Libraries/SmartStore.Data/Migrations/201508142203054_CronExpressions.cs b/src/Libraries/SmartStore.Data/Migrations/201508142203054_CronExpressions.cs index 9f12663ade..baa7a1cf16 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201508142203054_CronExpressions.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201508142203054_CronExpressions.cs @@ -13,7 +13,8 @@ public partial class CronExpressions : DbMigration, ILocaleResourcesProvider, ID public override void Up() { AddColumn("dbo.ScheduleTask", "CronExpression", c => c.String(maxLength: 1000, defaultValue: "0 */1 * * *" /* Every hour */)); - AddColumn("dbo.ScheduleTask", "RowVersion", c => c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion")); + //AddColumn("dbo.ScheduleTask", "RowVersion", c => c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion")); + AddColumn("dbo.ScheduleTask", "RowVersion", c => c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "timestamp")); DropColumn("dbo.ScheduleTask", "Seconds"); } diff --git a/src/Libraries/SmartStore.Data/Migrations/201609201852449_log4net.cs b/src/Libraries/SmartStore.Data/Migrations/201609201852449_log4net.cs index 5eda541bc0..afcd557345 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201609201852449_log4net.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201609201852449_log4net.cs @@ -18,6 +18,10 @@ public override void Up() { Sql("IF EXISTS (SELECT * FROM sys.indexes WHERE name='IX_Log_ContentHash' AND object_id = OBJECT_ID('[dbo].[Log]')) DROP INDEX [IX_Log_ContentHash] ON [dbo].[Log];"); Sql(@"TRUNCATE Table [Log]"); + } + else if (DataSettings.Current.IsMySqlServer) + { + } else { diff --git a/src/Libraries/SmartStore.Data/Migrations/201705281903241_MoreIndexes.cs b/src/Libraries/SmartStore.Data/Migrations/201705281903241_MoreIndexes.cs index 1cd8f0c3dd..7b27fbf515 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201705281903241_MoreIndexes.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201705281903241_MoreIndexes.cs @@ -13,11 +13,15 @@ public override void Up() // Avoid "Column 'Name' in table 'dbo.ProductVariantAttributeValue' is of a type that is invalid for use as a key column in an index". Sql("If -1 = (SELECT CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'ProductVariantAttributeValue' AND COLUMN_NAME = 'Name') ALTER TABLE [dbo].[ProductVariantAttributeValue] ALTER COLUMN [Name] nvarchar(4000) NULL;"); } - - CreateIndex("dbo.Product_Category_Mapping", "IsFeaturedProduct"); + else if (HostingEnvironment.IsHosted && DataSettings.Current.IsMySqlServer) + { + // Avoid "Column 'Name' in table 'dbo.ProductVariantAttributeValue' is of a type that is invalid for use as a key column in an index". + //Sql("If -1 = (SELECT CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'ProductVariantAttributeValue' AND COLUMN_NAME = 'Name') ALTER TABLE [dbo].[ProductVariantAttributeValue] ALTER COLUMN [Name] nvarchar(4000) NULL;"); + } + CreateIndex("dbo.Product_Category_Mapping", "IsFeaturedProduct"); CreateIndex("dbo.Product_Manufacturer_Mapping", "IsFeaturedProduct"); CreateIndex("dbo.SpecificationAttribute", "AllowFiltering"); - CreateIndex("dbo.Product_ProductAttribute_Mapping", "AttributeControlTypeId"); + //CreateIndex("dbo.ProductVariantAttributeValue", "AttributeControlTypeId"); CreateIndex("dbo.ProductAttribute", "AllowFiltering"); CreateIndex("dbo.ProductVariantAttributeValue", "Name"); CreateIndex("dbo.ProductVariantAttributeValue", "ValueTypeId"); @@ -28,7 +32,7 @@ public override void Down() DropIndex("dbo.ProductVariantAttributeValue", new[] { "ValueTypeId" }); DropIndex("dbo.ProductVariantAttributeValue", new[] { "Name" }); DropIndex("dbo.ProductAttribute", new[] { "AllowFiltering" }); - DropIndex("dbo.Product_ProductAttribute_Mapping", new[] { "AttributeControlTypeId" }); + DropIndex("dbo.ProductVariantAttributeValue", new[] { "AttributeControlTypeId" }); DropIndex("dbo.SpecificationAttribute", new[] { "AllowFiltering" }); DropIndex("dbo.Product_Manufacturer_Mapping", new[] { "IsFeaturedProduct" }); DropIndex("dbo.Product_Category_Mapping", new[] { "IsFeaturedProduct" }); diff --git a/src/Libraries/SmartStore.Data/Migrations/201802081830029_ShippingMethodMultistore.cs b/src/Libraries/SmartStore.Data/Migrations/201802081830029_ShippingMethodMultistore.cs index 92e74e27ac..071c681c0d 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201802081830029_ShippingMethodMultistore.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201802081830029_ShippingMethodMultistore.cs @@ -7,25 +7,25 @@ public partial class ShippingMethodMultistore : DbMigration { public override void Up() { - DropForeignKey("dbo.ShippingMethodRestrictions", "ShippingMethod_Id", "dbo.ShippingMethod"); - DropForeignKey("dbo.ShippingMethodRestrictions", "Country_Id", "dbo.Country"); - DropIndex("dbo.ShippingMethodRestrictions", new[] { "ShippingMethod_Id" }); - DropIndex("dbo.ShippingMethodRestrictions", new[] { "Country_Id" }); + //DropForeignKey("dbo.ShippingMethodRestrictions", "ShippingMethod_Id", "dbo.ShippingMethod"); + //DropForeignKey("dbo.ShippingMethodRestrictions", "Country_Id", "dbo.Country"); + //DropIndex("dbo.ShippingMethodRestrictions", new[] { "ShippingMethod_Id" }); + //DropIndex("dbo.ShippingMethodRestrictions", new[] { "Country_Id" }); AddColumn("dbo.ShippingMethod", "LimitedToStores", c => c.Boolean(nullable: false)); AddColumn("dbo.PaymentMethod", "LimitedToStores", c => c.Boolean(nullable: false)); - DropTable("dbo.ShippingMethodRestrictions"); + //DropTable("dbo.ShippingMethodRestrictions"); } public override void Down() { - CreateTable( - "dbo.ShippingMethodRestrictions", - c => new - { - ShippingMethod_Id = c.Int(nullable: false), - Country_Id = c.Int(nullable: false), - }) - .PrimaryKey(t => new { t.ShippingMethod_Id, t.Country_Id }); + //CreateTable( + // "dbo.ShippingMethodRestrictions", + // c => new + // { + // ShippingMethod_Id = c.Int(nullable: false), + // Country_Id = c.Int(nullable: false), + // }) + // .PrimaryKey(t => new { t.ShippingMethod_Id, t.Country_Id }); DropColumn("dbo.PaymentMethod", "LimitedToStores"); DropColumn("dbo.ShippingMethod", "LimitedToStores"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201806231547270_ScheduleTaskHistory.cs b/src/Libraries/SmartStore.Data/Migrations/201806231547270_ScheduleTaskHistory.cs index f2d93452c9..578cff3448 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201806231547270_ScheduleTaskHistory.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201806231547270_ScheduleTaskHistory.cs @@ -43,7 +43,8 @@ public override void Up() public override void Down() { - AddColumn("dbo.ScheduleTask", "RowVersion", c => c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion")); + //AddColumn("dbo.ScheduleTask", "RowVersion", c => c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion")); + AddColumn("dbo.ScheduleTask", "RowVersion", c => c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "timestamp")); AddColumn("dbo.ScheduleTask", "ProgressMessage", c => c.String(maxLength: 1000)); AddColumn("dbo.ScheduleTask", "ProgressPercent", c => c.Int()); AddColumn("dbo.ScheduleTask", "LastError", c => c.String(maxLength: 1000)); diff --git a/src/Libraries/SmartStore.Data/Migrations/201811061745204_IsSystemProductIndex.cs b/src/Libraries/SmartStore.Data/Migrations/201811061745204_IsSystemProductIndex.cs index a51d10dc8f..af7726a743 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201811061745204_IsSystemProductIndex.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201811061745204_IsSystemProductIndex.cs @@ -1,29 +1,41 @@ namespace SmartStore.Data.Migrations { + using SmartStore.Core.Data; using System; using System.Data.Entity.Migrations; - using SmartStore.Core.Data; + using System.Web.Hosting; public partial class IsSystemProductIndex : DbMigration { public override void Up() { - DropIndex("dbo.Product", "IX_Product_Deleted_and_Published"); - if (DataSettings.Current.IsSqlServer) + if(HostingEnvironment.IsHosted && DataSettings.Current.IsMySqlServer) + { + + } + else { - RenameIndex(table: "dbo.Product", name: "Product_SystemName_IsSystemProduct", newName: "IX_Product_SystemName_IsSystemProduct"); + DropIndex("dbo.Product", "IX_Product_Deleted_and_Published"); + //RenameIndex(table: "dbo.Product", name: "Product_SystemName_IsSystemProduct", newName: "IX_Product_SystemName_IsSystemProduct"); } + DropIndex("dbo.Product", "Product_SystemName_IsSystemProduct"); + CreateIndex("dbo.Product", new[] { "SystemName", "IsSystemProduct" }, name: "IX_Product_SystemName_IsSystemProduct"); CreateIndex("dbo.Product", new[] { "Published", "Deleted", "IsSystemProduct" }, name: "IX_Product_Published_Deleted_IsSystemProduct"); } public override void Down() { + DropIndex("dbo.Product", "IX_Product_SystemName_IsSystemProduct"); + CreateIndex("dbo.Product", new[] { "SystemName", "IsSystemProduct" }, name: "Product_SystemName_IsSystemProduct"); DropIndex("dbo.Product", "IX_Product_Published_Deleted_IsSystemProduct"); - if (DataSettings.Current.IsSqlServer) + if (HostingEnvironment.IsHosted && DataSettings.Current.IsMySqlServer) + { + + } + else { - RenameIndex(table: "dbo.Product", name: "IX_Product_SystemName_IsSystemProduct", newName: "Product_SystemName_IsSystemProduct"); + CreateIndex("dbo.Product", new[] { "Published", "Deleted" }, name: "IX_Product_Deleted_and_Published"); } - CreateIndex("dbo.Product", new[] { "Published", "Deleted" }, name: "IX_Product_Deleted_and_Published"); } } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201812212031185_UpdateToMySQL.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201812212031185_UpdateToMySQL.Designer.cs new file mode 100644 index 0000000000..867d9c11c7 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201812212031185_UpdateToMySQL.Designer.cs @@ -0,0 +1,29 @@ +// +namespace SmartStore.Data.Migrations +{ + using System.CodeDom.Compiler; + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Infrastructure; + using System.Resources; + + [GeneratedCode("EntityFramework.Migrations", "6.2.0-61023")] + public sealed partial class UpdateToMySQL : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(UpdateToMySQL)); + + string IMigrationMetadata.Id + { + get { return "201812212031185_UpdateToMySQL"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201812212031185_UpdateToMySQL.cs b/src/Libraries/SmartStore.Data/Migrations/201812212031185_UpdateToMySQL.cs new file mode 100644 index 0000000000..37549fe20c --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201812212031185_UpdateToMySQL.cs @@ -0,0 +1,534 @@ +namespace SmartStore.Data.Migrations +{ + using System; + using System.Data.Entity.Migrations; + + public partial class UpdateToMySQL : DbMigration + { + public override void Up() + { + DropIndex("dbo.Customer", "IX_Customer_CreatedOn"); + DropIndex("dbo.Customer", "IX_Customer_LastActivity"); + DropIndex("dbo.Customer", "IX_Customer_BirthDate"); + DropIndex("dbo.Picture", "IX_UpdatedOn_IsTransient"); + //DropIndex("dbo.ProductVariantAttributeValue", "IX_ProductVariantAttributeValue_ProductVariantAttributeId_DisplayOrder"); not found + DropIndex("dbo.ProductVariantAttributeValue", new[] { "Name" }); + DropIndex("dbo.Download", "IX_UpdatedOn_IsTransient"); + DropIndex("dbo.Forums_Post", new[] { "CreatedOnUtc" }); + DropIndex("dbo.Forums_Topic", new[] { "TopicTypeId", "LastPostTime" }); + DropIndex("dbo.Forums_Topic", new[] { "CreatedOnUtc" }); + DropIndex("dbo.WalletHistory", "IX_StoreId_CreatedOn"); + DropIndex("dbo.ScheduleTaskHistory", "IX_Started_Finished"); + DropIndex("dbo.ScheduleTask", "IX_NextRun_Enabled"); + AlterColumn("dbo.Topic", "SystemName", c => c.String(unicode: false)); + AlterColumn("dbo.Topic", "Password", c => c.String(unicode: false)); + AlterColumn("dbo.Topic", "Title", c => c.String(unicode: false)); + AlterColumn("dbo.Topic", "Body", c => c.String(unicode: false)); + AlterColumn("dbo.Topic", "MetaKeywords", c => c.String(unicode: false)); + AlterColumn("dbo.Topic", "MetaDescription", c => c.String(unicode: false)); + AlterColumn("dbo.Topic", "MetaTitle", c => c.String(unicode: false)); + AlterColumn("dbo.Topic", "WidgetZone", c => c.String(unicode: false)); + AlterColumn("dbo.Topic", "TitleTag", c => c.String(unicode: false)); + AlterColumn("dbo.Setting", "Value", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.LocalizedProperty", "LocaleValue", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.LocaleStringResource", "ResourceValue", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.PermissionRecord", "Name", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.Poll", "Name", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.Poll", "SystemKeyword", c => c.String(unicode: false)); + AlterColumn("dbo.Poll", "StartDateUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Poll", "EndDateUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.PollAnswer", "Name", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.CustomerContent", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.CustomerContent", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.BlogComment", "CommentText", c => c.String(unicode: false)); + AlterColumn("dbo.ProductReview", "Title", c => c.String(unicode: false)); + AlterColumn("dbo.ProductReview", "ReviewText", c => c.String(unicode: false)); + AlterColumn("dbo.NewsComment", "CommentTitle", c => c.String(unicode: false)); + AlterColumn("dbo.NewsComment", "CommentText", c => c.String(unicode: false)); + AlterColumn("dbo.Customer", "AdminComment", c => c.String(unicode: false)); + AlterColumn("dbo.Customer", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Customer", "LastLoginDateUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Customer", "LastActivityDateUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Customer", "BirthDate", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Address", "Salutation", c => c.String(unicode: false)); + AlterColumn("dbo.Address", "Title", c => c.String(unicode: false)); + AlterColumn("dbo.Address", "FirstName", c => c.String(unicode: false)); + AlterColumn("dbo.Address", "LastName", c => c.String(unicode: false)); + AlterColumn("dbo.Address", "Email", c => c.String(unicode: false)); + AlterColumn("dbo.Address", "Company", c => c.String(unicode: false)); + AlterColumn("dbo.Address", "City", c => c.String(unicode: false)); + AlterColumn("dbo.Address", "Address1", c => c.String(unicode: false)); + AlterColumn("dbo.Address", "Address2", c => c.String(unicode: false)); + AlterColumn("dbo.Address", "ZipPostalCode", c => c.String(unicode: false)); + AlterColumn("dbo.Address", "PhoneNumber", c => c.String(unicode: false)); + AlterColumn("dbo.Address", "FaxNumber", c => c.String(unicode: false)); + AlterColumn("dbo.Address", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Country", "AddressFormat", c => c.String(unicode: false)); + AlterColumn("dbo.BlogPost", "Title", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.BlogPost", "Body", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.BlogPost", "Tags", c => c.String(unicode: false)); + AlterColumn("dbo.BlogPost", "StartDateUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.BlogPost", "EndDateUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.BlogPost", "MetaDescription", c => c.String(unicode: false)); + AlterColumn("dbo.BlogPost", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Product", "ShortDescription", c => c.String(unicode: false)); + AlterColumn("dbo.Product", "FullDescription", c => c.String(unicode: false)); + AlterColumn("dbo.Product", "AdminComment", c => c.String(unicode: false)); + AlterColumn("dbo.Product", "MetaDescription", c => c.String(unicode: false)); + AlterColumn("dbo.Product", "UserAgreementText", c => c.String(unicode: false)); + AlterColumn("dbo.Product", "SpecialPriceStartDateTimeUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Product", "SpecialPriceEndDateTimeUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Product", "AvailableStartDateTimeUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Product", "AvailableEndDateTimeUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Product", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Product", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Discount", "StartDateUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Discount", "EndDateUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Category", "Description", c => c.String(unicode: false)); + AlterColumn("dbo.Category", "BottomDescription", c => c.String(unicode: false)); + AlterColumn("dbo.Category", "BadgeText", c => c.String(unicode: false)); + AlterColumn("dbo.Category", "MetaDescription", c => c.String(unicode: false)); + AlterColumn("dbo.Category", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Category", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Category", "DefaultViewMode", c => c.String(unicode: false)); + AlterColumn("dbo.Picture", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Manufacturer", "Description", c => c.String(unicode: false)); + AlterColumn("dbo.Manufacturer", "MetaDescription", c => c.String(unicode: false)); + AlterColumn("dbo.Manufacturer", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Manufacturer", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.DiscountRequirement", "DiscountRequirementRuleSystemName", c => c.String(unicode: false)); + AlterColumn("dbo.DiscountRequirement", "RestrictedProductIds", c => c.String(unicode: false)); + AlterColumn("dbo.DiscountRequirement", "RestrictedPaymentMethods", c => c.String(unicode: false)); + AlterColumn("dbo.DiscountRequirement", "RestrictedShippingOptions", c => c.String(unicode: false)); + AlterColumn("dbo.DiscountRequirement", "ExtraData", c => c.String(unicode: false)); + AlterColumn("dbo.ProductBundleItem", "ShortDescription", c => c.String(unicode: false)); + AlterColumn("dbo.ProductBundleItem", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.ProductBundleItem", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.SpecificationAttributeOption", "Name", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.SpecificationAttribute", "Name", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.ProductVariantAttributeCombination", "AttributesXml", c => c.String(unicode: false)); + AlterColumn("dbo.Product_ProductAttribute_Mapping", "TextPrompt", c => c.String(unicode: false)); + AlterColumn("dbo.ProductAttribute", "Name", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.ProductAttribute", "Description", c => c.String(unicode: false)); + AlterColumn("dbo.ProductAttribute", "ExportMappings", c => c.String(unicode: false)); + AlterColumn("dbo.ProductVariantAttributeValue", "Name", c => c.String(unicode: false)); + AlterColumn("dbo.Download", "DownloadUrl", c => c.String(unicode: false)); + AlterColumn("dbo.Download", "ContentType", c => c.String(unicode: false)); + AlterColumn("dbo.Download", "Filename", c => c.String(unicode: false)); + AlterColumn("dbo.Download", "Extension", c => c.String(unicode: false)); + AlterColumn("dbo.Download", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Download", "Changelog", c => c.String(unicode: false)); + AlterColumn("dbo.Forums_Post", "Text", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.Forums_Post", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Forums_Post", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Forums_Topic", "LastPostTime", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Forums_Topic", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Forums_Topic", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Forums_Forum", "Description", c => c.String(unicode: false)); + AlterColumn("dbo.Forums_Forum", "LastPostTime", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Forums_Forum", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Forums_Forum", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Forums_Group", "Description", c => c.String(unicode: false)); + AlterColumn("dbo.Forums_Group", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Forums_Group", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.News", "Title", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.News", "Short", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.News", "Full", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.News", "StartDateUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.News", "EndDateUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.News", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.News", "MetaDescription", c => c.String(unicode: false)); + AlterColumn("dbo.ExternalAuthenticationRecord", "Email", c => c.String(unicode: false)); + AlterColumn("dbo.ExternalAuthenticationRecord", "ExternalIdentifier", c => c.String(unicode: false)); + AlterColumn("dbo.ExternalAuthenticationRecord", "ExternalDisplayIdentifier", c => c.String(unicode: false)); + AlterColumn("dbo.ExternalAuthenticationRecord", "OAuthToken", c => c.String(unicode: false)); + AlterColumn("dbo.ExternalAuthenticationRecord", "OAuthAccessToken", c => c.String(unicode: false)); + AlterColumn("dbo.ExternalAuthenticationRecord", "ProviderSystemName", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "OrderNumber", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "PaymentMethodSystemName", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CustomerCurrencyCode", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "VatNumber", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "TaxRates", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CheckoutAttributeDescription", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CheckoutAttributesXml", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CustomerIp", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CardType", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CardName", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CardNumber", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "MaskedCreditCardNumber", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CardCvv2", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CardExpirationMonth", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CardExpirationYear", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "DirectDebitAccountHolder", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "DirectDebitAccountNumber", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "DirectDebitBankCode", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "DirectDebitBankName", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "DirectDebitBIC", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "DirectDebitCountry", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "DirectDebitIban", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CustomerOrderComment", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "AuthorizationTransactionId", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "AuthorizationTransactionCode", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "AuthorizationTransactionResult", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CaptureTransactionId", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CaptureTransactionResult", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "SubscriptionTransactionId", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "PurchaseOrderNumber", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "PaidDateUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Order", "ShippingMethod", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "ShippingRateComputationMethodSystemName", c => c.String(unicode: false)); + AlterColumn("dbo.Order", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Order", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.DiscountUsageHistory", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.GiftCardUsageHistory", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.GiftCard", "GiftCardCouponCode", c => c.String(unicode: false)); + AlterColumn("dbo.GiftCard", "RecipientName", c => c.String(unicode: false)); + AlterColumn("dbo.GiftCard", "RecipientEmail", c => c.String(unicode: false)); + AlterColumn("dbo.GiftCard", "SenderName", c => c.String(unicode: false)); + AlterColumn("dbo.GiftCard", "SenderEmail", c => c.String(unicode: false)); + AlterColumn("dbo.GiftCard", "Message", c => c.String(unicode: false)); + AlterColumn("dbo.GiftCard", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.OrderItem", "AttributeDescription", c => c.String(unicode: false)); + AlterColumn("dbo.OrderItem", "AttributesXml", c => c.String(unicode: false)); + AlterColumn("dbo.OrderItem", "BundleData", c => c.String(unicode: false)); + AlterColumn("dbo.OrderNote", "Note", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.OrderNote", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.RewardPointsHistory", "Message", c => c.String(unicode: false)); + AlterColumn("dbo.RewardPointsHistory", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Shipment", "TrackingNumber", c => c.String(unicode: false)); + AlterColumn("dbo.Shipment", "ShippedDateUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Shipment", "DeliveryDateUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Shipment", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.WalletHistory", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.ReturnRequest", "ReasonForReturn", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.ReturnRequest", "RequestedAction", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.ReturnRequest", "RequestedActionUpdatedOnUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.ReturnRequest", "CustomerComments", c => c.String(unicode: false)); + AlterColumn("dbo.ReturnRequest", "StaffNotes", c => c.String(unicode: false)); + AlterColumn("dbo.ReturnRequest", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.ReturnRequest", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.ShoppingCartItem", "AttributesXml", c => c.String(unicode: false)); + AlterColumn("dbo.ShoppingCartItem", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.ShoppingCartItem", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.CheckoutAttribute", "TextPrompt", c => c.String(unicode: false)); + AlterColumn("dbo.RecurringPaymentHistory", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.RecurringPayment", "StartDateUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.RecurringPayment", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.ActivityLog", "Comment", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.ActivityLog", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Log", "FullMessage", c => c.String(unicode: false)); + AlterColumn("dbo.Log", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.ShippingMethod", "Description", c => c.String(unicode: false)); + AlterColumn("dbo.GenericAttribute", "Value", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.Store", "HtmlBodyId", c => c.String(unicode: false)); + AlterColumn("dbo.Currency", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Currency", "UpdatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.ScheduleTaskHistory", "StartedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.ScheduleTaskHistory", "FinishedOnUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.ScheduleTaskHistory", "SucceededOnUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.ScheduleTask", "NextRunUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.QueuedEmail", "Body", c => c.String(unicode: false)); + AlterColumn("dbo.QueuedEmail", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.QueuedEmail", "SentOnUtc", c => c.DateTime(precision: 0)); + AlterColumn("dbo.Campaign", "Name", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.Campaign", "Subject", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.Campaign", "Body", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.Campaign", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.MessageTemplate", "LastModelTree", c => c.String(unicode: false)); + AlterColumn("dbo.MessageTemplate", "Body", c => c.String(unicode: false)); + AlterColumn("dbo.NewsLetterSubscription", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Forums_Subscription", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.Forums_PrivateMessage", "Text", c => c.String(nullable: false, unicode: false)); + AlterColumn("dbo.Forums_PrivateMessage", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.ImportProfile", "FileTypeConfiguration", c => c.String(unicode: false)); + AlterColumn("dbo.ImportProfile", "ExtraData", c => c.String(unicode: false)); + AlterColumn("dbo.ImportProfile", "ColumnMapping", c => c.String(unicode: false)); + AlterColumn("dbo.ImportProfile", "ResultInfo", c => c.String(unicode: false)); + AlterColumn("dbo.SyncMapping", "CustomString", c => c.String(unicode: false)); + AlterColumn("dbo.SyncMapping", "SyncedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + AlterColumn("dbo.ExportDeployment", "ResultInfo", c => c.String(unicode: false)); + AlterColumn("dbo.ExportProfile", "Filtering", c => c.String(unicode: false)); + AlterColumn("dbo.ExportProfile", "Projection", c => c.String(unicode: false)); + AlterColumn("dbo.ExportProfile", "ProviderConfigData", c => c.String(unicode: false)); + AlterColumn("dbo.ExportProfile", "ResultInfo", c => c.String(unicode: false)); + AlterColumn("dbo.BackInStockSubscription", "CreatedOnUtc", c => c.DateTime(nullable: false, precision: 0)); + CreateIndex("dbo.Customer", "CreatedOnUtc", name: "IX_Customer_CreatedOn"); + CreateIndex("dbo.Customer", "LastActivityDateUtc", name: "IX_Customer_LastActivity"); + CreateIndex("dbo.Customer", "BirthDate", name: "IX_Customer_BirthDate"); + CreateIndex("dbo.Picture", new[] { "UpdatedOnUtc", "IsTransient" }, name: "IX_UpdatedOn_IsTransient"); + // CreateIndex("dbo.ProductVariantAttributeValue", "ProductVariantAttributeId", name: "IX_ProductVAV_ProductVAId_DO"); duplicate key + //CreateIndex("dbo.ProductVariantAttributeValue", "Name"); cont index long text + CreateIndex("dbo.ProductVariantAttributeValue", "DisplayOrder", name: "xxx"); + CreateIndex("dbo.Download", new[] { "UpdatedOnUtc", "IsTransient" }, name: "IX_UpdatedOn_IsTransient"); + CreateIndex("dbo.Forums_Post", "CreatedOnUtc"); + CreateIndex("dbo.Forums_Topic", new[] { "TopicTypeId", "LastPostTime" }); + CreateIndex("dbo.Forums_Topic", "CreatedOnUtc"); + CreateIndex("dbo.WalletHistory", new[] { "StoreId", "CreatedOnUtc" }, name: "IX_StoreId_CreatedOn"); + CreateIndex("dbo.ScheduleTaskHistory", new[] { "StartedOnUtc", "FinishedOnUtc" }, name: "IX_Started_Finished"); + CreateIndex("dbo.ScheduleTask", new[] { "NextRunUtc", "Enabled" }, name: "IX_NextRun_Enabled"); + } + + public override void Down() + { + DropIndex("dbo.ScheduleTask", "IX_NextRun_Enabled"); + DropIndex("dbo.ScheduleTaskHistory", "IX_Started_Finished"); + DropIndex("dbo.WalletHistory", "IX_StoreId_CreatedOn"); + DropIndex("dbo.Forums_Topic", new[] { "CreatedOnUtc" }); + DropIndex("dbo.Forums_Topic", new[] { "TopicTypeId", "LastPostTime" }); + DropIndex("dbo.Forums_Post", new[] { "CreatedOnUtc" }); + DropIndex("dbo.Download", "IX_UpdatedOn_IsTransient"); + DropIndex("dbo.ProductVariantAttributeValue", "xxx"); + DropIndex("dbo.ProductVariantAttributeValue", new[] { "Name" }); + DropIndex("dbo.ProductVariantAttributeValue", "IX_ProductVAV_ProductVAId_DO"); + DropIndex("dbo.Picture", "IX_UpdatedOn_IsTransient"); + DropIndex("dbo.Customer", "IX_Customer_BirthDate"); + DropIndex("dbo.Customer", "IX_Customer_LastActivity"); + DropIndex("dbo.Customer", "IX_Customer_CreatedOn"); + AlterColumn("dbo.BackInStockSubscription", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.ExportProfile", "ResultInfo", c => c.String()); + AlterColumn("dbo.ExportProfile", "ProviderConfigData", c => c.String()); + AlterColumn("dbo.ExportProfile", "Projection", c => c.String()); + AlterColumn("dbo.ExportProfile", "Filtering", c => c.String()); + AlterColumn("dbo.ExportDeployment", "ResultInfo", c => c.String()); + AlterColumn("dbo.SyncMapping", "SyncedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.SyncMapping", "CustomString", c => c.String()); + AlterColumn("dbo.ImportProfile", "ResultInfo", c => c.String()); + AlterColumn("dbo.ImportProfile", "ColumnMapping", c => c.String()); + AlterColumn("dbo.ImportProfile", "ExtraData", c => c.String()); + AlterColumn("dbo.ImportProfile", "FileTypeConfiguration", c => c.String()); + AlterColumn("dbo.Forums_PrivateMessage", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Forums_PrivateMessage", "Text", c => c.String(nullable: false)); + AlterColumn("dbo.Forums_Subscription", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.NewsLetterSubscription", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.MessageTemplate", "Body", c => c.String()); + AlterColumn("dbo.MessageTemplate", "LastModelTree", c => c.String()); + AlterColumn("dbo.Campaign", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Campaign", "Body", c => c.String(nullable: false)); + AlterColumn("dbo.Campaign", "Subject", c => c.String(nullable: false)); + AlterColumn("dbo.Campaign", "Name", c => c.String(nullable: false)); + AlterColumn("dbo.QueuedEmail", "SentOnUtc", c => c.DateTime()); + AlterColumn("dbo.QueuedEmail", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.QueuedEmail", "Body", c => c.String()); + AlterColumn("dbo.ScheduleTask", "NextRunUtc", c => c.DateTime()); + AlterColumn("dbo.ScheduleTaskHistory", "SucceededOnUtc", c => c.DateTime()); + AlterColumn("dbo.ScheduleTaskHistory", "FinishedOnUtc", c => c.DateTime()); + AlterColumn("dbo.ScheduleTaskHistory", "StartedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Currency", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Currency", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Store", "HtmlBodyId", c => c.String()); + AlterColumn("dbo.GenericAttribute", "Value", c => c.String(nullable: false)); + AlterColumn("dbo.ShippingMethod", "Description", c => c.String()); + AlterColumn("dbo.Log", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Log", "FullMessage", c => c.String()); + AlterColumn("dbo.ActivityLog", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.ActivityLog", "Comment", c => c.String(nullable: false)); + AlterColumn("dbo.RecurringPayment", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.RecurringPayment", "StartDateUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.RecurringPaymentHistory", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.CheckoutAttribute", "TextPrompt", c => c.String()); + AlterColumn("dbo.ShoppingCartItem", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.ShoppingCartItem", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.ShoppingCartItem", "AttributesXml", c => c.String()); + AlterColumn("dbo.ReturnRequest", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.ReturnRequest", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.ReturnRequest", "StaffNotes", c => c.String()); + AlterColumn("dbo.ReturnRequest", "CustomerComments", c => c.String()); + AlterColumn("dbo.ReturnRequest", "RequestedActionUpdatedOnUtc", c => c.DateTime()); + AlterColumn("dbo.ReturnRequest", "RequestedAction", c => c.String(nullable: false)); + AlterColumn("dbo.ReturnRequest", "ReasonForReturn", c => c.String(nullable: false)); + AlterColumn("dbo.WalletHistory", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Shipment", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Shipment", "DeliveryDateUtc", c => c.DateTime()); + AlterColumn("dbo.Shipment", "ShippedDateUtc", c => c.DateTime()); + AlterColumn("dbo.Shipment", "TrackingNumber", c => c.String()); + AlterColumn("dbo.RewardPointsHistory", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.RewardPointsHistory", "Message", c => c.String()); + AlterColumn("dbo.OrderNote", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.OrderNote", "Note", c => c.String(nullable: false)); + AlterColumn("dbo.OrderItem", "BundleData", c => c.String()); + AlterColumn("dbo.OrderItem", "AttributesXml", c => c.String()); + AlterColumn("dbo.OrderItem", "AttributeDescription", c => c.String()); + AlterColumn("dbo.GiftCard", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.GiftCard", "Message", c => c.String()); + AlterColumn("dbo.GiftCard", "SenderEmail", c => c.String()); + AlterColumn("dbo.GiftCard", "SenderName", c => c.String()); + AlterColumn("dbo.GiftCard", "RecipientEmail", c => c.String()); + AlterColumn("dbo.GiftCard", "RecipientName", c => c.String()); + AlterColumn("dbo.GiftCard", "GiftCardCouponCode", c => c.String()); + AlterColumn("dbo.GiftCardUsageHistory", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.DiscountUsageHistory", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Order", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Order", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Order", "ShippingRateComputationMethodSystemName", c => c.String()); + AlterColumn("dbo.Order", "ShippingMethod", c => c.String()); + AlterColumn("dbo.Order", "PaidDateUtc", c => c.DateTime()); + AlterColumn("dbo.Order", "PurchaseOrderNumber", c => c.String()); + AlterColumn("dbo.Order", "SubscriptionTransactionId", c => c.String()); + AlterColumn("dbo.Order", "CaptureTransactionResult", c => c.String()); + AlterColumn("dbo.Order", "CaptureTransactionId", c => c.String()); + AlterColumn("dbo.Order", "AuthorizationTransactionResult", c => c.String()); + AlterColumn("dbo.Order", "AuthorizationTransactionCode", c => c.String()); + AlterColumn("dbo.Order", "AuthorizationTransactionId", c => c.String()); + AlterColumn("dbo.Order", "CustomerOrderComment", c => c.String()); + AlterColumn("dbo.Order", "DirectDebitIban", c => c.String()); + AlterColumn("dbo.Order", "DirectDebitCountry", c => c.String()); + AlterColumn("dbo.Order", "DirectDebitBIC", c => c.String()); + AlterColumn("dbo.Order", "DirectDebitBankName", c => c.String()); + AlterColumn("dbo.Order", "DirectDebitBankCode", c => c.String()); + AlterColumn("dbo.Order", "DirectDebitAccountNumber", c => c.String()); + AlterColumn("dbo.Order", "DirectDebitAccountHolder", c => c.String()); + AlterColumn("dbo.Order", "CardExpirationYear", c => c.String()); + AlterColumn("dbo.Order", "CardExpirationMonth", c => c.String()); + AlterColumn("dbo.Order", "CardCvv2", c => c.String()); + AlterColumn("dbo.Order", "MaskedCreditCardNumber", c => c.String()); + AlterColumn("dbo.Order", "CardNumber", c => c.String()); + AlterColumn("dbo.Order", "CardName", c => c.String()); + AlterColumn("dbo.Order", "CardType", c => c.String()); + AlterColumn("dbo.Order", "CustomerIp", c => c.String()); + AlterColumn("dbo.Order", "CheckoutAttributesXml", c => c.String()); + AlterColumn("dbo.Order", "CheckoutAttributeDescription", c => c.String()); + AlterColumn("dbo.Order", "TaxRates", c => c.String()); + AlterColumn("dbo.Order", "VatNumber", c => c.String()); + AlterColumn("dbo.Order", "CustomerCurrencyCode", c => c.String()); + AlterColumn("dbo.Order", "PaymentMethodSystemName", c => c.String()); + AlterColumn("dbo.Order", "OrderNumber", c => c.String()); + AlterColumn("dbo.ExternalAuthenticationRecord", "ProviderSystemName", c => c.String()); + AlterColumn("dbo.ExternalAuthenticationRecord", "OAuthAccessToken", c => c.String()); + AlterColumn("dbo.ExternalAuthenticationRecord", "OAuthToken", c => c.String()); + AlterColumn("dbo.ExternalAuthenticationRecord", "ExternalDisplayIdentifier", c => c.String()); + AlterColumn("dbo.ExternalAuthenticationRecord", "ExternalIdentifier", c => c.String()); + AlterColumn("dbo.ExternalAuthenticationRecord", "Email", c => c.String()); + AlterColumn("dbo.News", "MetaDescription", c => c.String()); + AlterColumn("dbo.News", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.News", "EndDateUtc", c => c.DateTime()); + AlterColumn("dbo.News", "StartDateUtc", c => c.DateTime()); + AlterColumn("dbo.News", "Full", c => c.String(nullable: false)); + AlterColumn("dbo.News", "Short", c => c.String(nullable: false)); + AlterColumn("dbo.News", "Title", c => c.String(nullable: false)); + AlterColumn("dbo.Forums_Group", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Forums_Group", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Forums_Group", "Description", c => c.String()); + AlterColumn("dbo.Forums_Forum", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Forums_Forum", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Forums_Forum", "LastPostTime", c => c.DateTime()); + AlterColumn("dbo.Forums_Forum", "Description", c => c.String()); + AlterColumn("dbo.Forums_Topic", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Forums_Topic", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Forums_Topic", "LastPostTime", c => c.DateTime()); + AlterColumn("dbo.Forums_Post", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Forums_Post", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Forums_Post", "Text", c => c.String(nullable: false)); + AlterColumn("dbo.Download", "Changelog", c => c.String()); + AlterColumn("dbo.Download", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Download", "Extension", c => c.String()); + AlterColumn("dbo.Download", "Filename", c => c.String()); + AlterColumn("dbo.Download", "ContentType", c => c.String()); + AlterColumn("dbo.Download", "DownloadUrl", c => c.String()); + AlterColumn("dbo.ProductVariantAttributeValue", "Name", c => c.String()); + AlterColumn("dbo.ProductAttribute", "ExportMappings", c => c.String()); + AlterColumn("dbo.ProductAttribute", "Description", c => c.String()); + AlterColumn("dbo.ProductAttribute", "Name", c => c.String(nullable: false)); + AlterColumn("dbo.Product_ProductAttribute_Mapping", "TextPrompt", c => c.String()); + AlterColumn("dbo.ProductVariantAttributeCombination", "AttributesXml", c => c.String()); + AlterColumn("dbo.SpecificationAttribute", "Name", c => c.String(nullable: false)); + AlterColumn("dbo.SpecificationAttributeOption", "Name", c => c.String(nullable: false)); + AlterColumn("dbo.ProductBundleItem", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.ProductBundleItem", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.ProductBundleItem", "ShortDescription", c => c.String()); + AlterColumn("dbo.DiscountRequirement", "ExtraData", c => c.String()); + AlterColumn("dbo.DiscountRequirement", "RestrictedShippingOptions", c => c.String()); + AlterColumn("dbo.DiscountRequirement", "RestrictedPaymentMethods", c => c.String()); + AlterColumn("dbo.DiscountRequirement", "RestrictedProductIds", c => c.String()); + AlterColumn("dbo.DiscountRequirement", "DiscountRequirementRuleSystemName", c => c.String()); + AlterColumn("dbo.Manufacturer", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Manufacturer", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Manufacturer", "MetaDescription", c => c.String()); + AlterColumn("dbo.Manufacturer", "Description", c => c.String()); + AlterColumn("dbo.Picture", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Category", "DefaultViewMode", c => c.String()); + AlterColumn("dbo.Category", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Category", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Category", "MetaDescription", c => c.String()); + AlterColumn("dbo.Category", "BadgeText", c => c.String()); + AlterColumn("dbo.Category", "BottomDescription", c => c.String()); + AlterColumn("dbo.Category", "Description", c => c.String()); + AlterColumn("dbo.Discount", "EndDateUtc", c => c.DateTime()); + AlterColumn("dbo.Discount", "StartDateUtc", c => c.DateTime()); + AlterColumn("dbo.Product", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Product", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Product", "AvailableEndDateTimeUtc", c => c.DateTime()); + AlterColumn("dbo.Product", "AvailableStartDateTimeUtc", c => c.DateTime()); + AlterColumn("dbo.Product", "SpecialPriceEndDateTimeUtc", c => c.DateTime()); + AlterColumn("dbo.Product", "SpecialPriceStartDateTimeUtc", c => c.DateTime()); + AlterColumn("dbo.Product", "UserAgreementText", c => c.String()); + AlterColumn("dbo.Product", "MetaDescription", c => c.String()); + AlterColumn("dbo.Product", "AdminComment", c => c.String()); + AlterColumn("dbo.Product", "FullDescription", c => c.String()); + AlterColumn("dbo.Product", "ShortDescription", c => c.String()); + AlterColumn("dbo.BlogPost", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.BlogPost", "MetaDescription", c => c.String()); + AlterColumn("dbo.BlogPost", "EndDateUtc", c => c.DateTime()); + AlterColumn("dbo.BlogPost", "StartDateUtc", c => c.DateTime()); + AlterColumn("dbo.BlogPost", "Tags", c => c.String()); + AlterColumn("dbo.BlogPost", "Body", c => c.String(nullable: false)); + AlterColumn("dbo.BlogPost", "Title", c => c.String(nullable: false)); + AlterColumn("dbo.Country", "AddressFormat", c => c.String()); + AlterColumn("dbo.Address", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Address", "FaxNumber", c => c.String()); + AlterColumn("dbo.Address", "PhoneNumber", c => c.String()); + AlterColumn("dbo.Address", "ZipPostalCode", c => c.String()); + AlterColumn("dbo.Address", "Address2", c => c.String()); + AlterColumn("dbo.Address", "Address1", c => c.String()); + AlterColumn("dbo.Address", "City", c => c.String()); + AlterColumn("dbo.Address", "Company", c => c.String()); + AlterColumn("dbo.Address", "Email", c => c.String()); + AlterColumn("dbo.Address", "LastName", c => c.String()); + AlterColumn("dbo.Address", "FirstName", c => c.String()); + AlterColumn("dbo.Address", "Title", c => c.String()); + AlterColumn("dbo.Address", "Salutation", c => c.String()); + AlterColumn("dbo.Customer", "BirthDate", c => c.DateTime()); + AlterColumn("dbo.Customer", "LastActivityDateUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Customer", "LastLoginDateUtc", c => c.DateTime()); + AlterColumn("dbo.Customer", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.Customer", "AdminComment", c => c.String()); + AlterColumn("dbo.NewsComment", "CommentText", c => c.String()); + AlterColumn("dbo.NewsComment", "CommentTitle", c => c.String()); + AlterColumn("dbo.ProductReview", "ReviewText", c => c.String()); + AlterColumn("dbo.ProductReview", "Title", c => c.String()); + AlterColumn("dbo.BlogComment", "CommentText", c => c.String()); + AlterColumn("dbo.CustomerContent", "UpdatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.CustomerContent", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AlterColumn("dbo.PollAnswer", "Name", c => c.String(nullable: false)); + AlterColumn("dbo.Poll", "EndDateUtc", c => c.DateTime()); + AlterColumn("dbo.Poll", "StartDateUtc", c => c.DateTime()); + AlterColumn("dbo.Poll", "SystemKeyword", c => c.String()); + AlterColumn("dbo.Poll", "Name", c => c.String(nullable: false)); + AlterColumn("dbo.PermissionRecord", "Name", c => c.String(nullable: false)); + AlterColumn("dbo.LocaleStringResource", "ResourceValue", c => c.String(nullable: false)); + AlterColumn("dbo.LocalizedProperty", "LocaleValue", c => c.String(nullable: false)); + AlterColumn("dbo.Setting", "Value", c => c.String(nullable: false)); + AlterColumn("dbo.Topic", "TitleTag", c => c.String()); + AlterColumn("dbo.Topic", "WidgetZone", c => c.String()); + AlterColumn("dbo.Topic", "MetaTitle", c => c.String()); + AlterColumn("dbo.Topic", "MetaDescription", c => c.String()); + AlterColumn("dbo.Topic", "MetaKeywords", c => c.String()); + AlterColumn("dbo.Topic", "Body", c => c.String()); + AlterColumn("dbo.Topic", "Title", c => c.String()); + AlterColumn("dbo.Topic", "Password", c => c.String()); + AlterColumn("dbo.Topic", "SystemName", c => c.String()); + CreateIndex("dbo.ScheduleTask", new[] { "NextRunUtc", "Enabled" }, name: "IX_NextRun_Enabled"); + CreateIndex("dbo.ScheduleTaskHistory", new[] { "StartedOnUtc", "FinishedOnUtc" }, name: "IX_Started_Finished"); + CreateIndex("dbo.WalletHistory", new[] { "StoreId", "CreatedOnUtc" }, name: "IX_StoreId_CreatedOn"); + CreateIndex("dbo.Forums_Topic", "CreatedOnUtc"); + CreateIndex("dbo.Forums_Topic", new[] { "TopicTypeId", "LastPostTime" }); + CreateIndex("dbo.Forums_Post", "CreatedOnUtc"); + CreateIndex("dbo.Download", new[] { "UpdatedOnUtc", "IsTransient" }, name: "IX_UpdatedOn_IsTransient"); + CreateIndex("dbo.ProductVariantAttributeValue", "Name"); + CreateIndex("dbo.ProductVariantAttributeValue", new[] { "ProductVariantAttributeId", "DisplayOrder" }, name: "IX_ProductVariantAttributeValue_ProductVariantAttributeId_DisplayOrder"); + CreateIndex("dbo.Picture", new[] { "UpdatedOnUtc", "IsTransient" }, name: "IX_UpdatedOn_IsTransient"); + CreateIndex("dbo.Customer", "BirthDate", name: "IX_Customer_BirthDate"); + CreateIndex("dbo.Customer", "LastActivityDateUtc", name: "IX_Customer_LastActivity"); + CreateIndex("dbo.Customer", "CreatedOnUtc", name: "IX_Customer_CreatedOn"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201812212031185_UpdateToMySQL.resx b/src/Libraries/SmartStore.Data/Migrations/201812212031185_UpdateToMySQL.resx new file mode 100644 index 0000000000..551ea11f1b --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201812212031185_UpdateToMySQL.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 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 + + + dbo + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/Configuration.cs b/src/Libraries/SmartStore.Data/Migrations/Configuration.cs new file mode 100644 index 0000000000..98b0e62051 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/Configuration.cs @@ -0,0 +1,23 @@ +namespace SmartStore.Data.Migrations +{ + using System; + using System.Data.Entity; + using System.Data.Entity.Migrations; + using System.Linq; + + internal sealed class Configuration : DbMigrationsConfiguration + { + public Configuration() + { + AutomaticMigrationsEnabled = false; + } + + protected override void Seed(SmartStore.Data.SmartObjectContext context) + { + // This method will be called after migrating to the latest version. + + // You can use the DbSet.AddOrUpdate() helper extension method + // to avoid creating duplicate seed data. + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs index c24d77ffd2..fbb65259ca 100644 --- a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs +++ b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs @@ -1,12 +1,15 @@ namespace SmartStore.Data.Migrations { using System; - using System.Data.Entity.Migrations; - using Setup; - using SmartStore.Core.Data; - using SmartStore.Core.Domain.Catalog; - using SmartStore.Core.Domain.Common; - using SmartStore.Utilities; + using System.Data.Entity; + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Model; + using System.Data.Entity.Migrations.Sql; + using System.Diagnostics; + using System.Linq; + using System.Text; + using MySql.Data.Entity; + using Setup; public sealed class MigrationsConfiguration : DbMigrationsConfiguration { @@ -16,17 +19,10 @@ public MigrationsConfiguration() AutomaticMigrationDataLossAllowed = true; ContextKey = "SmartStore.Core"; - if (DataSettings.Current.IsSqlServer) - { - var commandTimeout = CommonHelper.GetAppSetting("sm:EfMigrationsCommandTimeout"); - if (commandTimeout.HasValue) - { - CommandTimeout = commandTimeout.Value; - } - - CommandTimeout = 9999999; - } - } + // MOD: + //SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); + SetSqlGenerator("MySql.Data.MySqlClient", new MySQLMigration()); + } public void SeedDatabase(SmartObjectContext context) { @@ -43,17 +39,7 @@ protected override void Seed(SmartObjectContext context) public void MigrateSettings(SmartObjectContext context) { - context.MigrateSettings(x => - { - x.Add(TypeHelper.NameOf(y => y.CacheSegmentSize, true), 500); - x.Add(TypeHelper.NameOf(y => y.AlwaysPrefetchTranslations, true), false); - x.Add(TypeHelper.NameOf(y => y.AlwaysPrefetchUrlSlugs, true), false); - - // New CatalogSettings properties - x.Add(TypeHelper.NameOf(y => y.ShowSubCategoriesInSubPages, true), false); - x.Add(TypeHelper.NameOf(y => y.ShowDescriptionInSubPages, true), false); - x.Add(TypeHelper.NameOf(y => y.IncludeFeaturedProductsInSubPages, true), false); - }); + } public void MigrateLocaleResources(LocaleResourcesBuilder builder) @@ -592,7 +578,6 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Common.Voting", "Voting", "Abstimmung"); builder.AddOrUpdate("Common.Answer", "Answer", "Antwort"); - builder.AddOrUpdate("Common.Size", "Size", "Größe"); builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CustomerFormFields.Description", "Manage form fields that are displayed during registration.", @@ -613,34 +598,84 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) builder.AddOrUpdate("Admin.Common.ProcessingInfo", "{0}: {1} of {2} processed", "{0}: {1} von {2} verarbeitet"); + } + } + + + + + + + + + + + + + + + + + + + + + + public class MySQLMigration : MySqlMigrationSqlGenerator + { + //public MySQLMigration() + //{ + // YourContext x = new YourContext(); + //} + + + + + + private string TrimSchemaPrefix(string table) + { + if (table.StartsWith("dbo.")) + return table.Replace("dbo.", ""); + return table; + } + + protected override MigrationStatement Generate(CreateIndexOperation op) + { + StringBuilder sb = new StringBuilder(); + + sb = sb.Append("CREATE "); + + + + + if (op.IsUnique) + { + sb.Append("UNIQUE "); + } + + //index_col_name specification can end with ASC or DESC. + // sort order are permitted for future extensions for specifying ascending or descending index value storage + //Currently, they are parsed but ignored; index values are always stored in ascending order. + + object sort; + op.AnonymousArguments.TryGetValue("Sort", out sort); + var sortOrder = sort != null && sort.ToString() == "Ascending" ? + "ASC" : "DESC"; + + sb.AppendFormat("index `{0}` on `{1}` (", op.Name, TrimSchemaPrefix(op.Table)); + sb.Append(string.Join(",", op.Columns.Select(c => "`" + c + "` " + sortOrder)) + ") "); + + object indexTypeDefinition; + op.AnonymousArguments.TryGetValue("Type", out indexTypeDefinition); + + var indexType = indexTypeDefinition != null && string.Compare(indexTypeDefinition.ToString(), "Hash", StringComparison.InvariantCultureIgnoreCase) > 0 ? + "HASH" : "BTREE"; + + sb.Append("using " + indexType); - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowSubCategoriesInSubPages", - "Show subcategories also in subpages", - "Unterwarengruppen auch in Unterseiten anzeigen", - "Subpage: List index greater than 1 or any active filter.", - "Unterseite: Listenindex größer 1 oder mind. ein aktiver Filter."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowDescriptionInSubPages", - "Show page description also in subpages", - "Seitenbeschreibungen auch in Unterseiten anzeigen", - "Subpage: List index greater than 1 or any active filter.", - "Unterseite: Listenindex größer 1 oder mind. ein aktiver Filter."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.IncludeFeaturedProductsInSubPages", - "Show featured products also in subpages", - "Top-Produkte auch in Unterseiten anzeigen", - "Subpage: List index greater than 1 or any active filter.", - "Unterseite: Listenindex größer 1 oder mind. ein aktiver Filter."); - - builder.AddOrUpdate("Admin.Common.CopyOf", "Copy of {0}", "Kopie von {0}"); - - builder.AddOrUpdate("Admin.Configuration.Languages.DefaultLanguage.Note", - "The default language of the shop is {0}. The default is always the first published language.", - "Die Standardsprache des Shops ist {0}. Standard ist stets die erste veröffentlichte Sprache."); - - builder.AddOrUpdate("Admin.Configuration.Languages.AvailableLanguages.Note", - "Click Download to install a new language including all localized resources. On translate.smartstore.com you will find more details about available resources.", - "Klicken Sie auf Download, um eine neue Sprache mit allen lokalisierten Ressourcen zu installieren. Auf translate.smartstore.com finden Sie weitere Details zu verfügbaren Ressourcen."); + return new MigrationStatement() { + Sql = sb.ToString() + }; } } } diff --git a/src/Libraries/SmartStore.Data/MySQLDataProvider.cs b/src/Libraries/SmartStore.Data/MySQLDataProvider.cs new file mode 100644 index 0000000000..d7f6778d7e --- /dev/null +++ b/src/Libraries/SmartStore.Data/MySQLDataProvider.cs @@ -0,0 +1,46 @@ +using MySql.Data.Entity; +using MySql.Data.MySqlClient; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Data.Entity.Infrastructure; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SmartStore.Data +{ + public class MySQLDataProvider : IEfDataProvider + { + /// + /// Get connection factory + /// + /// Connection factory + public virtual IDbConnectionFactory GetConnectionFactory() + { + return new MySqlConnectionFactory(); + } + + ///// + ///// A value indicating whether this data provider supports stored procedures + ///// + public bool StoredProceduresSupported + { + get { return false; } // TODO: much faster to use + } + + ///// + ///// Gets a support database parameter object (used by stored procedures) + ///// + ///// Parameter + public DbParameter GetParameter() + { + return new MySqlParameter(); + } + + public string ProviderInvariantName + { + get { return "MySql.Data.MySqlClient"; } + } + } +} diff --git a/src/Libraries/SmartStore.Data/ObjectContextBase.cs b/src/Libraries/SmartStore.Data/ObjectContextBase.cs index 0e7c9c73bf..a346696f4a 100644 --- a/src/Libraries/SmartStore.Data/ObjectContextBase.cs +++ b/src/Libraries/SmartStore.Data/ObjectContextBase.cs @@ -5,6 +5,7 @@ using System.Data.Entity; using System.Data.Entity.Core.Objects; using System.Data.Entity.Infrastructure; +using System.Diagnostics; using System.Linq; using SmartStore.ComponentModel; using SmartStore.Core; @@ -29,10 +30,10 @@ public abstract partial class ObjectContextBase : DbContext, IDbContext /// /// Parameterless constructor for tooling support, e.g. EF Migrations. /// - protected ObjectContextBase() - : this(GetConnectionString(), null) + protected ObjectContextBase(): this(GetConnectionString(), null) { - } + Database.Log = sql => Debug.Write($"ObjectContextBase: {sql}"); + } protected ObjectContextBase(string nameOrConnectionString, string alias = null) : base(nameOrConnectionString) @@ -42,7 +43,7 @@ protected ObjectContextBase(string nameOrConnectionString, string alias = null) this.Alias = null; this.DbHookHandler = NullDbHookHandler.Instance; - if (_commandTimeoutInSeconds >= 0 && DataSettings.Current.IsSqlServer) + if (_commandTimeoutInSeconds >= 0) { Database.CommandTimeout = _commandTimeoutInSeconds; } @@ -190,6 +191,7 @@ public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, in var transactionalBehavior = doNotEnsureTransaction ? TransactionalBehavior.DoNotEnsureTransaction : TransactionalBehavior.EnsureTransaction; + // FIXME: var result = this.Database.ExecuteSqlCommand(transactionalBehavior, sql, parameters); if (timeout.HasValue) diff --git a/src/Libraries/SmartStore.Data/Setup/DbSeedingMigrator.cs b/src/Libraries/SmartStore.Data/Setup/DbSeedingMigrator.cs index ab071e861c..2d9d248da8 100644 --- a/src/Libraries/SmartStore.Data/Setup/DbSeedingMigrator.cs +++ b/src/Libraries/SmartStore.Data/Setup/DbSeedingMigrator.cs @@ -25,8 +25,7 @@ public class DbSeedingMigrator : DbMigrator where TContext : DbContext /// /// Initializes a new instance of the DbMigrator class with the default (core db) configuration. /// - public DbSeedingMigrator() - : this(new MigrationsConfiguration()) + public DbSeedingMigrator() : this(new MigrationsConfiguration()) { } @@ -34,10 +33,10 @@ public DbSeedingMigrator() /// Initializes a new instance of the DbMigrator class. /// /// Configuration to be used for the migration process. - public DbSeedingMigrator(DbMigrationsConfiguration configuration) - : base(configuration) - { - } + public DbSeedingMigrator(DbMigrationsConfiguration configuration): base(configuration) + { + + } public ILogger Logger { diff --git a/src/Libraries/SmartStore.Data/Setup/InstallDatabaseInitializer.cs b/src/Libraries/SmartStore.Data/Setup/InstallDatabaseInitializer.cs index 3a8d7f115a..48ebcbbdab 100644 --- a/src/Libraries/SmartStore.Data/Setup/InstallDatabaseInitializer.cs +++ b/src/Libraries/SmartStore.Data/Setup/InstallDatabaseInitializer.cs @@ -34,8 +34,8 @@ public override void InitializeDatabase(SmartObjectContext context) // The installation seeder contains ALL required seed data already. var migrator = new DbMigrator(base.CreateConfiguration()); - // Run all migrations including the initial one - migrator.Update(); + // Run all migrations including the initial one + migrator.Update(); // seed install data this.Seed(context); diff --git a/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs b/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs index c92790df60..c11b71aedc 100644 --- a/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs +++ b/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs @@ -4,7 +4,6 @@ using System.Data.Entity.Infrastructure; using System.Data.Entity.Migrations; using System.Linq; -using SmartStore.Core.Data; using SmartStore.Data.Migrations; using SmartStore.Utilities; @@ -135,7 +134,7 @@ protected virtual TConfig CreateConfiguration() config.TargetDatabase = new DbConnectionInfo(this.ConnectionString, dbContextInfo.ConnectionProviderName); } - if (config.CommandTimeout == null && DataSettings.Current.IsSqlServer) + if (config.CommandTimeout == null) { var commandTimeout = CommonHelper.GetAppSetting("sm:EfMigrationsCommandTimeout"); if (commandTimeout.HasValue) diff --git a/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.cs b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.cs index a7947fd324..cb667e9ca8 100644 --- a/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.cs +++ b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.cs @@ -3965,7 +3965,7 @@ public IList Topics() public IList Settings() { - var entities = new List + List entities = new List { new PdfSettings { diff --git a/src/Libraries/SmartStore.Data/SmartObjectContext.cs b/src/Libraries/SmartStore.Data/SmartObjectContext.cs index a294110582..eb2fbbee67 100644 --- a/src/Libraries/SmartStore.Data/SmartObjectContext.cs +++ b/src/Libraries/SmartStore.Data/SmartObjectContext.cs @@ -30,8 +30,7 @@ static SmartObjectContext() /// /// For tooling support, e.g. EF Migrations /// - public SmartObjectContext() - : base() + public SmartObjectContext() : base() { } diff --git a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj index f7a601595a..13b979a7ff 100644 --- a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj +++ b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj @@ -94,9 +94,14 @@ True ..\..\packages\Microsoft.SqlServer.Scripting.11.0.2100.61\lib\Microsoft.SqlServer.Smo.dll - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - True + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll @@ -640,18 +645,12 @@ 201811061745204_IsSystemProductIndex.cs - - - 201811082148279_LocalizedPropertyKeyGroupIndex.cs - - - - 201811161142587_TaskHistoryErrorLength.cs - - - - 201811202204501_ProductIndexSeekExport.cs + + + 201812212031185_UpdateToMySQL.cs + + @@ -1161,14 +1160,8 @@ 201811061745204_IsSystemProductIndex.cs - - 201811082148279_LocalizedPropertyKeyGroupIndex.cs - - - 201811161142587_TaskHistoryErrorLength.cs - - - 201811202204501_ProductIndexSeekExport.cs + + 201812212031185_UpdateToMySQL.cs diff --git a/src/Libraries/SmartStore.Data/Sql/Indexes - Copy.sql b/src/Libraries/SmartStore.Data/Sql/Indexes - Copy.sql new file mode 100644 index 0000000000..855d1934c5 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Sql/Indexes - Copy.sql @@ -0,0 +1,170 @@ +CREATE NONCLUSTERED INDEX [IX_LocaleStringResource] ON [LocaleStringResource] ([ResourceName] ASC, [LanguageId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Product_PriceDatesEtc] ON [Product] ([Price] ASC, [AvailableStartDateTimeUtc] ASC, [AvailableEndDateTimeUtc] ASC, [Published] ASC, [Deleted] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Country_DisplayOrder] ON [Country] ([DisplayOrder] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Currency_DisplayOrder] ON [Currency] ( [DisplayOrder] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Log_CreatedOnUtc] ON [Log] ([CreatedOnUtc] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Customer_Email] ON [Customer] ([Email] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Customer_Username] ON [Customer] ([Username] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Customer_CustomerGuid] ON [Customer] ([CustomerGuid] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_GenericAttribute_EntityId_and_KeyGroup] ON [GenericAttribute] ([EntityId] ASC, [KeyGroup] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_QueuedEmail_CreatedOnUtc] ON [QueuedEmail] ([CreatedOnUtc] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Order_CustomerId] ON [Order] ([CustomerId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Language_DisplayOrder] ON [Language] ([DisplayOrder] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_BlogPost_LanguageId] ON [BlogPost] ([LanguageId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_BlogComment_BlogPostId] ON [BlogComment] ([BlogPostId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_News_LanguageId] ON [News] ([LanguageId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_NewsComment_NewsItemId] ON [NewsComment] ([NewsItemId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_PollAnswer_PollId] ON [PollAnswer] ([PollId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_ProductReview_ProductId] ON [ProductReview] ([ProductId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_OrderItem_OrderId] ON [OrderItem] ([OrderId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_OrderNote_OrderId] ON [OrderNote] ([OrderId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_TierPrice_ProductId] ON [TierPrice] ([ProductId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_ShoppingCartItem_ShoppingCartTypeId_CustomerId] ON [ShoppingCartItem] ([ShoppingCartTypeId] ASC, [CustomerId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_RelatedProduct_ProductId1] ON [RelatedProduct] ([ProductId1] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_ProductVariantAttributeValue_ProductVariantAttributeId] ON [ProductVariantAttributeValue] ([ProductVariantAttributeId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Product_ProductAttribute_Mapping_ProductId] ON [Product_ProductAttribute_Mapping] ([ProductId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Manufacturer_DisplayOrder] ON [Manufacturer] ([DisplayOrder] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Category_DisplayOrder] ON [Category] ([DisplayOrder] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Category_ParentCategoryId] ON [Category] ([ParentCategoryId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Forums_Group_DisplayOrder] ON [Forums_Group] ([DisplayOrder] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Forums_Forum_DisplayOrder] ON [Forums_Forum] ([DisplayOrder] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Forums_Forum_ForumGroupId] ON [Forums_Forum] ([ForumGroupId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Forums_Topic_ForumId] ON [Forums_Topic] ([ForumId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Forums_Post_TopicId] ON [Forums_Post] ([TopicId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Forums_Post_CustomerId] ON [Forums_Post] ([CustomerId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Forums_Subscription_ForumId] ON [Forums_Subscription] ([ForumId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Forums_Subscription_TopicId] ON [Forums_Subscription] ([TopicId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Product_Deleted_and_Published] ON [Product] ([Published] ASC, [Deleted] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Product_Published] ON [Product] ([Published] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Product_ShowOnHomepage] ON [Product] ([ShowOnHomepage] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Product_ParentGroupedProductId] ON [Product] ([ParentGroupedProductId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Product_VisibleIndividually] ON [Product] ([VisibleIndividually] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_PCM_Product_and_Category] ON [Product_Category_Mapping] ([CategoryId] ASC, [ProductId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_PMM_Product_and_Manufacturer] ON [Product_Manufacturer_Mapping] ([ManufacturerId] ASC, [ProductId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_ProductTag_Name] ON [ProductTag] ([Name] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_ActivityLog_CreatedOnUtc] ON [ActivityLog] ([CreatedOnUtc] ASC) +GO + +CREATE UNIQUE NONCLUSTERED INDEX [IX_UrlRecord_Slug] ON [UrlRecord] ([Slug] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_AclRecord_EntityId_EntityName] ON [AclRecord] ([EntityId] ASC, [EntityName] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_StoreMapping_EntityId_EntityName] ON [StoreMapping] ([EntityId] ASC, [EntityName] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Category_LimitedToStores] ON [Category] ([LimitedToStores] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Manufacturer_LimitedToStores] ON [Manufacturer] ([LimitedToStores] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Product_LimitedToStores] ON [Product] ([LimitedToStores] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_ProductVariantAttributeCombination_SKU] ON [ProductVariantAttributeCombination] ([SKU] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Product_Name] ON [Product] ([Name] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_Product_Sku] ON [Product] ([Sku] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_ProductBundleItem_ProductId] ON [ProductBundleItem] ([ProductId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_ProductBundleItem_BundleProductId] ON [ProductBundleItem] ([BundleProductId] ASC) +GO + +CREATE NONCLUSTERED INDEX [IX_ProductBundleItemAttributeFilter_BundleItemId] ON [ProductBundleItemAttributeFilter] ([BundleItemId] ASC) +GO \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Sql/Indexes.Inverse - Copy.sql b/src/Libraries/SmartStore.Data/Sql/Indexes.Inverse - Copy.sql new file mode 100644 index 0000000000..2d00394576 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Sql/Indexes.Inverse - Copy.sql @@ -0,0 +1,170 @@ +DROP INDEX [IX_LocaleStringResource] ON [LocaleStringResource] +GO + +DROP INDEX [IX_Product_PriceDatesEtc] ON [Product] +GO + +DROP INDEX [IX_Country_DisplayOrder] ON [Country] +GO + +DROP INDEX [IX_Currency_DisplayOrder] ON [Currency] +GO + +DROP INDEX [IX_Log_CreatedOnUtc] ON [Log] +GO + +DROP INDEX [IX_Customer_Email] ON [Customer] +GO + +DROP INDEX [IX_Customer_Username] ON [Customer] +GO + +DROP INDEX [IX_Customer_CustomerGuid] ON [Customer] +GO + +DROP INDEX [IX_GenericAttribute_EntityId_and_KeyGroup] ON [GenericAttribute] +GO + +DROP INDEX [IX_QueuedEmail_CreatedOnUtc] ON [QueuedEmail] +GO + +DROP INDEX [IX_Order_CustomerId] ON [Order] +GO + +DROP INDEX [IX_Language_DisplayOrder] ON [Language] +GO + +DROP INDEX [IX_BlogPost_LanguageId] ON [BlogPost] +GO + +DROP INDEX [IX_BlogComment_BlogPostId] ON [BlogComment] +GO + +DROP INDEX [IX_News_LanguageId] ON [News] +GO + +DROP INDEX [IX_NewsComment_NewsItemId] ON [NewsComment] +GO + +DROP INDEX [IX_PollAnswer_PollId] ON [PollAnswer] +GO + +DROP INDEX [IX_ProductReview_ProductId] ON [ProductReview] +GO + +DROP INDEX [IX_OrderItem_OrderId] ON [OrderItem] +GO + +DROP INDEX [IX_OrderNote_OrderId] ON [OrderNote] +GO + +DROP INDEX [IX_TierPrice_ProductId] ON [TierPrice] +GO + +DROP INDEX [IX_ShoppingCartItem_ShoppingCartTypeId_CustomerId] ON [ShoppingCartItem] +GO + +DROP INDEX [IX_RelatedProduct_ProductId1] ON [RelatedProduct] +GO + +DROP INDEX [IX_ProductVariantAttributeValue_ProductVariantAttributeId] ON [ProductVariantAttributeValue] +GO + +DROP INDEX [IX_Product_ProductAttribute_Mapping_ProductId] ON [Product_ProductAttribute_Mapping] +GO + +DROP INDEX [IX_Manufacturer_DisplayOrder] ON [Manufacturer] +GO + +DROP INDEX [IX_Category_DisplayOrder] ON [Category] +GO + +DROP INDEX [IX_Category_ParentCategoryId] ON [Category] +GO + +DROP INDEX [IX_Forums_Group_DisplayOrder] ON [Forums_Group] +GO + +DROP INDEX [IX_Forums_Forum_DisplayOrder] ON [Forums_Forum] +GO + +DROP INDEX [IX_Forums_Forum_ForumGroupId] ON [Forums_Forum] +GO + +DROP INDEX [IX_Forums_Topic_ForumId] ON [Forums_Topic] +GO + +DROP INDEX [IX_Forums_Post_TopicId] ON [Forums_Post] +GO + +DROP INDEX [IX_Forums_Post_CustomerId] ON [Forums_Post] +GO + +DROP INDEX [IX_Forums_Subscription_ForumId] ON [Forums_Subscription] +GO + +DROP INDEX [IX_Forums_Subscription_TopicId] ON [Forums_Subscription] +GO + +DROP INDEX [IX_Product_Deleted_and_Published] ON [Product] +GO + +DROP INDEX [IX_Product_Published] ON [Product] +GO + +DROP INDEX [IX_Product_ShowOnHomepage] ON [Product] +GO + +DROP INDEX [IX_Product_ParentGroupedProductId] ON [Product] +GO + +DROP INDEX [IX_Product_VisibleIndividually] ON [Product] +GO + +DROP INDEX [IX_PCM_Product_and_Category] ON [Product_Category_Mapping] +GO + +DROP INDEX [IX_PMM_Product_and_Manufacturer] ON [Product_Manufacturer_Mapping] +GO + +DROP INDEX [IX_ProductTag_Name] ON [ProductTag] +GO + +DROP INDEX [IX_ActivityLog_CreatedOnUtc] ON [ActivityLog] +GO + +DROP INDEX [IX_UrlRecord_Slug] ON [UrlRecord] +GO + +DROP INDEX [IX_AclRecord_EntityId_EntityName] ON [AclRecord] +GO + +DROP INDEX [IX_StoreMapping_EntityId_EntityName] ON [StoreMapping] +GO + +DROP INDEX [IX_Category_LimitedToStores] ON [Category] +GO + +DROP INDEX [IX_Manufacturer_LimitedToStores] ON [Manufacturer] +GO + +DROP INDEX [IX_Product_LimitedToStores] ON [Product] +GO + +DROP INDEX [IX_ProductVariantAttributeCombination_SKU] +GO + +DROP INDEX [IX_Product_Name] ON [Product] +GO + +DROP INDEX [IX_Product_Sku] ON [Product] +GO + +DROP INDEX [IX_ProductBundleItem_ProductId] ON [ProductBundleItem] +GO + +DROP INDEX [IX_ProductBundleItem_BundleProductId] ON [ProductBundleItem] +GO + +DROP INDEX [IX_ProductBundleItemAttributeFilter_BundleItemId] ON [ProductBundleItemAttributeFilter] +GO \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer - Copy.sql b/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer - Copy.sql new file mode 100644 index 0000000000..ad39c3e86c --- /dev/null +++ b/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer - Copy.sql @@ -0,0 +1,11 @@ +CREATE NONCLUSTERED INDEX [IX_StateProvince_CountryId] ON [StateProvince] ([CountryId]) INCLUDE ([DisplayOrder]) +GO + +CREATE NONCLUSTERED INDEX [IX_PSAM_AllowFiltering] ON [Product_SpecificationAttribute_Mapping] ([AllowFiltering] ASC) INCLUDE ([ProductId],[SpecificationAttributeOptionId]) +GO + +CREATE NONCLUSTERED INDEX [IX_PSAM_SpecificationAttributeOptionId_AllowFiltering] ON [Product_SpecificationAttribute_Mapping] ([SpecificationAttributeOptionId] ASC, [AllowFiltering] ASC) INCLUDE ([ProductId]) +GO + +CREATE NONCLUSTERED INDEX [IX_LocalizedProperty_Key] ON [LocalizedProperty] ([Id]) INCLUDE ([EntityId], [LocaleKeyGroup], [LocaleKey]) +GO \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer.Inverse - Copy.sql b/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer.Inverse - Copy.sql new file mode 100644 index 0000000000..dbc47e8321 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer.Inverse - Copy.sql @@ -0,0 +1,11 @@ +DROP INDEX [IX_StateProvince_CountryId] ON [StateProvince] +GO + +DROP INDEX [IX_PSAM_AllowFiltering] ON [Product_SpecificationAttribute_Mapping] +GO + +DROP INDEX [IX_PSAM_SpecificationAttributeOptionId_AllowFiltering] ON [Product_SpecificationAttribute_Mapping] +GO + +DROP INDEX [IX_LocalizedProperty_Key] ON [LocalizedProperty] +GO \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer.Inverse.sql b/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer.Inverse.sql index 709dabb0ec..dbc47e8321 100644 --- a/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer.Inverse.sql +++ b/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer.Inverse.sql @@ -8,4 +8,4 @@ DROP INDEX [IX_PSAM_SpecificationAttributeOptionId_AllowFiltering] ON [Product_S GO DROP INDEX [IX_LocalizedProperty_Key] ON [LocalizedProperty] -GO +GO \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer.sql b/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer.sql index c871635093..ad39c3e86c 100644 --- a/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer.sql +++ b/src/Libraries/SmartStore.Data/Sql/Indexes.SqlServer.sql @@ -8,4 +8,4 @@ CREATE NONCLUSTERED INDEX [IX_PSAM_SpecificationAttributeOptionId_AllowFiltering GO CREATE NONCLUSTERED INDEX [IX_LocalizedProperty_Key] ON [LocalizedProperty] ([Id]) INCLUDE ([EntityId], [LocaleKeyGroup], [LocaleKey]) -GO +GO \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Sql/StoredProcedures - Copy.sql b/src/Libraries/SmartStore.Data/Sql/StoredProcedures - Copy.sql new file mode 100644 index 0000000000..550b34cf7d --- /dev/null +++ b/src/Libraries/SmartStore.Data/Sql/StoredProcedures - Copy.sql @@ -0,0 +1,89 @@ +CREATE FUNCTION [dbo].[sm_splitstring_to_table] +( + @string NVARCHAR(MAX), + @delimiter CHAR(1) +) +RETURNS @output TABLE( + data NVARCHAR(MAX) +) +BEGIN + DECLARE @start INT, @end INT + SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) + + WHILE @start < LEN(@string) + 1 BEGIN + IF @end = 0 + SET @end = LEN(@string) + 1 + + INSERT INTO @output (data) + VALUES(SUBSTRING(@string, @start, @end - @start)) + SET @start = @end + 1 + SET @end = CHARINDEX(@delimiter, @string, @start) + END + RETURN +END +GO + + + +CREATE FUNCTION [dbo].[sm_getnotnullnotempty] +( + @p1 nvarchar(max) = null, + @p2 nvarchar(max) = null +) +RETURNS nvarchar(max) +AS +BEGIN + IF @p1 IS NULL + return @p2 + IF @p1 ='' + return @p2 + + return @p1 +END +GO + + + +CREATE FUNCTION [dbo].[sm_getprimarykey_indexname] +( + @table_name nvarchar(1000) = null +) +RETURNS nvarchar(1000) +AS +BEGIN + DECLARE @index_name nvarchar(1000) + + SELECT @index_name = i.name + FROM sys.tables AS tbl + INNER JOIN sys.indexes AS i ON (i.index_id > 0 and i.is_hypothetical = 0) AND (i.object_id=tbl.object_id) + WHERE (i.is_unique=1 and i.is_disabled=0) and (tbl.name=@table_name) + + RETURN @index_name +END +GO + + + +CREATE PROCEDURE [dbo].[ProductTagCountLoadAll] +( + @StoreId int +) +AS +BEGIN + SET NOCOUNT ON + + SELECT pt.Id as [ProductTagId], COUNT(p.Id) as [ProductCount] + FROM ProductTag pt with (NOLOCK) + LEFT JOIN Product_ProductTag_Mapping pptm with (NOLOCK) ON pt.[Id] = pptm.[ProductTag_Id] + LEFT JOIN Product p with (NOLOCK) ON pptm.[Product_Id] = p.[Id] + WHERE + p.[Deleted] = 0 + AND p.Published = 1 + AND (@StoreId = 0 or (p.LimitedToStores = 0 OR EXISTS ( + SELECT 1 FROM [StoreMapping] sm + WHERE [sm].EntityId = p.Id AND [sm].EntityName = 'Product' and [sm].StoreId=@StoreId + ))) + GROUP BY pt.Id + ORDER BY pt.Id +END +GO \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Sql/StoredProcedures.Inverse - Copy.sql b/src/Libraries/SmartStore.Data/Sql/StoredProcedures.Inverse - Copy.sql new file mode 100644 index 0000000000..904868e026 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Sql/StoredProcedures.Inverse - Copy.sql @@ -0,0 +1,13 @@ + +DROP PROCEDURE [dbo].[ProductTagCountLoadAll] +GO + +DROP FUNCTION [dbo].[sm_getprimarykey_indexname] +GO + +DROP FUNCTION [dbo].[sm_getnotnullnotempty] +GO + +DROP FUNCTION [dbo].[sm_splitstring_to_table] +GO + diff --git a/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs b/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs index 8309f68139..c41c8f2690 100644 --- a/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs +++ b/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs @@ -158,46 +158,54 @@ orderby p.Id // 1st pass int pageIndex = -1; - while (true) - { - var products = PagedList.Create(query, ++pageIndex, 1000); - var map = GetPoductPictureMap(ctx, products.Select(x => x.Id).ToArray()); + while (true) + { + var products = PagedList.Create(query, ++pageIndex, 1000); + IDictionary map = GetPoductPictureMap(ctx, products.Select(x => x.Id).ToArray()); - foreach (var p in products) - { - int? fixedPictureId = null; - if (map.ContainsKey(p.Id)) - { - // Product has still a pic. - fixedPictureId = map[p.Id]; - } + foreach (var p in products) + { + int? fixedPictureId = null; + if (map.ContainsKey(p.Id)) + { + // Product has still a pic. + fixedPictureId = map[p.Id]; + } - // Update only if fixed PictureId differs from current - if (fixedPictureId != p.MainPictureId) - { - toUpate.Add(p.Id, fixedPictureId); - } - } + // Update only if fixed PictureId differs from current + if (fixedPictureId != p.MainPictureId) + { + toUpate.Add(p.Id, fixedPictureId); + } + } - if (!products.HasNextPage) - break; - } + if (!products.HasNextPage) + break; + } - // 2nd pass - foreach (var chunk in toUpate.Slice(1000)) - { - using (var tx = ctx.Database.BeginTransaction()) - { - foreach (var kvp in chunk) - { - context.ExecuteSqlCommand("Update [Product] Set [MainPictureId] = {0} WHERE [Id] = {1}", false, null, kvp.Value, kvp.Key); - } + // 2nd pass + foreach (var chunk in toUpate.Slice(1000)) + { + using (var tx = ctx.Database.BeginTransaction()) + { + foreach (var kvp in chunk) + { - context.SaveChanges(); - tx.Commit(); - } - } + if (DataSettings.Current.IsMySqlServer) + { + context.ExecuteSqlCommand("Update Product Set MainPictureId = {0} WHERE Product.Id = {1}", false, null, kvp.Value, kvp.Key); + } + else + { + context.ExecuteSqlCommand("Update [Product] Set [MainPictureId] = {0} WHERE [Id] = {1}", false, null, kvp.Value, kvp.Key); + } + } + context.SaveChanges(); + tx.Commit(); + } + } + return toUpate.Count; } diff --git a/src/Libraries/SmartStore.Data/app.config b/src/Libraries/SmartStore.Data/app.config index 9e763e6753..3319f104d7 100644 --- a/src/Libraries/SmartStore.Data/app.config +++ b/src/Libraries/SmartStore.Data/app.config @@ -16,4 +16,15 @@ - + + + + + + + + + + + + diff --git a/src/Libraries/SmartStore.Data/packages.config b/src/Libraries/SmartStore.Data/packages.config index fe0a5053cd..a5f782f356 100644 --- a/src/Libraries/SmartStore.Data/packages.config +++ b/src/Libraries/SmartStore.Data/packages.config @@ -5,6 +5,8 @@ - + + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Catalog/CategoryTreeChangeHandler.cs b/src/Libraries/SmartStore.Services/Catalog/CategoryTreeChangeHandler.cs index ebbe3e7632..8e0741a6c8 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CategoryTreeChangeHandler.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CategoryTreeChangeHandler.cs @@ -10,8 +10,6 @@ using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Security; using SmartStore.Core.Domain.Stores; -using SmartStore.Core.Events; -using SmartStore.Core.Search; namespace SmartStore.Services.Catalog { @@ -35,7 +33,7 @@ public CategoryTreeChangedEvent(CategoryTreeChangeReason reason) public CategoryTreeChangeReason Reason { get; private set; } } - public class CategoryTreeChangeHook : IDbSaveHook, IConsumer + public class CategoryTreeChangeHook : IDbSaveHook { private readonly ICommonServices _services; private readonly ICategoryService _categoryService; @@ -258,14 +256,6 @@ public void OnAfterSave(IHookedEntity entry) } } - void IConsumer.HandleEvent(IndexingCompletedEvent message) - { - if (message.IndexInfo.IsModified) - { - PublishEvent(CategoryTreeChangeReason.ElementCounts); - } - } - private void PublishEvent(CategoryTreeChangeReason reason) { if (_handledReasons[(int)reason] == false) diff --git a/src/Libraries/SmartStore.Services/Catalog/CopyProductService.cs b/src/Libraries/SmartStore.Services/Catalog/CopyProductService.cs index 15088a1c1f..52bb1b48e5 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CopyProductService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CopyProductService.cs @@ -493,6 +493,7 @@ private void ProcessLocalization(Product product, Product clone, IEnumerable /// Grouped product identifiers. - /// A value indicating whether to show hidden records. /// Map of associated products. - Multimap GetAssociatedProductsByProductIds(int[] productIds, bool showHidden = false); + Multimap GetAssociatedProducts(int[] productIds); /// /// Get applied discounts by product identifiers diff --git a/src/Libraries/SmartStore.Services/Catalog/Importer/ProductImporter.cs b/src/Libraries/SmartStore.Services/Catalog/Importer/ProductImporter.cs index 671ca44a8a..ce758e49ab 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Importer/ProductImporter.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Importer/ProductImporter.cs @@ -723,6 +723,7 @@ protected virtual int ProcessProducts( row.SetProperty(context.Result, (x) => x.OrderMinimumQuantity, 1); row.SetProperty(context.Result, (x) => x.OrderMaximumQuantity, 100); row.SetProperty(context.Result, (x) => x.QuantityStep, 1); + row.SetProperty(context.Result, (x) => x.QuantiyControlType); row.SetProperty(context.Result, (x) => x.HideQuantityControl); row.SetProperty(context.Result, (x) => x.AllowedQuantities); row.SetProperty(context.Result, (x) => x.DisableBuyButton); @@ -763,11 +764,6 @@ protected virtual int ProcessProducts( row.SetProperty(context.Result, (x) => x.CustomsTariffNumber); row.SetProperty(context.Result, (x) => x.CountryOfOriginId); - if (row.TryGetDataValue("QuantiyControlType", out int qct)) - { - product.QuantiyControlType = (QuantityControlType)qct; - } - if (row.TryGetDataValue("ProductTemplateViewPath", out string tvp, row.IsTransient)) { product.ProductTemplateId = tvp.HasValue() && templateViewPaths.ContainsKey(tvp) ? templateViewPaths[tvp] : defaultTemplateId; diff --git a/src/Libraries/SmartStore.Services/Catalog/PriceCalculationContext.cs b/src/Libraries/SmartStore.Services/Catalog/PriceCalculationContext.cs index eea8195b4f..b86356056c 100644 --- a/src/Libraries/SmartStore.Services/Catalog/PriceCalculationContext.cs +++ b/src/Libraries/SmartStore.Services/Catalog/PriceCalculationContext.cs @@ -61,7 +61,7 @@ public PriceCalculationContext(IEnumerable products, _productIdsAppliedDiscounts = new List(products.Where(x => x.HasDiscountsApplied).Select(x => x.Id)); _bundledProductIds = new List(products.Where(x => x.ProductType == ProductType.BundledProduct).Select(x => x.Id)); _groupedProductIds = new List(products.Where(x => x.ProductType == ProductType.GroupedProduct).Select(x => x.Id)); - } + } _funcAttributes = attributes; _funcAttributeCombinations = attributeCombinations; @@ -87,7 +87,6 @@ public void Clear() _associatedProducts?.Clear(); _bundledProductIds.Clear(); - _groupedProductIds.Clear(); } public LazyMultimap Attributes diff --git a/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs b/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs index fb33efe0a8..9f0c8c7c48 100644 --- a/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs @@ -432,7 +432,7 @@ public virtual PriceCalculationContext CreatePriceCalculationContext( x => _manufacturerService.GetProductManufacturersByProductIds(x), x => _productService.GetAppliedDiscountsByProductIds(x), x => _productService.GetBundleItemsByProductIds(x, true), - x => _productService.GetAssociatedProductsByProductIds(x) + x => _productService.GetAssociatedProducts(x) ); return context; @@ -712,29 +712,26 @@ public virtual decimal GetPreselectedPrice(Product product, Customer customer, C context = CreatePriceCalculationContext(customer: customer); } - if (product.ProductType == ProductType.BundledProduct) - { + if (product.ProductType == ProductType.BundledProduct) + { var bundleItems = context.ProductBundleItems .GetOrLoad(product.Id) .Select(x => new ProductBundleItemData(x)) .ToList(); - - var productIds = bundleItems.Select(x => x.Item.ProductId).ToList(); - productIds.Add(product.Id); - context.Collect(productIds); + var bundleItemContext = CreatePriceCalculationContext(bundleItems.Select(x => x.Item.Product), customer); // Fetch bundleItems.AdditionalCharge for all bundle items. foreach (var bundleItem in bundleItems.Where(x => x.Item.Product.CanBeBundleItem())) - { - var unused = GetPreselectedPrice(bundleItem.Item.Product, customer, currency, context, bundleItem, bundleItems); - } + { + var unused = GetPreselectedPrice(bundleItem.Item.Product, customer, currency, bundleItemContext, bundleItem, bundleItems); + } - result = GetPreselectedPrice(product, customer, currency, context, null, bundleItems); - } - else - { - result = GetPreselectedPrice(product, customer, currency, context, null, null); - } + result = GetPreselectedPrice(product, customer, currency, context, null, bundleItems); + } + else + { + result = GetPreselectedPrice(product, customer, currency, context, null, null); + } return result; } diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductService.cs b/src/Libraries/SmartStore.Services/Catalog/ProductService.cs index bd20bc65e9..733432fb31 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductService.cs @@ -624,7 +624,7 @@ public virtual Multimap GetProductTagsByProductIds(int[] produc return map; } - public virtual Multimap GetAssociatedProductsByProductIds(int[] productIds, bool showHidden = false) + public virtual Multimap GetAssociatedProducts(int[] productIds) { Guard.NotNull(productIds, nameof(productIds)); @@ -633,11 +633,9 @@ public virtual Multimap GetAssociatedProductsByProductIds(int[] pr return new Multimap(); } - // Ignore multistore. Expect multistore setting for associated products is the same as for parent grouped product. var query = _productRepository.TableUntracked - .Where(x => productIds.Contains(x.ParentGroupedProductId) && !x.Deleted && (showHidden || x.Published)) - .OrderBy(x => x.ParentGroupedProductId) - .ThenBy(x => x.DisplayOrder); + .Where(x => productIds.Contains(x.ParentGroupedProductId) && !x.Deleted && x.Published) + .OrderBy(x => x.DisplayOrder); var associatedProducts = query.ToList(); diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductTagService.cs b/src/Libraries/SmartStore.Services/Catalog/ProductTagService.cs index 798df87df0..d20873330b 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductTagService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductTagService.cs @@ -99,45 +99,43 @@ private class ProductTagWithCount /// Dictionary of "product tag ID : product count" private Dictionary GetProductCount(int storeId) { - string key = string.Format(PRODUCTTAG_COUNT_KEY, storeId); - return _cacheManager.Get(key, () => - { - IEnumerable tagCount = null; - - if (_commonSettings.UseStoredProceduresIfSupported && _dataProvider.StoredProceduresSupported) - { - //stored procedures are enabled and supported by the database. It's much faster than the LINQ implementation below - - var pStoreId = _dataProvider.GetParameter(); - pStoreId.ParameterName = "StoreId"; - pStoreId.Value = storeId; - pStoreId.DbType = DbType.Int32; - - tagCount = _dbContext.SqlQuery("Exec ProductTagCountLoadAll @StoreId", pStoreId); - } - else - { - //stored procedures aren't supported. Use LINQ - - tagCount = _productTagRepository.Table - .Select(pt => new ProductTagWithCount - { - ProductTagId = pt.Id, - ProductCount = (storeId > 0 && !QuerySettings.IgnoreMultiStore) ? - (from p in pt.Products - join sm in _storeMappingRepository.Table on new { pid = p.Id, pname = "Product" } equals new { pid = sm.EntityId, pname = sm.EntityName } into psm - from sm in psm.DefaultIfEmpty() - where (!p.LimitedToStores || storeId == sm.StoreId) && !p.Deleted && p.Published - select p).Count() : - pt.Products.Count(p => !p.Deleted && p.Published) - }); - } - - return tagCount.ToDictionary(x => x.ProductTagId, x => x.ProductCount); - }); - } + string key = string.Format(PRODUCTTAG_COUNT_KEY, storeId); + return _cacheManager.Get(key, () => + { + IEnumerable tagCount = null; + + if (_commonSettings.UseStoredProceduresIfSupported && _dataProvider.StoredProceduresSupported) + { + //stored procedures are enabled and supported by the database. It's much faster than the LINQ implementation below + var pStoreId = _dataProvider.GetParameter(); + pStoreId.ParameterName = "StoreId"; + pStoreId.Value = storeId; + pStoreId.DbType = DbType.Int32; + + tagCount = _dbContext.SqlQuery("Exec ProductTagCountLoadAll @StoreId", pStoreId); + } + else + { + //stored procedures aren't supported. Use LINQ + tagCount = _productTagRepository.Table + .Select(pt => new ProductTagWithCount + { + ProductTagId = pt.Id, + ProductCount = (storeId > 0 && !QuerySettings.IgnoreMultiStore) ? + (from p in pt.Products + join sm in _storeMappingRepository.Table on new { pid = p.Id, pname = "Product" } equals new { pid = sm.EntityId, pname = sm.EntityName } into psm + from sm in psm.DefaultIfEmpty() + where (!p.LimitedToStores || storeId == sm.StoreId) && !p.Deleted && p.Published + select p).Count() : + pt.Products.Count(p => !p.Deleted && p.Published) + }); + } + + return tagCount.ToDictionary(x => x.ProductTagId, x => x.ProductCount); + }); + } - #endregion + #endregion #region Methods diff --git a/src/Libraries/SmartStore.Services/Common/MaintenanceService.cs b/src/Libraries/SmartStore.Services/Common/MaintenanceService.cs index d5b3c8d746..069731f52a 100644 --- a/src/Libraries/SmartStore.Services/Common/MaintenanceService.cs +++ b/src/Libraries/SmartStore.Services/Common/MaintenanceService.cs @@ -50,7 +50,7 @@ public MaintenanceService(IDataProvider dataProvider, IDbContext dbContext, { //stored procedures are enabled and supported by the database. - // TODO: find a better way to get table name + //TODO: find a better way to get table name var tableName = typeof(T).Name; var result = _dbContext.SqlQuery(string.Format("SELECT IDENT_CURRENT('[{0}]')", tableName)); return Convert.ToInt32(result.FirstOrDefault()); diff --git a/src/Libraries/SmartStore.Services/Customers/CustomerReportService.cs b/src/Libraries/SmartStore.Services/Customers/CustomerReportService.cs index 6209807af8..abd95dd74d 100644 --- a/src/Libraries/SmartStore.Services/Customers/CustomerReportService.cs +++ b/src/Libraries/SmartStore.Services/Customers/CustomerReportService.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Data.Entity; using SmartStore.Core.Data; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Orders; @@ -107,8 +106,8 @@ group co by co.c.Id into g throw new ArgumentException("Wrong orderBy parameter", "orderBy"); } - // load 20 customers - query2 = query2.Take(() => 20); + //load 20 customers + query2 = query2.Take(20); var result = query2.ToList().Select(x => { diff --git a/src/Libraries/SmartStore.Services/Customers/CustomerService.cs b/src/Libraries/SmartStore.Services/Customers/CustomerService.cs index db565351e5..f9ee11d3e9 100644 --- a/src/Libraries/SmartStore.Services/Customers/CustomerService.cs +++ b/src/Libraries/SmartStore.Services/Customers/CustomerService.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Linq.Expressions; using System.Web; -using System.Data.Entity; using SmartStore.Collections; using SmartStore.Core; using SmartStore.Core.Data; @@ -444,7 +443,20 @@ from c in Customers.DefaultIfEmpty() && a.Value == clientIdent select c; } - else + else if (DataSettings.Current.IsMySqlServer) + { + query = from a in _gaRepository.TableUntracked + join c in _customerRepository.Table on a.EntityId equals c.Id into Customers + from c in Customers.DefaultIfEmpty() + where c.LastActivityDateUtc >= dateFrom + && c.Username == null + && c.Email == null + && a.KeyGroup == "Customer" + && a.Key == "ClientIdent" + && a.Value == clientIdent + select c; + } + else { query = from a in _gaRepository.TableUntracked join c in _customerRepository.Table on a.EntityId equals c.Id into Customers @@ -571,7 +583,7 @@ orderby cGroup.Key select cGroup.FirstOrDefault(); query = query.OrderBy(c => c.Id); - var customers = query.Take(() => maxItemsToDelete).ToList(); + var customers = query.Take(maxItemsToDelete).ToList(); int numberOfDeletedCustomers = 0; foreach (var c in customers) diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExportResult.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExportResult.cs index 57c85801ab..13032378ef 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExportResult.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExportResult.cs @@ -62,23 +62,4 @@ public class ExportFileInfo public RelatedEntityType? RelatedType { get; set; } } } - - - public class DataExportPreviewResult - { - public DataExportPreviewResult() - { - Data = new List(); - } - - /// - /// Preview data. - /// - public List Data { get; set; } - - /// - /// Number of total records. - /// - public int TotalRecords { get; set; } - } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs index 811dfa2abe..c7ae3da2af 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs @@ -6,7 +6,6 @@ using System.Text; using System.Threading; using System.Web; -using System.Data.Entity; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; @@ -42,9 +41,6 @@ using SmartStore.Services.Tax; using SmartStore.Utilities; using SmartStore.Utilities.Threading; -using SmartStore.Collections; -using SmartStore.Core.Domain.Directory; -using SmartStore.Core.Domain.Seo; namespace SmartStore.Services.DataExchange.Export { @@ -100,15 +96,14 @@ public partial class DataExporter : IDataExporter private readonly Lazy _catalogSettings; private readonly Lazy _localizationSettings; private readonly Lazy _taxSettings; - private readonly Lazy _seoSettings; - public DataExporter( + public DataExporter( ICommonServices services, IDbContext dbContext, HttpContextBase httpContext, Lazy priceFormatter, Lazy exportProfileService, - Lazy localizedEntityService, + Lazy localizedEntityService, Lazy languageService, Lazy urlRecordService, Lazy pictureService, @@ -146,8 +141,7 @@ public DataExporter( Lazy customerSettings, Lazy catalogSettings, Lazy localizationSettings, - Lazy taxSettings, - Lazy seoSettings) + Lazy taxSettings) { _services = services; _dbContext = dbContext; @@ -195,54 +189,29 @@ public DataExporter( _catalogSettings = catalogSettings; _localizationSettings = localizationSettings; _taxSettings = taxSettings; - _seoSettings = seoSettings; T = NullLocalizer.Instance; } public Localizer T { get; set; } - #endregion - - #region Utilities - - private LocalizedPropertyCollection CreateTranslationCollection(string keyGroup, IEnumerable entities) - { - if (entities == null || !entities.Any()) - { - return new LocalizedPropertyCollection(keyGroup, null, Enumerable.Empty()); - } - - var collection = _localizedEntityService.Value.GetLocalizedPropertyCollection(keyGroup, entities.Select(x => x.Id).Distinct().ToArray()); - return collection; - } - - private UrlRecordCollection CreateUrlRecordCollection(string entityName, IEnumerable entities) - { - if (entities == null || !entities.Any()) - { - return new UrlRecordCollection(entityName, null, Enumerable.Empty()); - } + #endregion - var collection = _urlRecordService.Value.GetUrlRecordCollection(entityName, null, entities.Select(x => x.Id).Distinct().ToArray()); - return collection; - } + #region Utilities - private void SetProgress(DataExporterContext ctx, int loadedRecords) + private void SetProgress(DataExporterContext ctx, int loadedRecords) { try { if (!ctx.IsPreview && loadedRecords > 0) { - var totalRecords = ctx.StatsPerStore.Sum(x => x.Value.TotalRecords); + int totalRecords = ctx.RecordsPerStore.Sum(x => x.Value); - if (ctx.Request.Profile.Limit > 0 && totalRecords > ctx.Request.Profile.Limit) - { - totalRecords = ctx.Request.Profile.Limit; - } + if (ctx.Request.Profile.Limit > 0 && totalRecords > ctx.Request.Profile.Limit) + totalRecords = ctx.Request.Profile.Limit; ctx.RecordCount = Math.Min(ctx.RecordCount + loadedRecords, totalRecords); - var msg = ctx.ProgressInfo.FormatInvariant(ctx.RecordCount.ToString("N0"), totalRecords.ToString("N0")); + var msg = ctx.ProgressInfo.FormatInvariant(ctx.RecordCount, totalRecords); ctx.Request.ProgressValueSetter.Invoke(ctx.RecordCount, totalRecords, msg); } } @@ -314,6 +283,7 @@ private void StreamToFile(DataExporterContext ctx, Stream stream, string path, A using (_rwLock.GetWriteLock()) using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) { + ctx.Log.Info($"Creating file {path}."); stream.CopyTo(fileStream); } } @@ -322,7 +292,7 @@ private void StreamToFile(DataExporterContext ctx, Stream stream, string path, A { ctx.ExecuteContext.Abort = DataExchangeAbortion.Hard; ctx.Log.ErrorFormat(ex, $"Failed to stream file {path}."); - ctx.Result.LastError = ex.ToAllMessages(true); + ctx.Result.LastError = ex.ToString(); } finally { @@ -341,11 +311,7 @@ private void DetachAllEntitiesAndClear(DataExporterContext ctx) { try { - ctx.AssociatedProductContext?.Clear(); - ctx.TranslationsPerPage?.Clear(); - ctx.UrlRecordsPerPage?.Clear(); - - if (ctx.ProductExportContext != null) + if (ctx.ProductExportContext != null) { _dbContext.DetachEntities(x => { @@ -412,74 +378,33 @@ x is Picture || x is ProductBundleItem || x is ProductCategory || x is ProductMa } } - private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx) + private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx, int pageIndex = 0) { - var stats = ctx.StatsPerStore[ctx.Store.Id]; - var offset = Math.Max(ctx.Request.Profile.Offset, 0); + var offset = Math.Max(ctx.Request.Profile.Offset, 0) + (pageIndex * PageSize); var limit = Math.Max(ctx.Request.Profile.Limit, 0); - var recordsPerSegment = ctx.IsPreview ? 0 : Math.Max(ctx.Request.Profile.BatchSize, 0); - var totalRecords = offset + stats.TotalRecords; - - ctx.LastId = stats.OffsetId; - - switch (ctx.Request.Provider.Value.EntityType) + var recordsPerSegment = (ctx.IsPreview ? 0 : Math.Max(ctx.Request.Profile.BatchSize, 0)); + var totalCount = Math.Max(ctx.Request.Profile.Offset, 0) + ctx.RecordsPerStore.First(x => x.Key == ctx.Store.Id).Value; + + switch (ctx.Request.Provider.Value.EntityType) { case ExportEntityType.Product: ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter ( - () => GetProducts(ctx), + skip => GetProducts(ctx, skip), entities => { - // Load data behind navigation properties for current queue in one go. - ctx.ProductExportContext = CreateProductExportContext(entities, ctx.ContextCustomer, ctx.Store.Id); - ctx.AssociatedProductContext = null; - - var context = ctx.ProductExportContext; - if (!ctx.Projection.NoGroupedProducts && entities.Where(x => x.ProductType == ProductType.GroupedProduct).Any()) - { - context.AssociatedProducts.LoadAll(); - var associatedProducts = context.AssociatedProducts.SelectMany(x => x.Value); - ctx.AssociatedProductContext = CreateProductExportContext(associatedProducts, ctx.ContextCustomer, ctx.Store.Id); - - var allProductEntities = entities.Where(x => x.ProductType != ProductType.GroupedProduct).Concat(associatedProducts); - ctx.TranslationsPerPage[nameof(Product)] = CreateTranslationCollection(nameof(Product), allProductEntities); - ctx.UrlRecordsPerPage[nameof(Product)] = CreateUrlRecordCollection(nameof(Product), allProductEntities); - } - else - { - ctx.TranslationsPerPage[nameof(Product)] = CreateTranslationCollection(nameof(Product), entities); - ctx.UrlRecordsPerPage[nameof(Product)] = CreateUrlRecordCollection(nameof(Product), entities); - } - - context.ProductTags.LoadAll(); - context.ProductBundleItems.LoadAll(); - context.SpecificationAttributes.LoadAll(); - context.Attributes.LoadAll(); - - var psa = context.SpecificationAttributes.SelectMany(x => x.Value); - var sao = psa.Select(x => x.SpecificationAttributeOption); - var sa = psa.Select(x => x.SpecificationAttributeOption.SpecificationAttribute); - - var pva = context.Attributes.SelectMany(x => x.Value); - var pvav = pva.SelectMany(x => x.ProductVariantAttributeValues); - var pa = pva.Select(x => x.ProductAttribute); - - ctx.TranslationsPerPage[nameof(ProductTag)] = CreateTranslationCollection(nameof(ProductTag), context.ProductTags.SelectMany(x => x.Value)); - ctx.TranslationsPerPage[nameof(ProductBundleItem)] = CreateTranslationCollection(nameof(ProductBundleItem), context.ProductBundleItems.SelectMany(x => x.Value)); - ctx.TranslationsPerPage[nameof(SpecificationAttribute)] = CreateTranslationCollection(nameof(SpecificationAttribute), sa); - ctx.TranslationsPerPage[nameof(SpecificationAttributeOption)] = CreateTranslationCollection(nameof(SpecificationAttributeOption), sao); - ctx.TranslationsPerPage[nameof(ProductAttribute)] = CreateTranslationCollection(nameof(ProductAttribute), pa); - ctx.TranslationsPerPage[nameof(ProductVariantAttributeValue)] = CreateTranslationCollection(nameof(ProductVariantAttributeValue), pvav); - }, + // load data behind navigation properties for current queue in one go + ctx.ProductExportContext = CreateProductExportContext(entities, ctx.ContextCustomer, ctx.Store.Id); + }, entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords + offset, PageSize, limit, recordsPerSegment, totalCount ); break; case ExportEntityType.Order: ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter ( - () => GetOrders(ctx), + skip => GetOrders(ctx, skip), entities => { ctx.OrderExportContext = new OrderExportContext(entities, @@ -490,24 +415,16 @@ private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx) x => _orderService.Value.GetOrderItemsByOrderIds(x), x => _shipmentService.Value.GetShipmentsByOrderIds(x) ); - - ctx.OrderExportContext.OrderItems.LoadAll(); - - var orderItems = ctx.OrderExportContext.OrderItems.SelectMany(x => x.Value); - var products = orderItems.Select(x => x.Product); - - ctx.TranslationsPerPage[nameof(Product)] = CreateTranslationCollection(nameof(Product), products); - ctx.UrlRecordsPerPage[nameof(Product)] = CreateUrlRecordCollection(nameof(Product), products); - }, + }, entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords + offset, PageSize, limit, recordsPerSegment, totalCount ); break; case ExportEntityType.Manufacturer: ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter ( - () => GetManufacturers(ctx), + skip => GetManufacturers(ctx, skip), entities => { ctx.ManufacturerExportContext = new ManufacturerExportContext(entities, @@ -516,14 +433,14 @@ private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx) ); }, entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords + offset, PageSize, limit, recordsPerSegment, totalCount ); break; case ExportEntityType.Category: ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter ( - () => GetCategories(ctx), + skip => GetCategories(ctx, skip), entities => { ctx.CategoryExportContext = new CategoryExportContext(entities, @@ -532,14 +449,14 @@ private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx) ); }, entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords + offset, PageSize, limit, recordsPerSegment, totalCount ); break; case ExportEntityType.Customer: ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter ( - () => GetCustomers(ctx), + skip => GetCustomers(ctx, skip), entities => { ctx.CustomerExportContext = new CustomerExportContext(entities, @@ -547,32 +464,27 @@ private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx) ); }, entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords + offset, PageSize, limit, recordsPerSegment, totalCount ); break; case ExportEntityType.NewsLetterSubscription: ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter ( - () => GetNewsLetterSubscriptions(ctx), + skip => GetNewsLetterSubscriptions(ctx, skip), null, entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords + offset, PageSize, limit, recordsPerSegment, totalCount ); break; case ExportEntityType.ShoppingCartItem: ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter ( - () => GetShoppingCartItems(ctx), - entities => - { - var products = entities.Select(x => x.Product); - ctx.TranslationsPerPage[nameof(Product)] = CreateTranslationCollection(nameof(Product), products); - ctx.UrlRecordsPerPage[nameof(Product)] = CreateUrlRecordCollection(nameof(Product), products); - }, - entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords + skip => GetShoppingCartItems(ctx, skip), + null, + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalCount ); break; @@ -639,7 +551,6 @@ private bool CallProvider(DataExporterContext ctx, string method, string path) } var context = ctx.ExecuteContext; - var provider = ctx.Request.Provider.Value; try { @@ -647,18 +558,18 @@ private bool CallProvider(DataExporterContext ctx, string method, string path) if (method == "Execute") { - provider.Execute(context); + ctx.Request.Provider.Value.Execute(context); } else if (method == "OnExecuted") { - provider.OnExecuted(context); + ctx.Request.Provider.Value.OnExecuted(context); } } catch (Exception ex) { context.Abort = DataExchangeAbortion.Hard; ctx.Log.ErrorFormat(ex, $"The provider failed at the {method.NaIfEmpty()} method."); - ctx.Result.LastError = ex.ToAllMessages(true); + ctx.Result.LastError = ex.ToString(); } finally { @@ -666,20 +577,14 @@ private bool CallProvider(DataExporterContext ctx, string method, string path) if (method == "Execute") { - var sb = new StringBuilder(); - sb.Append($"Provider reports {context.RecordsSucceeded.ToString("N0")} successfully exported record(s) of type {provider.EntityType.ToString()} to {path.NaIfEmpty()}."); + ctx.Log.Info($"Provider reports {context.RecordsSucceeded.ToString("N0")} successfully exported record(s) of type {ctx.Request.Provider.Value.EntityType.ToString()}."); - foreach (var unit in context.ExtraDataUnits.Where(x => x.RelatedType.HasValue && x.DataStream != null)) + foreach (var unit in context.ExtraDataUnits.Where(x => x.RelatedType.HasValue)) { - // Set unit.DataStream to null so that providers know that this unit should no longer be written to. - // We need these units later for ExportProfile.ResultInfo. - var unitPath = Path.Combine(context.Folder, unit.FileName); - StreamToFile(ctx, unit.DataStream, unitPath, x => unit.DataStream = null); + StreamToFile(ctx, unit.DataStream, Path.Combine(context.Folder, unit.FileName), x => unit.DataStream = null); - sb.Append($"\r\nProvider reports {unit.RecordsSucceeded.ToString("N0")} successfully exported record(s) of type {unit.RelatedType.Value.ToString()} to {unitPath.NaIfEmpty()}."); + ctx.Log.Info($"Provider reports {unit.RecordsSucceeded.ToString("N0")} successfully exported record(s) of type {unit.RelatedType.Value.ToString()}."); } - - ctx.Log.Info(sb.ToString()); } } @@ -747,10 +652,10 @@ private bool Deploy(DataExporterContext ctx, string zipPath) if (context.Result != null) { - context.Result.LastError = ex.ToAllMessages(true); + context.Result.LastError = ex.ToAllMessages(); } - ctx.Log.Error(ex, $"Deployment \"{deployment.Name}\" of type {deployment.DeploymentType.ToString()} failed."); + ctx.Log.ErrorFormat(ex, "Deployment \"{0}\" of type {1} failed", deployment.Name, deployment.DeploymentType.ToString()); } deployment.ResultInfo = XmlHelper.Serialize(context.Result); @@ -857,7 +762,7 @@ public virtual ProductExportContext CreateProductExportContext( x => _manufacturerService.Value.GetProductManufacturersByProductIds(x), x => _productService.Value.GetAppliedDiscountsByProductIds(x), x => _productService.Value.GetBundleItemsByProductIds(x, showHidden), - x => _productService.Value.GetAssociatedProductsByProductIds(x), + x => _productService.Value.GetAssociatedProducts(x), x => _pictureService.Value.GetPicturesByProductIds(x, maxPicturesPerProduct, true), x => _productService.Value.GetProductPicturesByProductIds(x), x => _productService.Value.GetProductTagsByProductIds(x), @@ -867,7 +772,7 @@ public virtual ProductExportContext CreateProductExportContext( return context; } - private IQueryable GetProductQuery(DataExporterContext ctx, int? skip, int take) + private IQueryable GetProductQuery(DataExporterContext ctx, int skip, int take) { IQueryable query = null; @@ -880,8 +785,8 @@ private IQueryable GetProductQuery(DataExporterContext ctx, int? skip, var searchQuery = new CatalogSearchQuery() .WithCurrency(ctx.ContextCurrency) .WithLanguage(ctx.ContextLanguage) - .VisibleIndividuallyOnly(true) .HasStoreId(ctx.Request.Profile.PerStore ? ctx.Store.Id : f.StoreId) + .VisibleIndividuallyOnly(true) .PriceBetween(f.PriceMinimum, f.PriceMaximum) .WithStockQuantity(f.AvailabilityMinimum, f.AvailabilityMaximum) .CreatedBetween(createdFrom, createdTo); @@ -911,108 +816,83 @@ private IQueryable GetProductQuery(DataExporterContext ctx, int? skip, searchQuery = searchQuery.WithProductId(f.IdMinimum, f.IdMaximum); query = _catalogSearchService.Value.PrepareQuery(searchQuery); + query = query.OrderByDescending(x => x.CreatedOnUtc); } else { query = ctx.Request.ProductQuery; } - query = query.OrderBy(x => x.Id); + if (skip > 0) + query = query.Skip(skip); - // Skip required for preview. - var skipValue = skip.GetValueOrDefault(); - if (skipValue > 0) - { - query = query.Skip(() => skipValue); - } - else if (ctx.LastId > 0) - { - query = query.Where(x => x.Id > ctx.LastId); - } - - if (take != int.MaxValue) - { - query = query.Take(() => take); - } + if (take != int.MaxValue) + query = query.Take(take); return query; } - private List GetProducts(DataExporterContext ctx) + private List GetProducts(DataExporterContext ctx, int skip) { - var stats = ctx.StatsPerStore[ctx.Store.Id]; - if (ctx.LastId >= stats.MaxId) - { - // End of data reached. - return null; - } + // we use ctx.EntityIdsPerSegment to avoid exporting products multiple times per segment\file (cause of associated products). - var products = GetProductQuery(ctx, null, PageSize).ToList(); - if (!products.Any()) - { - return null; - } + var result = new List(); - var result = new List(); - Multimap associatedProducts = null; - - if (ctx.Projection.NoGroupedProducts) - { - var groupedProductIds = products.Where(x => x.ProductType == ProductType.GroupedProduct).Select(x => x.Id).ToArray(); - associatedProducts = _productService.Value.GetAssociatedProductsByProductIds(groupedProductIds, true); - } + var products = GetProductQuery(ctx, skip, PageSize).ToList(); foreach (var product in products) { if (product.ProductType == ProductType.SimpleProduct || product.ProductType == ProductType.BundledProduct) { - // We use ctx.EntityIdsPerSegment to avoid exporting products multiple times per segment\file (cause of associated products). - if (ctx.EntityIdsPerSegment.Add(product.Id)) - { - result.Add(product); - } + if (!ctx.EntityIdsPerSegment.Contains(product.Id)) + { + result.Add(product); + ctx.EntityIdsPerSegment.Add(product.Id); + } } else if (product.ProductType == ProductType.GroupedProduct) { if (ctx.Projection.NoGroupedProducts) { - if (associatedProducts.ContainsKey(product.Id)) - { - foreach (var associatedProduct in associatedProducts[product.Id]) - { - if (ctx.Projection.OnlyIndividuallyVisibleAssociated && !associatedProduct.VisibleIndividually) - { - continue; - } - if (ctx.Filter.IsPublished.HasValue && ctx.Filter.IsPublished.Value != associatedProduct.Published) - { - continue; - } - - if (ctx.EntityIdsPerSegment.Add(associatedProduct.Id)) - { - result.Add(associatedProduct); - } - } - } + var searchQuery = new CatalogSearchQuery() + .HasParentGroupedProduct(product.Id) + .HasStoreId(ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId); + + if (ctx.Projection.OnlyIndividuallyVisibleAssociated) + searchQuery = searchQuery.VisibleIndividuallyOnly(true); + + if (ctx.Filter.IsPublished.HasValue) + searchQuery = searchQuery.PublishedOnly(ctx.Filter.IsPublished.Value); + + var query = _catalogSearchService.Value.PrepareQuery(searchQuery); + var associatedProducts = query.OrderBy(p => p.DisplayOrder).ToList(); + + foreach (var associatedProduct in associatedProducts) + { + if (!ctx.EntityIdsPerSegment.Contains(associatedProduct.Id)) + { + result.Add(associatedProduct); + ctx.EntityIdsPerSegment.Add(associatedProduct.Id); + } + } } else { - if (ctx.EntityIdsPerSegment.Add(product.Id)) + if (!ctx.EntityIdsPerSegment.Contains(product.Id)) { result.Add(product); + ctx.EntityIdsPerSegment.Add(product.Id); } } } } - ctx.LastId = products.Last().Id; - SetProgress(ctx, products.Count); + SetProgress(ctx, products.Count); return result; } - private IQueryable GetOrderQuery(DataExporterContext ctx, int? skip, int take) + private IQueryable GetOrderQuery(DataExporterContext ctx, int skip, int take) { var query = _orderService.Value.GetOrders( ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId, @@ -1026,160 +906,93 @@ private IQueryable GetOrderQuery(DataExporterContext ctx, int? skip, int null, null); - if (ctx.Request.EntitiesToExport.Any()) - { - query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); - } + if (ctx.Request.EntitiesToExport.Any()) + query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); - query = query.OrderBy(x => x.Id); + query = query.OrderByDescending(x => x.CreatedOnUtc); - var skipValue = skip.GetValueOrDefault(); - if (skipValue > 0) - { - query = query.Skip(() => skipValue); - } - else if (ctx.LastId > 0) - { - query = query.Where(x => x.Id > ctx.LastId); - } + if (skip > 0) + query = query.Skip(skip); - if (take != int.MaxValue) - { - query = query.Take(() => take); - } + if (take != int.MaxValue) + query = query.Take(take); return query; } - private List GetOrders(DataExporterContext ctx) + private List GetOrders(DataExporterContext ctx, int skip) { - var stats = ctx.StatsPerStore[ctx.Store.Id]; - if (ctx.LastId >= stats.MaxId) - { - // End of data reached. - return null; - } - - var orders = GetOrderQuery(ctx, null, PageSize).ToList(); - if (!orders.Any()) - { - return null; - } + var orders = GetOrderQuery(ctx, skip, PageSize).ToList(); if (ctx.Projection.OrderStatusChange != ExportOrderStatusChange.None) { ctx.SetLoadedEntityIds(orders.Select(x => x.Id)); } - ctx.LastId = orders.Last().Id; - SetProgress(ctx, orders.Count); + SetProgress(ctx, orders.Count); return orders; } - private IQueryable GetManufacturerQuery(DataExporterContext ctx, int? skip, int take) + private IQueryable GetManufacturerQuery(DataExporterContext ctx, int skip, int take) { var storeId = ctx.Request.Profile.PerStore ? ctx.Store.Id : 0; var query = _manufacturerService.Value.GetManufacturers(true, storeId); - if (ctx.Request.EntitiesToExport.Any()) - { - query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); - } + if (ctx.Request.EntitiesToExport.Any()) + query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); - query = query.OrderBy(x => x.Id); + query = query.OrderBy(x => x.DisplayOrder); - var skipValue = skip.GetValueOrDefault(); - if (skipValue > 0) - { - query = query.Skip(() => skipValue); - } - else if (ctx.LastId > 0) - { - query = query.Where(x => x.Id > ctx.LastId); - } + if (skip > 0) + query = query.Skip(skip); - if (take != int.MaxValue) - { - query = query.Take(() => take); - } + if (take != int.MaxValue) + query = query.Take(take); return query; } - private List GetManufacturers(DataExporterContext ctx) + private List GetManufacturers(DataExporterContext ctx, int skip) { - var stats = ctx.StatsPerStore[ctx.Store.Id]; - if (ctx.LastId >= stats.MaxId) - { - // End of data reached. - return null; - } + var manus = GetManufacturerQuery(ctx, skip, PageSize).ToList(); - var manus = GetManufacturerQuery(ctx, null, PageSize).ToList(); - if (!manus.Any()) - { - return null; - } - - ctx.LastId = manus.Last().Id; - SetProgress(ctx, manus.Count); + SetProgress(ctx, manus.Count); return manus; } - private IQueryable GetCategoryQuery(DataExporterContext ctx, int? skip, int take) + private IQueryable GetCategoryQuery(DataExporterContext ctx, int skip, int take) { var storeId = ctx.Request.Profile.PerStore ? ctx.Store.Id : 0; var query = _categoryService.Value.BuildCategoriesQuery(null, true, null, storeId); - if (ctx.Request.EntitiesToExport.Any()) - { - query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); - } + if (ctx.Request.EntitiesToExport.Any()) + query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); - query = query.OrderBy(x => x.Id); + query = query + .OrderBy(x => x.ParentCategoryId) + .ThenBy(x => x.DisplayOrder); - var skipValue = skip.GetValueOrDefault(); - if (skipValue > 0) - { - query = query.Skip(() => skipValue); - } - else if (ctx.LastId > 0) - { - query = query.Where(x => x.Id > ctx.LastId); - } + if (skip > 0) + query = query.Skip(skip); - if (take != int.MaxValue) - { - query = query.Take(() => take); - } + if (take != int.MaxValue) + query = query.Take(take); return query; } - private List GetCategories(DataExporterContext ctx) + private List GetCategories(DataExporterContext ctx, int skip) { - var stats = ctx.StatsPerStore[ctx.Store.Id]; - if (ctx.LastId >= stats.MaxId) - { - // End of data reached. - return null; - } + var categories = GetCategoryQuery(ctx, skip, PageSize).ToList(); - var categories = GetCategoryQuery(ctx, null, PageSize).ToList(); - if (!categories.Any()) - { - return null; - } - - ctx.LastId = categories.Last().Id; - SetProgress(ctx, categories.Count); + SetProgress(ctx, categories.Count); return categories; } - private IQueryable GetCustomerQuery(DataExporterContext ctx, int? skip, int take) + private IQueryable GetCustomerQuery(DataExporterContext ctx, int skip, int take) { var query = _customerRepository.Value.TableUntracked .Expand(x => x.BillingAddress) @@ -1189,30 +1002,20 @@ private IQueryable GetCustomerQuery(DataExporterContext ctx, int? skip .Expand(x => x.CustomerRoles) .Where(x => !x.Deleted); - if (ctx.Filter.IsActiveCustomer.HasValue) - { - query = query.Where(x => x.Active == ctx.Filter.IsActiveCustomer.Value); - } + if (ctx.Filter.IsActiveCustomer.HasValue) + query = query.Where(x => x.Active == ctx.Filter.IsActiveCustomer.Value); - if (ctx.Filter.IsTaxExempt.HasValue) - { - query = query.Where(x => x.IsTaxExempt == ctx.Filter.IsTaxExempt.Value); - } + if (ctx.Filter.IsTaxExempt.HasValue) + query = query.Where(x => x.IsTaxExempt == ctx.Filter.IsTaxExempt.Value); - if (ctx.Filter.CustomerRoleIds != null && ctx.Filter.CustomerRoleIds.Any()) - { - query = query.Where(x => x.CustomerRoles.Select(y => y.Id).Intersect(ctx.Filter.CustomerRoleIds).Any()); - } + if (ctx.Filter.CustomerRoleIds != null && ctx.Filter.CustomerRoleIds.Length > 0) + query = query.Where(x => x.CustomerRoles.Select(y => y.Id).Intersect(ctx.Filter.CustomerRoleIds).Any()); - if (ctx.Filter.BillingCountryIds != null && ctx.Filter.BillingCountryIds.Any()) - { - query = query.Where(x => x.BillingAddress != null && ctx.Filter.BillingCountryIds.Contains(x.BillingAddress.Id)); - } + if (ctx.Filter.BillingCountryIds != null && ctx.Filter.BillingCountryIds.Length > 0) + query = query.Where(x => x.BillingAddress != null && ctx.Filter.BillingCountryIds.Contains(x.BillingAddress.Id)); - if (ctx.Filter.ShippingCountryIds != null && ctx.Filter.ShippingCountryIds.Any()) - { - query = query.Where(x => x.ShippingAddress != null && ctx.Filter.ShippingCountryIds.Contains(x.ShippingAddress.Id)); - } + if (ctx.Filter.ShippingCountryIds != null && ctx.Filter.ShippingCountryIds.Length > 0) + query = query.Where(x => x.ShippingAddress != null && ctx.Filter.ShippingCountryIds.Contains(x.ShippingAddress.Id)); if (ctx.Filter.LastActivityFrom.HasValue) { @@ -1254,67 +1057,40 @@ private IQueryable GetCustomerQuery(DataExporterContext ctx, int? skip .Select(x => x.Customer); } - if (ctx.Request.EntitiesToExport.Any()) - { - query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); - } + if (ctx.Request.EntitiesToExport.Any()) + query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); - query = query.OrderBy(x => x.Id); + query = query.OrderByDescending(x => x.CreatedOnUtc); - var skipValue = skip.GetValueOrDefault(); - if (skipValue > 0) - { - query = query.Skip(() => skipValue); - } - else if (ctx.LastId > 0) - { - query = query.Where(x => x.Id > ctx.LastId); - } + if (skip > 0) + query = query.Skip(skip); - if (take != int.MaxValue) - { - query = query.Take(() => take); - } + if (take != int.MaxValue) + query = query.Take(take); return query; } - private List GetCustomers(DataExporterContext ctx) + private List GetCustomers(DataExporterContext ctx, int skip) { - var stats = ctx.StatsPerStore[ctx.Store.Id]; - if (ctx.LastId >= stats.MaxId) - { - // End of data reached. - return null; - } - - var customers = GetCustomerQuery(ctx, null, PageSize).ToList(); - if (!customers.Any()) - { - return null; - } + var customers = GetCustomerQuery(ctx, skip, PageSize).ToList(); - ctx.LastId = customers.Last().Id; - SetProgress(ctx, customers.Count); + SetProgress(ctx, customers.Count); return customers; } - private IQueryable GetNewsLetterSubscriptionQuery(DataExporterContext ctx, int? skip, int take) + private IQueryable GetNewsLetterSubscriptionQuery(DataExporterContext ctx, int skip, int take) { - var storeId = ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId; + var storeId = (ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId); var query = _subscriptionRepository.Value.TableUntracked; - if (storeId > 0) - { - query = query.Where(x => x.StoreId == storeId); - } + if (storeId > 0) + query = query.Where(x => x.StoreId == storeId); - if (ctx.Filter.IsActiveSubscriber.HasValue) - { - query = query.Where(x => x.Active == ctx.Filter.IsActiveSubscriber.Value); - } + if (ctx.Filter.IsActiveSubscriber.HasValue) + query = query.Where(x => x.Active == ctx.Filter.IsActiveSubscriber.Value); if (ctx.Filter.WorkingLanguageId != null && ctx.Filter.WorkingLanguageId != 0) { @@ -1343,66 +1119,43 @@ private IQueryable GetNewsLetterSubscriptionQuery(DataEx query = query.Where(x => createdTo >= x.CreatedOnUtc); } - if (ctx.Request.EntitiesToExport.Any()) - { - query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); - } + if (ctx.Request.EntitiesToExport.Any()) + query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); - query = query.OrderBy(x => x.Id); + query = query + .OrderBy(x => x.StoreId) + .ThenBy(x => x.Email); - var skipValue = skip.GetValueOrDefault(); - if (skipValue > 0) - { - query = query.Skip(() => skipValue); - } - else if (ctx.LastId > 0) - { - query = query.Where(x => x.Id > ctx.LastId); - } + if (skip > 0) + query = query.Skip(skip); - if (take != int.MaxValue) - { - query = query.Take(() => take); - } + if (take != int.MaxValue) + query = query.Take(take); return query; } - private List GetNewsLetterSubscriptions(DataExporterContext ctx) + private List GetNewsLetterSubscriptions(DataExporterContext ctx, int skip) { - var stats = ctx.StatsPerStore[ctx.Store.Id]; - if (ctx.LastId >= stats.MaxId) - { - // End of data reached. - return null; - } - - var subscriptions = GetNewsLetterSubscriptionQuery(ctx, null, PageSize).ToList(); - if (!subscriptions.Any()) - { - return null; - } + var subscriptions = GetNewsLetterSubscriptionQuery(ctx, skip, PageSize).ToList(); - ctx.LastId = subscriptions.Last().Id; - SetProgress(ctx, subscriptions.Count); + SetProgress(ctx, subscriptions.Count); return subscriptions; } - private IQueryable GetShoppingCartItemQuery(DataExporterContext ctx, int? skip, int take) + private IQueryable GetShoppingCartItemQuery(DataExporterContext ctx, int skip, int take) { - var storeId = ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId; + var storeId = (ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId); var query = _shoppingCartItemRepository.Value.TableUntracked .Expand(x => x.Customer) .Expand(x => x.Customer.CustomerRoles) .Expand(x => x.Product) - .Where(x => !x.Customer.Deleted); + .Where(x => !x.Customer.Deleted); // && !x.Product.Deleted - if (storeId > 0) - { - query = query.Where(x => x.StoreId == storeId); - } + if (storeId > 0) + query = query.Where(x => x.StoreId == storeId); if (ctx.Request.ActionOrigin.IsCaseInsensitiveEqual("CurrentCarts")) { @@ -1417,20 +1170,14 @@ private IQueryable GetShoppingCartItemQuery(DataExporterContex query = query.Where(x => x.ShoppingCartTypeId == ctx.Filter.ShoppingCartTypeId.Value); } - if (ctx.Filter.IsActiveCustomer.HasValue) - { - query = query.Where(x => x.Customer.Active == ctx.Filter.IsActiveCustomer.Value); - } + if (ctx.Filter.IsActiveCustomer.HasValue) + query = query.Where(x => x.Customer.Active == ctx.Filter.IsActiveCustomer.Value); - if (ctx.Filter.IsTaxExempt.HasValue) - { - query = query.Where(x => x.Customer.IsTaxExempt == ctx.Filter.IsTaxExempt.Value); - } + if (ctx.Filter.IsTaxExempt.HasValue) + query = query.Where(x => x.Customer.IsTaxExempt == ctx.Filter.IsTaxExempt.Value); - if (ctx.Filter.CustomerRoleIds != null && ctx.Filter.CustomerRoleIds.Any()) - { - query = query.Where(x => x.Customer.CustomerRoles.Select(y => y.Id).Intersect(ctx.Filter.CustomerRoleIds).Any()); - } + if (ctx.Filter.CustomerRoleIds != null && ctx.Filter.CustomerRoleIds.Length > 0) + query = query.Where(x => x.Customer.CustomerRoles.Select(y => y.Id).Intersect(ctx.Filter.CustomerRoleIds).Any()); if (ctx.Filter.LastActivityFrom.HasValue) { @@ -1465,56 +1212,37 @@ private IQueryable GetShoppingCartItemQuery(DataExporterContex query = query.Where(x => x.BundleItemId == null); } - if (ctx.Request.EntitiesToExport.Any()) - { - query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); - } + if (ctx.Request.EntitiesToExport.Any()) + query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); - query = query.OrderBy(x => x.Id); + query = query + .OrderBy(x => x.ShoppingCartTypeId) + .ThenBy(x => x.CustomerId) + .ThenByDescending(x => x.CreatedOnUtc); - var skipValue = skip.GetValueOrDefault(); - if (skipValue > 0) - { - query = query.Skip(() => skipValue); - } - else if (ctx.LastId > 0) - { - query = query.Where(x => x.Id > ctx.LastId); - } + if (skip > 0) + query = query.Skip(skip); - if (take != int.MaxValue) - { - query = query.Take(take); - } + if (take != int.MaxValue) + query = query.Take(take); return query; } - private List GetShoppingCartItems(DataExporterContext ctx) + private List GetShoppingCartItems(DataExporterContext ctx, int skip) { - var stats = ctx.StatsPerStore[ctx.Store.Id]; - if (ctx.LastId >= stats.MaxId) - { - // End of data reached. - return null; - } + var shoppingCartItems = GetShoppingCartItemQuery(ctx, skip, PageSize).ToList(); - var cartItems = GetShoppingCartItemQuery(ctx, null, PageSize).ToList(); - if (!cartItems.Any()) - { - return null; - } - - ctx.LastId = cartItems.Last().Id; - SetProgress(ctx, cartItems.Count); + SetProgress(ctx, shoppingCartItems.Count); - return cartItems; + return shoppingCartItems; } #endregion - private List Init(DataExporterContext ctx) + private List Init(DataExporterContext ctx, int? totalRecords = null) { + // Init base things that are even required for preview. Init all other things (regular export) in ExportCoreOuter. List result = null; ctx.ContextCurrency = _currencyService.Value.GetCurrencyById(ctx.Projection.CurrencyId ?? 0) ?? _services.WorkContext.WorkingCurrency; @@ -1524,100 +1252,62 @@ private List Init(DataExporterContext ctx) ctx.Stores = _services.StoreService.GetAllStores().ToDictionary(x => x.Id, x => x); ctx.Languages = _languageService.Value.GetAllLanguages(true).ToDictionary(x => x.Id, x => x); - if (ctx.IsPreview) - { - foreach (var name in new string[] { nameof(Currency), nameof(Country), nameof(StateProvince), nameof(DeliveryTime), nameof(QuantityUnit), nameof(Category), nameof(Manufacturer) }) - { - ctx.Translations[name] = new LocalizedPropertyCollection(name, null, Enumerable.Empty()); - } - - foreach (var name in new string[] { nameof(Product), nameof(ProductTag), nameof(ProductBundleItem), nameof(SpecificationAttribute), nameof(SpecificationAttributeOption), - nameof(ProductAttribute), nameof(ProductVariantAttributeValue) }) - { - ctx.TranslationsPerPage[name] = new LocalizedPropertyCollection(name, null, Enumerable.Empty()); - } - - ctx.UrlRecords[nameof(Category)] = new UrlRecordCollection(nameof(Category), null, Enumerable.Empty()); - ctx.UrlRecords[nameof(Manufacturer)] = new UrlRecordCollection(nameof(Manufacturer), null, Enumerable.Empty()); - ctx.UrlRecordsPerPage[nameof(Product)] = new UrlRecordCollection(nameof(Product), null, Enumerable.Empty()); - } - else - { - // Get all translations and slugs for certain entities in one go. - ctx.Translations[nameof(Currency)] = _localizedEntityService.Value.GetLocalizedPropertyCollection(nameof(Currency), null); - ctx.Translations[nameof(Country)] = _localizedEntityService.Value.GetLocalizedPropertyCollection(nameof(Country), null); - ctx.Translations[nameof(StateProvince)] = _localizedEntityService.Value.GetLocalizedPropertyCollection(nameof(StateProvince), null); - ctx.Translations[nameof(DeliveryTime)] = _localizedEntityService.Value.GetLocalizedPropertyCollection(nameof(DeliveryTime), null); - ctx.Translations[nameof(QuantityUnit)] = _localizedEntityService.Value.GetLocalizedPropertyCollection(nameof(QuantityUnit), null); - ctx.Translations[nameof(Manufacturer)] = _localizedEntityService.Value.GetLocalizedPropertyCollection(nameof(Manufacturer), null); - ctx.Translations[nameof(Category)] = _localizedEntityService.Value.GetLocalizedPropertyCollection(nameof(Category), null); - - ctx.UrlRecords[nameof(Category)] = _urlRecordService.Value.GetUrlRecordCollection(nameof(Category), null, null); - ctx.UrlRecords[nameof(Manufacturer)] = _urlRecordService.Value.GetUrlRecordCollection(nameof(Manufacturer), null, null); - } - - if (!ctx.IsPreview && ctx.Request.Profile.PerStore) + if (!ctx.IsPreview && ctx.Request.Profile.PerStore) { result = new List(ctx.Stores.Values.Where(x => x.Id == ctx.Filter.StoreId || ctx.Filter.StoreId == 0)); } else { - int? storeId = ctx.Filter.StoreId == 0 ? ctx.Projection.StoreId : ctx.Filter.StoreId; + int? storeId = (ctx.Filter.StoreId == 0 ? ctx.Projection.StoreId : ctx.Filter.StoreId); + ctx.Store = ctx.Stores.Values.FirstOrDefault(x => x.Id == (storeId ?? _services.StoreContext.CurrentStore.Id)); result = new List { ctx.Store }; } - // Get record stats for each store. + // get total records for progress foreach (var store in result) { ctx.Store = store; - IQueryable query = null; + int totalCount = 0; - switch (ctx.Request.Provider.Value.EntityType) - { - case ExportEntityType.Product: - query = GetProductQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue); - break; - case ExportEntityType.Order: - query = GetOrderQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue); - break; - case ExportEntityType.Manufacturer: - query = GetManufacturerQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue); - break; - case ExportEntityType.Category: - query = GetCategoryQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue); - break; - case ExportEntityType.Customer: - query = GetCustomerQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue); - break; - case ExportEntityType.NewsLetterSubscription: - query = GetNewsLetterSubscriptionQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue); - break; - case ExportEntityType.ShoppingCartItem: - query = GetShoppingCartItemQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue); - break; - } - - var stats = new RecordStats - { - TotalRecords = query.Count() - }; - - if (!ctx.IsPreview) - { - stats.MaxId = query.Max(x => (int?)x.Id) ?? 0; - if (ctx.Request.Profile.Offset > 0) - { - stats.OffsetId = query.Select(x => x.Id).FirstOrDefault(); - } - } + if (totalRecords.HasValue) + { + totalCount = totalRecords.Value; // speed up preview by not counting total at each page + } + else + { + switch (ctx.Request.Provider.Value.EntityType) + { + case ExportEntityType.Product: + totalCount = GetProductQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); + break; + case ExportEntityType.Order: + totalCount = GetOrderQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); + break; + case ExportEntityType.Manufacturer: + totalCount = GetManufacturerQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); + break; + case ExportEntityType.Category: + totalCount = GetCategoryQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); + break; + case ExportEntityType.Customer: + totalCount = GetCustomerQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); + break; + case ExportEntityType.NewsLetterSubscription: + totalCount = GetNewsLetterSubscriptionQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); + break; + case ExportEntityType.ShoppingCartItem: + totalCount = GetShoppingCartItemQuery(ctx, ctx.Request.Profile.Offset, int.MaxValue).Count(); + break; + } + } - ctx.StatsPerStore[store.Id] = stats; + ctx.RecordsPerStore.Add(store.Id, totalCount); } - return result; + return result; } private void ExportCoreInner(DataExporterContext ctx, Store store) @@ -1677,7 +1367,7 @@ private void ExportCoreInner(DataExporterContext ctx, Store store) context.PublicFolderPath = publicDeployment.GetDeploymentFolder(true); context.PublicFolderUrl = publicDeployment.GetPublicFolderUrl(_services, ctx.Store); - var fileExtension = provider.Value.FileExtension.HasValue() ? provider.Value.FileExtension.ToLower().EnsureStartsWith(".") : ""; + var fileExtension = provider.Value.FileExtension.HasValue() ? provider.Value.FileExtension.ToLower().EnsureStartsWith(".") : ""; using (var segmenter = CreateSegmenter(ctx)) { @@ -1724,6 +1414,7 @@ private void ExportCoreInner(DataExporterContext ctx, Store store) ctx.EntityIdsPerSegment.Clear(); DetachAllEntitiesAndClear(ctx); + _localizedEntityService.Value.ClearCache(); if (context.IsMaxFailures) { @@ -1818,15 +1509,16 @@ private void ExportCoreOuter(DataExporterContext ctx) } } - // lazyLoading: false, proxyCreation: false impossible due to price calculation. - using (var scope = new DbContextScope(_dbContext, autoDetectChanges: false, proxyCreation: true, validateOnSave: false, forceNoTracking: true)) + // lazyLoading: false, proxyCreation: false impossible. how to identify all properties of all data levels of all entities + // that require manual resolving for now and for future? fragile, susceptible to faults (e.g. price calculation)... + using (var scope = new DbContextScope(_dbContext, autoDetectChanges: false, proxyCreation: true, validateOnSave: false, forceNoTracking: true)) { ctx.DeliveryTimes = _deliveryTimeService.Value.GetAllDeliveryTimes().ToDictionary(x => x.Id); ctx.QuantityUnits = _quantityUnitService.Value.GetAllQuantityUnits().ToDictionary(x => x.Id); ctx.ProductTemplates = _productTemplateService.Value.GetAllProductTemplates().ToDictionary(x => x.Id, x => x.ViewPath); ctx.CategoryTemplates = _categoryTemplateService.Value.GetAllCategoryTemplates().ToDictionary(x => x.Id, x => x.ViewPath); - if (ctx.Request.Provider.Value.EntityType == ExportEntityType.Product || + if (ctx.Request.Provider.Value.EntityType == ExportEntityType.Product || ctx.Request.Provider.Value.EntityType == ExportEntityType.Order) { ctx.Countries = _countryService.Value.GetAllCountries(true).ToDictionary(x => x.Id, x => x); @@ -1886,7 +1578,7 @@ private void ExportCoreOuter(DataExporterContext ctx) catch (Exception ex) { logger.ErrorsAll(ex); - ctx.Result.LastError = ex.ToAllMessages(true); + ctx.Result.LastError = ex.ToString(); } finally { @@ -1894,7 +1586,6 @@ private void ExportCoreOuter(DataExporterContext ctx) { if (!ctx.IsPreview && profile.Id != 0) { - ctx.Result.Files = ctx.Result.Files.OrderBy(x => x.RelatedType).ToList(); profile.ResultInfo = XmlHelper.Serialize(ctx.Result); _exportProfileService.Value.UpdateExportProfile(profile); @@ -1906,6 +1597,7 @@ private void ExportCoreOuter(DataExporterContext ctx) } DetachAllEntitiesAndClear(ctx); + _localizedEntityService.Value.ClearCache(); try { @@ -1917,8 +1609,6 @@ private void ExportCoreOuter(DataExporterContext ctx) ctx.QuantityUnits.Clear(); ctx.DeliveryTimes.Clear(); ctx.Stores.Clear(); - ctx.Translations.Clear(); - ctx.UrlRecords.Clear(); ctx.Request.CustomData.Clear(); @@ -1947,14 +1637,10 @@ private void ExportCoreOuter(DataExporterContext ctx) { int? orderStatusId = null; - if (ctx.Projection.OrderStatusChange == ExportOrderStatusChange.Processing) - { - orderStatusId = (int)OrderStatus.Processing; - } - else if (ctx.Projection.OrderStatusChange == ExportOrderStatusChange.Complete) - { - orderStatusId = (int)OrderStatus.Complete; - } + if (ctx.Projection.OrderStatusChange == ExportOrderStatusChange.Processing) + orderStatusId = (int)OrderStatus.Processing; + else if (ctx.Projection.OrderStatusChange == ExportOrderStatusChange.Complete) + orderStatusId = (int)OrderStatus.Complete; using (var scope = new DbContextScope(_dbContext, false, null, false, false, false, false)) { @@ -1973,7 +1659,7 @@ private void ExportCoreOuter(DataExporterContext ctx) catch (Exception ex) { logger.ErrorsAll(ex); - ctx.Result.LastError = ex.ToAllMessages(true); + ctx.Result.LastError = ex.ToString(); } } } @@ -1996,69 +1682,77 @@ public DataExportResult Export(DataExportRequest request, CancellationToken canc return ctx.Result; } - public DataExportPreviewResult Preview(DataExportRequest request, int pageIndex) + public IList Preview(DataExportRequest request, int pageIndex, int? totalRecords = null) { - var result = new DataExportPreviewResult(); - var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(5.0)); + var result = new List(); + var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(5.0)); var ctx = new DataExporterContext(request, cancellation.Token, true); - var unused = Init(ctx); - var skip = Math.Max(ctx.Request.Profile.Offset, 0) + (pageIndex * PageSize); + var unused = Init(ctx, totalRecords); + var offset = Math.Max(ctx.Request.Profile.Offset, 0) + (pageIndex * PageSize); if (!HasPermission(ctx)) { throw new SmartException(T("Admin.AccessDenied")); } - result.TotalRecords = ctx.StatsPerStore.First().Value.TotalRecords; - - switch (request.Provider.Value.EntityType) + switch (request.Provider.Value.EntityType) { case ExportEntityType.Product: { - var items = GetProductQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + var items = GetProductQuery(ctx, offset, PageSize).ToList(); + items.Each(x => result.Add(ToDynamic(ctx, x))); } break; case ExportEntityType.Order: { - var items = GetOrderQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + var items = GetOrderQuery(ctx, offset, PageSize).ToList(); + items.Each(x => result.Add(ToDynamic(ctx, x))); } break; case ExportEntityType.Category: { - var items = GetCategoryQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + var items = GetCategoryQuery(ctx, offset, PageSize).ToList(); + items.Each(x => result.Add(ToDynamic(ctx, x))); } break; case ExportEntityType.Manufacturer: { - var items = GetManufacturerQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + var items = GetManufacturerQuery(ctx, offset, PageSize).ToList(); + items.Each(x => result.Add(ToDynamic(ctx, x))); } break; case ExportEntityType.Customer: { - var items = GetCustomerQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + var items = GetCustomerQuery(ctx, offset, PageSize).ToList(); + items.Each(x => result.Add(ToDynamic(ctx, x))); } break; case ExportEntityType.NewsLetterSubscription: { - var items = GetNewsLetterSubscriptionQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + var items = GetNewsLetterSubscriptionQuery(ctx, offset, PageSize).ToList(); + items.Each(x => result.Add(ToDynamic(ctx, x))); } break; case ExportEntityType.ShoppingCartItem: { - var items = GetShoppingCartItemQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + var items = GetShoppingCartItemQuery(ctx, offset, PageSize).ToList(); + items.Each(x => result.Add(ToDynamic(ctx, x))); } break; } - return result; + return result; + } + + public int GetDataCount(DataExportRequest request) + { + var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(5.0)); + var ctx = new DataExporterContext(request, cancellation.Token, true); + var unused = Init(ctx); + + var totalCount = ctx.RecordsPerStore.First().Value; + return totalCount; } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/FileSystemFilePublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/FileSystemFilePublisher.cs index 346cf02176..8fdfe9ba43 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/FileSystemFilePublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/FileSystemFilePublisher.cs @@ -19,7 +19,7 @@ public virtual void Publish(ExportDeploymentContext context, ExportDeployment de context.Result.LastError = context.T("Admin.DataExchange.Export.Deployment.CopyFileFailed"); } - context.Log.Info($"Copied export data files to {targetFolder}."); + context.Log.Info("Copied export data files to " + targetFolder); } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/PublicFolderPublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/PublicFolderPublisher.cs index 7e9d67beb3..a008e69b0c 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/PublicFolderPublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/PublicFolderPublisher.cs @@ -27,7 +27,7 @@ public virtual void Publish(ExportDeploymentContext context, ExportDeployment de File.Copy(context.ZipPath, destinationFile, true); - context.Log.Info($"Copied zipped export data to {destinationFile}."); + context.Log.Info("Copied zipped export data to " + destinationFile); } } else @@ -37,7 +37,7 @@ public virtual void Publish(ExportDeploymentContext context, ExportDeployment de context.Result.LastError = context.T("Admin.DataExchange.Export.Deployment.CopyFileFailed"); } - context.Log.Info($"Copied export data files to {destinationFolder}."); + context.Log.Info("Copied export data files to " + destinationFolder); } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/DynamicEntityHelper.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/DynamicEntityHelper.cs index 7258f5d6c9..8bc039898c 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/DynamicEntityHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/DynamicEntityHelper.cs @@ -25,16 +25,16 @@ using SmartStore.Core.Html; using SmartStore.Services.Catalog; using SmartStore.Services.Catalog.Modelling; -using SmartStore.Services.Customers; using SmartStore.Services.DataExchange.Export.Events; using SmartStore.Services.DataExchange.Export.Internal; using SmartStore.Services.Localization; using SmartStore.Services.Media; using SmartStore.Services.Seo; +using SmartStore.Services.Customers; namespace SmartStore.Services.DataExchange.Export { - public partial class DataExporter + public partial class DataExporter { private readonly string[] _orderCustomerAttributes = new string[] { @@ -49,13 +49,13 @@ private void PrepareProductDescription(DataExporterContext ctx, dynamic dynObjec { try { - var languageId = ctx.LanguageId; + var languageId = (ctx.Projection.LanguageId ?? 0); string description = ""; - // Description merging. + // description merging if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.None) { - // Export empty description. + // export empty description } else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ShortDescriptionOrNameIfEmpty) { @@ -87,38 +87,37 @@ private void PrepareProductDescription(DataExporterContext ctx, dynamic dynObjec { var productManus = ctx.ProductExportContext.ProductManufacturers.GetOrLoad(product.Id); - if (productManus != null && productManus.Any()) - { - var translations = ctx.Translations[nameof(Manufacturer)]; - var manufacturer = productManus.First().Manufacturer; - description = translations.GetValue(languageId, manufacturer.Id, nameof(manufacturer.Name)) ?? manufacturer.Name; - } + if (productManus != null && productManus.Any()) + description = productManus.First().Manufacturer.GetLocalized(x => x.Name, languageId, true, false); description = description.Grow((string)dynObject.Name, " "); - description = ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ManufacturerAndNameAndShortDescription - ? description.Grow((string)dynObject.ShortDescription, " ") - : description.Grow((string)dynObject.FullDescription, " "); + + if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ManufacturerAndNameAndShortDescription) + description = description.Grow((string)dynObject.ShortDescription, " "); + else + description = description.Grow((string)dynObject.FullDescription, " "); } - // Append text. + // append text if (ctx.Projection.AppendDescriptionText.HasValue() && ((string)dynObject.ShortDescription).IsEmpty() && ((string)dynObject.FullDescription).IsEmpty()) { string[] appendText = ctx.Projection.AppendDescriptionText.SplitSafe(","); if (appendText.Length > 0) { - var rnd = new Random().Next(0, appendText.Length - 1); + var rnd = (new Random()).Next(0, appendText.Length - 1); + description = description.Grow(appendText.SafeGet(rnd), " "); } } - // Remove critical characters. + // remove critical characters if (description.HasValue() && ctx.Projection.RemoveCriticalCharacters) { foreach (var str in ctx.Projection.CriticalCharacters.SplitSafe(",")) description = description.Replace(str, ""); } - // Convert to plain text. + // convert to plain text if (description.HasValue() && ctx.Projection.DescriptionToPlainText) { //Regex reg = new Regex("<[^>]+>", RegexOptions.IgnoreCase); @@ -160,129 +159,95 @@ private decimal CalculatePrice( ICollection attributeValues) { var price = product.Price; - var productContext = ctx.ProductExportContext as PriceCalculationContext; - var associatedProductContext = ctx.AssociatedProductContext as PriceCalculationContext; + var priceCalculationContext = ctx.ProductExportContext as PriceCalculationContext; - if (combination != null) + if (combination != null) { // price for attribute combination var attributesTotalPriceBase = decimal.Zero; if (attributeValues != null) { - attributeValues.Each(x => attributesTotalPriceBase += _priceCalculationService.Value.GetProductVariantAttributeValuePriceAdjustment(x, product, ctx.ContextCustomer, productContext)); + attributeValues.Each(x => attributesTotalPriceBase += _priceCalculationService.Value.GetProductVariantAttributeValuePriceAdjustment(x, product, ctx.ContextCustomer, priceCalculationContext)); } - price = _priceCalculationService.Value.GetFinalPrice(product, null, ctx.ContextCustomer, attributesTotalPriceBase, true, 1, null, productContext); + price = _priceCalculationService.Value.GetFinalPrice(product, null, ctx.ContextCustomer, attributesTotalPriceBase, true, 1, null, priceCalculationContext); } else if (ctx.Projection.PriceType.HasValue) - { - var priceType = ctx.Projection.PriceType.Value; - - if (product.ProductType == ProductType.GroupedProduct) - { - var associatedProducts = productContext.AssociatedProducts.GetOrLoad(product.Id); - if (associatedProducts.Any()) - { - var firstAssociatedProduct = associatedProducts.First(); - - if (priceType == PriceDisplayType.PreSelectedPrice) - { - price = _priceCalculationService.Value.GetPreselectedPrice(firstAssociatedProduct, ctx.ContextCustomer, ctx.ContextCurrency, associatedProductContext); - } - else if (priceType == PriceDisplayType.PriceWithoutDiscountsAndAttributes) - { - price = _priceCalculationService.Value.GetFinalPrice(firstAssociatedProduct, null, ctx.ContextCustomer, decimal.Zero, false, 1, null, associatedProductContext); - } - else if (priceType == PriceDisplayType.LowestPrice) - { - price = _priceCalculationService.Value.GetLowestPrice(product, ctx.ContextCustomer, associatedProductContext, associatedProducts, out _) ?? decimal.Zero; - } - } - } - else - { - if (priceType == PriceDisplayType.PreSelectedPrice) - { - price = _priceCalculationService.Value.GetPreselectedPrice(product, ctx.ContextCustomer, ctx.ContextCurrency, productContext); - } - else if (priceType == PriceDisplayType.PriceWithoutDiscountsAndAttributes) - { - price = _priceCalculationService.Value.GetFinalPrice(product, null, ctx.ContextCustomer, decimal.Zero, false, 1, null, productContext); - } - else if (priceType == PriceDisplayType.LowestPrice) - { - price = _priceCalculationService.Value.GetLowestPrice(product, ctx.ContextCustomer, productContext, out _); - } - } + { + // price for product + if (ctx.Projection.PriceType.Value == PriceDisplayType.LowestPrice) + { + bool displayFromMessage; + price = _priceCalculationService.Value.GetLowestPrice(product, ctx.ContextCustomer, priceCalculationContext, out displayFromMessage); + } + else if (ctx.Projection.PriceType.Value == PriceDisplayType.PreSelectedPrice) + { + price = _priceCalculationService.Value.GetPreselectedPrice(product, ctx.ContextCustomer, ctx.ContextCurrency, priceCalculationContext); + } + else if (ctx.Projection.PriceType.Value == PriceDisplayType.PriceWithoutDiscountsAndAttributes) + { + price = _priceCalculationService.Value.GetFinalPrice(product, null, ctx.ContextCustomer, decimal.Zero, false, 1, null, priceCalculationContext); + } } return ConvertPrice(ctx, product, price) ?? price; } - private List GetLocalized( - DataExporterContext ctx, - LocalizedPropertyCollection translations, - UrlRecordCollection urlRecords, - T entity, - params Expression>[] keySelectors) - where T : BaseEntity, ILocalizedEntity - { - Guard.NotNull(translations, nameof(translations)); + private List GetLocalized(DataExporterContext ctx, T entity, params Expression>[] keySelectors) + where T : BaseEntity, ILocalizedEntity + { + if (ctx.Languages.Count <= 1) + return null; - if (ctx.Languages.Count <= 1) - { - return null; - } + var localized = new List(); - var localized = new List(); - var localeKeyGroup = entity.GetEntityName(); - //var isSlugSupported = typeof(ISlugSupported).IsAssignableFrom(typeof(T)); + var localeKeyGroup = typeof(T).Name; + var isSlugSupported = typeof(ISlugSupported).IsAssignableFrom(typeof(T)); - foreach (var language in ctx.Languages) - { - var languageCulture = language.Value.LanguageCulture.EmptyNull().ToLower(); + foreach (var language in ctx.Languages) + { + var languageCulture = language.Value.LanguageCulture.EmptyNull().ToLower(); - // Add SEO name. - if (urlRecords != null) - { - var value = urlRecords.GetSlug(language.Value.Id, entity.Id, false); - if (value.HasValue()) - { - dynamic exp = new HybridExpando(); - exp.Culture = languageCulture; - exp.LocaleKey = "SeName"; - exp.LocaleValue = value; - - localized.Add(exp); - } - } + // add SeName + if (isSlugSupported) + { + var value = _urlRecordService.Value.GetActiveSlug(entity.Id, localeKeyGroup, language.Value.Id); + if (value.HasValue()) + { + dynamic exp = new HybridExpando(); + exp.Culture = languageCulture; + exp.LocaleKey = "SeName"; + exp.LocaleValue = value; - // Add localized property value. - foreach (var keySelector in keySelectors) - { - var member = keySelector.Body as MemberExpression; - var propInfo = member.Member as PropertyInfo; - string localeKey = propInfo.Name; - var value = translations.GetValue(language.Value.Id, entity.Id, localeKey); - - // We do not export empty values to not fill databases with it. - if (value.HasValue()) - { - dynamic exp = new HybridExpando(); - exp.Culture = languageCulture; - exp.LocaleKey = localeKey; - exp.LocaleValue = value; - - localized.Add(exp); - } - } - } + localized.Add(exp); + } + } - return localized.Any() ? localized : null; - } + foreach (var keySelector in keySelectors) + { + var member = keySelector.Body as MemberExpression; + var propInfo = member.Member as PropertyInfo; + string localeKey = propInfo.Name; + var value = _localizedEntityService.Value.GetLocalizedValue(language.Value.Id, entity.Id, localeKeyGroup, localeKey); + + // we better not export empty values. the risk is to high that they are imported and unnecessary fill databases. + if (value.HasValue()) + { + dynamic exp = new HybridExpando(); + exp.Culture = languageCulture; + exp.LocaleKey = localeKey; + exp.LocaleValue = value; - private dynamic ToDynamic(DataExporterContext ctx, ExportProfile profile) + localized.Add(exp); + } + } + } + + return (localized.Count == 0 ? null : localized); + } + + private dynamic ToDynamic(DataExporterContext ctx, ExportProfile profile) { if (profile == null) return null; @@ -293,26 +258,21 @@ private dynamic ToDynamic(DataExporterContext ctx, ExportProfile profile) private dynamic ToDynamic(DataExporterContext ctx, Currency currency) { - if (currency == null) - { - return null; - } + if (currency == null) + return null; dynamic result = new DynamicEntity(currency); - var translations = ctx.Translations[nameof(Currency)]; - result.Name = translations.GetValue(ctx.LanguageId, currency.Id, nameof(currency.Name)) ?? currency.Name; - result._Localized = GetLocalized(ctx, translations, null, currency, x => x.Name); + result.Name = currency.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); + result._Localized = GetLocalized(ctx, currency, x => x.Name); return result; } private dynamic ToDynamic(DataExporterContext ctx, Language language) { - if (language == null) - { - return null; - } + if (language == null) + return null; dynamic result = new DynamicEntity(language); return result; @@ -320,38 +280,32 @@ private dynamic ToDynamic(DataExporterContext ctx, Language language) private dynamic ToDynamic(DataExporterContext ctx, Country country) { - if (country == null) - { - return null; - } + if (country == null) + return null; dynamic result = new DynamicEntity(country); - var translations = ctx.Translations[nameof(Country)]; - result.Name = translations.GetValue(ctx.LanguageId, country.Id, nameof(country.Name)) ?? country.Name; - result._Localized = GetLocalized(ctx, translations, null, country, x => x.Name); + result.Name = country.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); + result._Localized = GetLocalized(ctx, country, x => x.Name); return result; } private dynamic ToDynamic(DataExporterContext ctx, Address address) { - if (address == null) - { - return null; - } + if (address == null) + return null; dynamic result = new DynamicEntity(address); - result.Country = ToDynamic(ctx, address.Country); + result.Country = ToDynamic(ctx, address.Country); if (address.StateProvinceId.GetValueOrDefault() > 0) { dynamic sp = new DynamicEntity(address.StateProvince); - var translations = ctx.Translations[nameof(StateProvince)]; - sp.Name = translations.GetValue(ctx.LanguageId, address.StateProvince.Id, nameof(StateProvince)) ?? address.StateProvince.Name; - sp._Localized = GetLocalized(ctx, translations, null, address.StateProvince, x => x.Name); + sp.Name = address.StateProvince.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); + sp._Localized = GetLocalized(ctx, address.StateProvince, x => x.Name); result.StateProvince = sp; } @@ -365,21 +319,18 @@ private dynamic ToDynamic(DataExporterContext ctx, Address address) private dynamic ToDynamic(DataExporterContext ctx, RewardPointsHistory points) { - if (points == null) - { - return null; - } + if (points == null) + return null; dynamic result = new DynamicEntity(points); + return result; } private dynamic ToDynamic(DataExporterContext ctx, Customer customer) { - if (customer == null) - { - return null; - } + if (customer == null) + return null; dynamic result = new DynamicEntity(customer); @@ -409,10 +360,8 @@ private dynamic ToDynamic(DataExporterContext ctx, Customer customer) private dynamic ToDynamic(DataExporterContext ctx, Store store) { - if (store == null) - { - return null; - } + if (store == null) + return null; dynamic result = new DynamicEntity(store); @@ -424,16 +373,13 @@ private dynamic ToDynamic(DataExporterContext ctx, Store store) private dynamic ToDynamic(DataExporterContext ctx, DeliveryTime deliveryTime) { - if (deliveryTime == null) - { - return null; - } + if (deliveryTime == null) + return null; dynamic result = new DynamicEntity(deliveryTime); - var translations = ctx.Translations[nameof(DeliveryTime)]; - result.Name = translations.GetValue(ctx.LanguageId, deliveryTime.Id, nameof(deliveryTime.Name)) ?? deliveryTime.Name; - result._Localized = GetLocalized(ctx, translations, null, deliveryTime, x => x.Name); + result.Name = deliveryTime.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); + result._Localized = GetLocalized(ctx, deliveryTime, x => x.Name); return result; } @@ -442,26 +388,24 @@ private void ToDeliveryTime(DataExporterContext ctx, dynamic parent, int? delive { if (ctx.DeliveryTimes != null) { - parent.DeliveryTime = deliveryTimeId.HasValue && ctx.DeliveryTimes.ContainsKey(deliveryTimeId.Value) - ? ToDynamic(ctx, ctx.DeliveryTimes[deliveryTimeId.Value]) - : null; + if (deliveryTimeId.HasValue && ctx.DeliveryTimes.ContainsKey(deliveryTimeId.Value)) + parent.DeliveryTime = ToDynamic(ctx, ctx.DeliveryTimes[deliveryTimeId.Value]); + else + parent.DeliveryTime = null; } } private dynamic ToDynamic(DataExporterContext ctx, QuantityUnit quantityUnit) { - if (quantityUnit == null) - { - return null; - } + if (quantityUnit == null) + return null; dynamic result = new DynamicEntity(quantityUnit); - var translations = ctx.Translations[nameof(QuantityUnit)]; - result.Name = translations.GetValue(ctx.LanguageId, quantityUnit.Id, nameof(quantityUnit.Name)) ?? quantityUnit.Name; - result.Description = translations.GetValue(ctx.LanguageId, quantityUnit.Id, nameof(quantityUnit.Description)) ?? quantityUnit.Description; + result.Name = quantityUnit.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); + result.Description = quantityUnit.GetLocalized(x => x.Description, ctx.Projection.LanguageId ?? 0, true, false); - result._Localized = GetLocalized(ctx, translations, null, quantityUnit, + result._Localized = GetLocalized(ctx, quantityUnit, x => x.Name, x => x.Description); @@ -472,20 +416,20 @@ private void ToQuantityUnit(DataExporterContext ctx, dynamic parent, int? quanti { if (ctx.QuantityUnits != null) { - parent.QuantityUnit = quantityUnitId.HasValue && ctx.QuantityUnits.ContainsKey(quantityUnitId.Value) - ? ToDynamic(ctx, ctx.QuantityUnits[quantityUnitId.Value]) - : null; + if (quantityUnitId.HasValue && ctx.QuantityUnits.ContainsKey(quantityUnitId.Value)) + parent.QuantityUnit = ToDynamic(ctx, ctx.QuantityUnits[quantityUnitId.Value]); + else + parent.QuantityUnit = null; } } private dynamic ToDynamic(DataExporterContext ctx, Picture picture, int thumbPictureSize, int detailsPictureSize) { - if (picture == null) - { - return null; - } + if (picture == null) + return null; // TODO: (mc) Refactor > GetPictureInfo + dynamic result = new DynamicEntity(picture); var pictureInfo = _pictureService.Value.GetPictureInfo(picture); var host = _services.StoreService.GetHost(ctx.Store); @@ -497,6 +441,8 @@ private dynamic ToDynamic(DataExporterContext ctx, Picture picture, int thumbPic result._ThumbImageUrl = _pictureService.Value.GetUrl(pictureInfo, thumbPictureSize, FallbackPictureType.NoFallback, host); result._ImageUrl = _pictureService.Value.GetUrl(pictureInfo, detailsPictureSize, FallbackPictureType.NoFallback, host); result._FullSizeImageUrl = _pictureService.Value.GetUrl(pictureInfo, 0, FallbackPictureType.NoFallback, host); + + //result._ThumbLocalPath = _pictureService.Value.GetThumbLocalPath(picture); } return result; @@ -504,40 +450,34 @@ private dynamic ToDynamic(DataExporterContext ctx, Picture picture, int thumbPic private dynamic ToDynamic(DataExporterContext ctx, ProductVariantAttribute pva) { - if (pva == null) - { - return null; - } + if (pva == null) + return null; - var languageId = ctx.LanguageId; - var attribute = pva.ProductAttribute; + dynamic result = new DynamicEntity(pva); - dynamic result = new DynamicEntity(pva); - dynamic dynAttribute = new DynamicEntity(attribute); - var paTranslations = ctx.TranslationsPerPage[nameof(ProductAttribute)]; - var pvavTranslations = ctx.TranslationsPerPage[nameof(ProductVariantAttributeValue)]; + dynamic attribute = new DynamicEntity(pva.ProductAttribute); - dynAttribute.Name = paTranslations.GetValue(languageId, attribute.Id, nameof(attribute.Name)) ?? attribute.Name; - dynAttribute.Description = paTranslations.GetValue(languageId, attribute.Id, nameof(attribute.Description)) ?? attribute.Description; + attribute.Name = pva.ProductAttribute.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); + attribute.Description = pva.ProductAttribute.GetLocalized(x => x.Description, ctx.Projection.LanguageId ?? 0, true, false); - dynAttribute.Values = pva.ProductVariantAttributeValues + attribute.Values = pva.ProductVariantAttributeValues .OrderBy(x => x.DisplayOrder) .Select(x => { dynamic dyn = new DynamicEntity(x); - dyn.Name = pvavTranslations.GetValue(languageId, x.Id, nameof(x.Name)) ?? x.Name; - dyn._Localized = GetLocalized(ctx, pvavTranslations, null, x, y => y.Name); + dyn.Name = x.GetLocalized(y => y.Name, ctx.Projection.LanguageId ?? 0, true, false); + dyn._Localized = GetLocalized(ctx, x, y => y.Name); return dyn; }) .ToList(); - dynAttribute._Localized = GetLocalized(ctx, paTranslations, null, attribute, + attribute._Localized = GetLocalized(ctx, pva.ProductAttribute, x => x.Name, x => x.Description); - result.Attribute = dynAttribute; + result.Attribute = attribute; return result; } @@ -557,27 +497,23 @@ private dynamic ToDynamic(DataExporterContext ctx, ProductVariantAttributeCombin private dynamic ToDynamic(DataExporterContext ctx, Manufacturer manufacturer) { - if (manufacturer == null) - { - return null; - } + if (manufacturer == null) + return null; dynamic result = new DynamicEntity(manufacturer); - var translations = ctx.Translations[nameof(Manufacturer)]; - var urlRecords = ctx.UrlRecords[nameof(Manufacturer)]; - result.Picture = null; - result.Name = translations.GetValue(ctx.LanguageId, manufacturer.Id, nameof(manufacturer.Name)) ?? manufacturer.Name; + result.Picture = null; + result.Name = manufacturer.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); - if (!ctx.IsPreview) + if (!ctx.IsPreview) { - result.SeName = ctx.UrlRecords[nameof(Manufacturer)].GetSlug(ctx.LanguageId, manufacturer.Id); - result.Description = translations.GetValue(ctx.LanguageId, manufacturer.Id, nameof(manufacturer.Description)) ?? manufacturer.Description; - result.MetaKeywords = translations.GetValue(ctx.LanguageId, manufacturer.Id, nameof(manufacturer.MetaKeywords)) ?? manufacturer.MetaKeywords; - result.MetaDescription = translations.GetValue(ctx.LanguageId, manufacturer.Id, nameof(manufacturer.MetaDescription)) ?? manufacturer.MetaDescription; - result.MetaTitle = translations.GetValue(ctx.LanguageId, manufacturer.Id, nameof(manufacturer.MetaTitle)) ?? manufacturer.MetaTitle; + result.SeName = manufacturer.GetSeName(ctx.Projection.LanguageId ?? 0, true, false); + result.Description = manufacturer.GetLocalized(x => x.Description, ctx.Projection.LanguageId ?? 0, true, false); + result.MetaKeywords = manufacturer.GetLocalized(x => x.MetaKeywords, ctx.Projection.LanguageId ?? 0, true, false); + result.MetaDescription = manufacturer.GetLocalized(x => x.MetaDescription, ctx.Projection.LanguageId ?? 0, true, false); + result.MetaTitle = manufacturer.GetLocalized(x => x.MetaTitle, ctx.Projection.LanguageId ?? 0, true, false); - result._Localized = GetLocalized(ctx, translations, urlRecords, manufacturer, + result._Localized = GetLocalized(ctx, manufacturer, x => x.Name, x => x.Description, x => x.MetaKeywords, @@ -590,33 +526,29 @@ private dynamic ToDynamic(DataExporterContext ctx, Manufacturer manufacturer) private dynamic ToDynamic(DataExporterContext ctx, Category category) { - if (category == null) - { - return null; - } + if (category == null) + return null; dynamic result = new DynamicEntity(category); - var translations = ctx.Translations[nameof(Category)]; - var urlRecords = ctx.UrlRecords[nameof(Category)]; - result.Picture = null; - result.Name = translations.GetValue(ctx.LanguageId, category.Id, nameof(category.Name)) ?? category.Name; - result.FullName = translations.GetValue(ctx.LanguageId, category.Id, nameof(category.FullName)) ?? category.FullName; + result.Picture = null; + result.Name = category.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); + result.FullName = category.GetLocalized(x => x.FullName, ctx.Projection.LanguageId ?? 0, true, false); if (!ctx.IsPreview) { - result.SeName = ctx.UrlRecords[nameof(Category)].GetSlug(ctx.LanguageId, category.Id); - result.Description = translations.GetValue(ctx.LanguageId, category.Id, nameof(category.Description)) ?? category.Description; - result.BottomDescription = translations.GetValue(ctx.LanguageId, category.Id, nameof(category.BottomDescription)) ?? category.BottomDescription; - result.MetaKeywords = translations.GetValue(ctx.LanguageId, category.Id, nameof(category.MetaKeywords)) ?? category.MetaKeywords; - result.MetaDescription = translations.GetValue(ctx.LanguageId, category.Id, nameof(category.MetaDescription)) ?? category.MetaDescription; - result.MetaTitle = translations.GetValue(ctx.LanguageId, category.Id, nameof(category.MetaTitle)) ?? category.MetaTitle; + result.SeName = category.GetSeName(ctx.Projection.LanguageId ?? 0, true, false); + result.Description = category.GetLocalized(x => x.Description, ctx.Projection.LanguageId ?? 0, true, false); + result.BottomDescription = category.GetLocalized(x => x.BottomDescription, ctx.Projection.LanguageId ?? 0, true, false); + result.MetaKeywords = category.GetLocalized(x => x.MetaKeywords, ctx.Projection.LanguageId ?? 0, true, false); + result.MetaDescription = category.GetLocalized(x => x.MetaDescription, ctx.Projection.LanguageId ?? 0, true, false); + result.MetaTitle = category.GetLocalized(x => x.MetaTitle, ctx.Projection.LanguageId ?? 0, true, false); result._CategoryTemplateViewPath = ctx.CategoryTemplates.ContainsKey(category.CategoryTemplateId) ? ctx.CategoryTemplates[category.CategoryTemplateId] : ""; - result._Localized = GetLocalized(ctx, translations, urlRecords, category, + result._Localized = GetLocalized(ctx, category, x => x.Name, x => x.FullName, x => x.Description, @@ -631,17 +563,12 @@ private dynamic ToDynamic(DataExporterContext ctx, Category category) private dynamic ToDynamic(DataExporterContext ctx, Product product, string seName = null) { - if (product == null) - { - return null; - } + if (product == null) + return null; dynamic result = new DynamicEntity(product); - var translations = ctx.TranslationsPerPage[nameof(Product)]; - var urlRecords = ctx.UrlRecordsPerPage[nameof(Product)]; - var localizedName = translations.GetValue(ctx.LanguageId, product.Id, nameof(product.Name)) ?? product.Name; - result.AppliedDiscounts = null; + result.AppliedDiscounts = null; result.Downloads = null; result.TierPrices = null; result.ProductAttributes = null; @@ -652,17 +579,18 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, string seNam result.ProductTags = null; result.ProductSpecificationAttributes = null; result.ProductBundleItems = null; - result.Name = localizedName; + + result.Name = product.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); if (!ctx.IsPreview) { - result.SeName = seName ?? ctx.UrlRecordsPerPage[nameof(Product)].GetSlug(ctx.LanguageId, product.Id); - result.ShortDescription = translations.GetValue(ctx.LanguageId, product.Id, nameof(product.ShortDescription)) ?? product.ShortDescription; - result.FullDescription = translations.GetValue(ctx.LanguageId, product.Id, nameof(product.FullDescription)) ?? product.FullDescription; - result.MetaKeywords = translations.GetValue(ctx.LanguageId, product.Id, nameof(product.MetaKeywords)) ?? product.MetaKeywords; - result.MetaDescription = translations.GetValue(ctx.LanguageId, product.Id, nameof(product.MetaDescription)) ?? product.MetaDescription; - result.MetaTitle = translations.GetValue(ctx.LanguageId, product.Id, nameof(product.MetaTitle)) ?? product.MetaTitle; - result.BundleTitleText = translations.GetValue(ctx.LanguageId, product.Id, nameof(product.BundleTitleText)) ?? product.BundleTitleText; + result.SeName = seName ?? product.GetSeName(ctx.Projection.LanguageId ?? 0, true, false); + result.ShortDescription = product.GetLocalized(x => x.ShortDescription, ctx.Projection.LanguageId ?? 0, true, false); + result.FullDescription = product.GetLocalized(x => x.FullDescription, ctx.Projection.LanguageId ?? 0, true, false, true); + result.MetaKeywords = product.GetLocalized(x => x.MetaKeywords, ctx.Projection.LanguageId ?? 0, true, false); + result.MetaDescription = product.GetLocalized(x => x.MetaDescription, ctx.Projection.LanguageId ?? 0, true, false); + result.MetaTitle = product.GetLocalized(x => x.MetaTitle, ctx.Projection.LanguageId ?? 0, true, false); + result.BundleTitleText = product.GetLocalized(x => x.BundleTitleText, ctx.Projection.LanguageId ?? 0, true, false); result._ProductTemplateViewPath = ctx.ProductTemplates.ContainsKey(product.ProductTemplateId) ? ctx.ProductTemplates[product.ProductTemplateId] @@ -674,7 +602,7 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, string seNam ToDeliveryTime(ctx, result, product.DeliveryTimeId); ToQuantityUnit(ctx, result, product.QuantityUnitId); - result._Localized = GetLocalized(ctx, translations, urlRecords, product, + result._Localized = GetLocalized(ctx, product, x => x.Name, x => x.ShortDescription, x => x.FullDescription, @@ -691,6 +619,7 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParen { product.MergeWithCombination(productContext.Combination); + var languageId = ctx.Projection.LanguageId ?? 0; var numberOfPictures = ctx.Projection.NumberOfPictures ?? int.MaxValue; var productDetailsPictureSize = ctx.Projection.PictureSize > 0 ? ctx.Projection.PictureSize : _mediaSettings.Value.ProductDetailsPictureSize; @@ -735,9 +664,8 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParen if (ctx.Projection.AttributeCombinationValueMerging == ExportAttributeValueMerging.AppendAllValuesToName) { - var translations = ctx.TranslationsPerPage[nameof(ProductVariantAttributeValue)]; var valueNames = variantAttributeValues - .Select(x => translations.GetValue(ctx.LanguageId, x.Id, nameof(x.Name)) ?? x.Name) + .Select(x => x.GetLocalized(y => y.Name, languageId, true, false)) .ToList(); dynObject.Name = ((string)dynObject.Name).Grow(string.Join(", ", valueNames), " "); @@ -879,6 +807,7 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParen .Select(x => { dynamic dyn = new DynamicEntity(x); + return dyn; }) .ToList(); @@ -906,12 +835,10 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParen .Select(x => { dynamic dyn = new DynamicEntity(x); - var translations = ctx.TranslationsPerPage[nameof(ProductTag)]; - var localizedName = translations.GetValue(ctx.LanguageId, x.Id, nameof(x.Name)) ?? x.Name; - dyn.Name = localizedName; - dyn.SeName = SeoExtensions.GetSeName(localizedName, _seoSettings.Value); - dyn._Localized = GetLocalized(ctx, translations, null, x, y => y.Name); + dyn.Name = x.GetLocalized(y => y.Name, languageId, true, false); + dyn.SeName = x.GetSeName(languageId); + dyn._Localized = GetLocalized(ctx, x, y => y.Name); return dyn; }) @@ -929,11 +856,10 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParen .Select(x => { dynamic dyn = new DynamicEntity(x); - var translations = ctx.TranslationsPerPage[nameof(ProductBundleItem)]; - dyn.Name = translations.GetValue(ctx.LanguageId, x.Id, nameof(x.Name)) ?? x.Name; - dyn.ShortDescription = translations.GetValue(ctx.LanguageId, x.Id, nameof(x.ShortDescription)) ?? x.ShortDescription; - dyn._Localized = GetLocalized(ctx, translations, null, x, y => y.Name, y => y.ShortDescription); + dyn.Name = x.GetLocalized(y => y.Name, languageId, true, false); + dyn.ShortDescription = x.GetLocalized(y => y.ShortDescription, languageId, true, false); + dyn._Localized = GetLocalized(ctx, x, y => y.Name, y => y.ShortDescription); return dyn; }) @@ -954,16 +880,11 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParen string brand = null; var productManus = ctx.ProductExportContext.ProductManufacturers.GetOrLoad(product.Id); - if (productManus != null && productManus.Any()) - { - var translations = ctx.Translations[nameof(Manufacturer)]; - var manufacturer = productManus.First().Manufacturer; - brand = translations.GetValue(ctx.LanguageId, manufacturer.Id, nameof(manufacturer.Name)) ?? manufacturer.Name; - } - if (brand.IsEmpty()) - { - brand = ctx.Projection.Brand; - } + if (productManus != null && productManus.Any()) + brand = productManus.First().Manufacturer.GetLocalized(x => x.Name, languageId, true, false); + + if (brand.IsEmpty()) + brand = ctx.Projection.Brand; dynObject._Brand = brand; } @@ -996,7 +917,7 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParen if (ctx.Supports(ExportFeatures.OffersShippingTimeFallback)) { dynamic deliveryTime = dynObject.DeliveryTime; - dynObject._ShippingTime = deliveryTime == null ? ctx.Projection.ShippingTime : deliveryTime.Name; + dynObject._ShippingTime = (deliveryTime == null ? ctx.Projection.ShippingTime : deliveryTime.Name); } if (ctx.Supports(ExportFeatures.OffersShippingCostsFallback)) @@ -1070,17 +991,15 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParen private dynamic ToDynamic(DataExporterContext ctx, Order order) { - if (order == null) - { - return null; - } + if (order == null) + return null; dynamic result = new DynamicEntity(order); result.OrderNumber = order.GetOrderNumber(); - result.OrderStatus = order.OrderStatus.GetLocalizedEnum(_services.Localization, ctx.LanguageId); - result.PaymentStatus = order.PaymentStatus.GetLocalizedEnum(_services.Localization, ctx.LanguageId); - result.ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_services.Localization, ctx.LanguageId); + result.OrderStatus = order.OrderStatus.GetLocalizedEnum(_services.Localization, ctx.Projection.LanguageId ?? 0); + result.PaymentStatus = order.PaymentStatus.GetLocalizedEnum(_services.Localization, ctx.Projection.LanguageId ?? 0); + result.ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_services.Localization, ctx.Projection.LanguageId ?? 0); result.Customer = null; result.BillingAddress = null; @@ -1101,14 +1020,13 @@ private dynamic ToDynamic(DataExporterContext ctx, Order order) private dynamic ToDynamic(DataExporterContext ctx, OrderItem orderItem) { - if (orderItem == null) - { - return null; - } + if (orderItem == null) + return null; dynamic result = new DynamicEntity(orderItem); orderItem.Product.MergeWithCombination(orderItem.AttributesXml, _productAttributeParser.Value); + result.Product = ToDynamic(ctx, orderItem.Product); return result; @@ -1116,10 +1034,8 @@ private dynamic ToDynamic(DataExporterContext ctx, OrderItem orderItem) private dynamic ToDynamic(DataExporterContext ctx, Shipment shipment) { - if (shipment == null) - { - return null; - } + if (shipment == null) + return null; dynamic result = new DynamicEntity(shipment); @@ -1127,6 +1043,7 @@ private dynamic ToDynamic(DataExporterContext ctx, Shipment shipment) .Select(x => { dynamic exp = new DynamicEntity(x); + return exp; }) .ToList(); @@ -1136,21 +1053,18 @@ private dynamic ToDynamic(DataExporterContext ctx, Shipment shipment) private dynamic ToDynamic(DataExporterContext ctx, Discount discount) { - if (discount == null) - { - return null; - } + if (discount == null) + return null; dynamic result = new DynamicEntity(discount); + return result; } private dynamic ToDynamic(DataExporterContext ctx, Download download) { if (download == null) - { return null; - } dynamic result = new DynamicEntity(download); return result; @@ -1158,34 +1072,31 @@ private dynamic ToDynamic(DataExporterContext ctx, Download download) private dynamic ToDynamic(DataExporterContext ctx, ProductSpecificationAttribute psa) { - if (psa == null) - { - return null; - } + if (psa == null) + return null; - var option = psa.SpecificationAttributeOption; - var attribute = option.SpecificationAttribute; + var option = psa.SpecificationAttributeOption; - dynamic result = new DynamicEntity(psa); - dynamic dynAttribute = new DynamicEntity(attribute); - var saTranslations = ctx.TranslationsPerPage[nameof(SpecificationAttribute)]; - var saoTranslations = ctx.TranslationsPerPage[nameof(SpecificationAttributeOption)]; + dynamic result = new DynamicEntity(psa); - dynAttribute.Name = saTranslations.GetValue(ctx.LanguageId, attribute.Id, nameof(attribute.Name)) ?? attribute.Name; - dynAttribute._Localized = GetLocalized(ctx, saTranslations, null, attribute, x => x.Name); + dynamic dynAttribute = new DynamicEntity(option.SpecificationAttribute); - dynAttribute.Alias = saTranslations.GetValue(ctx.LanguageId, attribute.Id, nameof(attribute.Alias)) ?? attribute.Alias; - dynAttribute._Localized = GetLocalized(ctx, saTranslations, null, attribute, x => x.Alias); + dynAttribute.Name = option.SpecificationAttribute.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); + dynAttribute._Localized = GetLocalized(ctx, option.SpecificationAttribute, x => x.Name); - dynamic dynOption = new DynamicEntity(option); + dynAttribute.Alias = option.SpecificationAttribute.GetLocalized(x => x.Alias, ctx.Projection.LanguageId ?? 0, true, false); + dynAttribute._Localized = GetLocalized(ctx, option.SpecificationAttribute, x => x.Alias); - dynOption.Name = saoTranslations.GetValue(ctx.LanguageId, option.Id, nameof(option.Name)) ?? option.Name; - dynOption._Localized = GetLocalized(ctx, saoTranslations, null, option, x => x.Name); + dynamic dynOption = new DynamicEntity(option); - dynOption.Alias = saoTranslations.GetValue(ctx.LanguageId, option.Id, nameof(option.Alias)) ?? option.Alias; - dynOption._Localized = GetLocalized(ctx, saoTranslations, null, option, x => x.Alias); + dynOption.Name = option.GetLocalized(x => x.Name, ctx.Projection.LanguageId ?? 0, true, false); + dynOption._Localized = GetLocalized(ctx, option, x => x.Name); + + dynOption.Alias = option.GetLocalized(x => x.Alias, ctx.Projection.LanguageId ?? 0, true, false); + dynOption._Localized = GetLocalized(ctx, option, x => x.Alias); dynOption.SpecificationAttribute = dynAttribute; + result.SpecificationAttributeOption = dynOption; return result; @@ -1193,21 +1104,18 @@ private dynamic ToDynamic(DataExporterContext ctx, ProductSpecificationAttribute private dynamic ToDynamic(DataExporterContext ctx, GenericAttribute genericAttribute) { - if (genericAttribute == null) - { - return null; - } + if (genericAttribute == null) + return null; dynamic result = new DynamicEntity(genericAttribute); + return result; } private dynamic ToDynamic(DataExporterContext ctx, NewsLetterSubscription subscription) { - if (subscription == null) - { - return null; - } + if (subscription == null) + return null; dynamic result = new DynamicEntity(subscription); @@ -1220,10 +1128,8 @@ private dynamic ToDynamic(DataExporterContext ctx, NewsLetterSubscription subscr private dynamic ToDynamic(DataExporterContext ctx, ShoppingCartItem shoppingCartItem) { - if (shoppingCartItem == null) - { - return null; - } + if (shoppingCartItem == null) + return null; dynamic result = new DynamicEntity(shoppingCartItem); @@ -1244,8 +1150,7 @@ private List Convert(DataExporterContext ctx, Product product) { var result = new List(); var productContext = new DynamicProductContext(); - - productContext.SeName = ctx.UrlRecordsPerPage[nameof(Product)].GetSlug(ctx.LanguageId, product.Id); + productContext.SeName = product.GetSeName(ctx.Projection.LanguageId ?? 0, true, false); productContext.Combinations = ctx.ProductExportContext.AttributeCombinations.GetOrLoad(product.Id); productContext.AbsoluteProductUrl = _productUrlHelper.Value.GetAbsoluteProductUrl( diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportDataSegmenter.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportDataSegmenter.cs index cb44a39618..0673598fdc 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportDataSegmenter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportDataSegmenter.cs @@ -38,21 +38,23 @@ internal interface IExportDataSegmenterProvider : IExportDataSegmenterConsumer, public class ExportDataSegmenter : IExportDataSegmenterProvider where T : BaseEntity { - private readonly Func> _load; - private readonly Action> _loaded; - private readonly Func> _convert; + private Func> _load; + private Action> _loaded; + private Func> _convert; - private readonly int _offset; - private readonly int _take; - private readonly int _limit; - private readonly int _recordsPerSegment; - private readonly int _totalRecords; + private int _offset; + private int _take; + private int _limit; + private int _recordsPerSegment; + private int _totalRecords; - private Queue _data; - private bool _endOfData; + private int _skip; + private int _countRecords; + + private Queue _data; public ExportDataSegmenter( - Func> load, + Func> load, Action> loaded, Func> convert, int offset, @@ -69,64 +71,61 @@ public ExportDataSegmenter( _limit = limit; _recordsPerSegment = recordsPerSegment; _totalRecords = totalRecords; + + _skip = _offset; } /// - /// Total number of records. + /// Total number of records /// public int TotalRecords { get { - var total = Math.Max(_totalRecords - _offset, 0); + var total = Math.Max(_totalRecords - _offset, 0); - if (_limit > 0 && _limit < total) - { - return _limit; - } + if (_limit > 0 && _limit < total) + return _limit; return total; } } - /// - /// Number of processed records. - /// - public int RecordCount { get; private set; } + /// + /// Number of processed records + /// + public int RecordCount + { + get { return _countRecords; } + } - /// - /// Record per segment counter. - /// - public int RecordPerSegmentCount { get; set; } + /// + /// Record per segment counter + /// + public int RecordPerSegmentCount { get; set; } /// - /// Whether there is data available. + /// Whether there is data available /// public bool HasData { get { - if (_limit > 0 && RecordCount >= _limit) - { - return false; - } + if (_limit > 0 && _countRecords >= _limit) + return false; - if (_data != null && _data.Count > 0) - { - return true; - } + if (_data != null && _data.Count > 0) + return true; - if (_endOfData) - { - return false; - } + if (_skip >= _totalRecords) + return false; return true; } } /// - /// Gets current data segment. + /// Gets current data segment /// public IReadOnlyCollection CurrentSegment { @@ -139,83 +138,68 @@ public IReadOnlyCollection CurrentSegment { _convert(entity).Each(x => records.Add(x)); - if (++RecordCount >= _limit && _limit > 0) - { - return records; - } + if (++_countRecords >= _limit && _limit > 0) + return records; - if (++RecordPerSegmentCount >= _recordsPerSegment && _recordsPerSegment > 0) - { - return records; - } + if (++RecordPerSegmentCount >= _recordsPerSegment && _recordsPerSegment > 0) + return records; } return records; } } - /// - /// Read next segment. - /// - /// true next segment available. false no more data. - public bool ReadNextSegment() + /// + /// Reads the next segment + /// + /// + public bool ReadNextSegment() { - if (_limit > 0 && RecordCount >= _limit) - { - return false; - } - - if (_recordsPerSegment > 0 && RecordPerSegmentCount >= _recordsPerSegment) - { - return false; - } - - // Do not make the queue longer than necessary. - if (_recordsPerSegment > 0 && _data != null && _data.Count >= _recordsPerSegment) - { - return true; - } - - var newData = _load(); - - if (_data != null && _data.Count > 0) - { - var data = new List(_data); - if (newData != null) - { - data.AddRange(newData); - } - - _data = new Queue(data); - } - else - { - if (newData == null) - { - // End of data reached. - _endOfData = true; - return false; - } - - _data = new Queue(newData); - } - - // Give provider the opportunity to make something with entity ids. + if (_limit > 0 && _countRecords >= _limit) + return false; + + if (_recordsPerSegment > 0 && RecordPerSegmentCount >= _recordsPerSegment) + return false; + + // do not make the queue longer than necessary + if (_recordsPerSegment > 0 && _data != null && _data.Count >= _recordsPerSegment) + return true; + + if (_skip >= _totalRecords) + return false; + + if (_data != null) + _skip += _take; + + if (_data != null && _data.Count > 0) + { + var data = new List(_data); + data.AddRange(_load(_skip)); + + _data = new Queue(data); + } + else + { + _data = new Queue(_load(_skip)); + } + + // give provider the opportunity to make something with entity ids _loaded?.Invoke(_data.AsReadOnly()); - return _data.Count > 0; + return (_data.Count > 0); } /// - /// Dispose and reset segmenter instance. + /// Dispose and reset segmenter instance /// public void Dispose() { - RecordCount = 0; + if (_data != null) + _data.Clear(); + + _skip = _offset; + _countRecords = 0; RecordPerSegmentCount = 0; - - _endOfData = false; - _data?.Clear(); - } + } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportExtensions.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportExtensions.cs index 83a3fb4f96..c05c807ac7 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportExtensions.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportExtensions.cs @@ -250,17 +250,17 @@ public static string GetIconClass(this ExportDeploymentType type) switch (type) { case ExportDeploymentType.FileSystem: - return "far fa-folder-open"; + return "fa-folder-open-o"; case ExportDeploymentType.Email: - return "far fa-envelope"; + return "fa-envelope-o"; case ExportDeploymentType.Http: - return "fa fa-globe"; + return "fa-globe"; case ExportDeploymentType.Ftp: - return "far fa-copy"; + return "fa-files-o"; case ExportDeploymentType.PublicFolder: - return "fa fa-unlock"; + return "fa-unlock"; default: - return "fa fa-question"; + return "fa-question"; } } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportXmlHelper.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportXmlHelper.cs index df53b4cb3f..f224098270 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportXmlHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportXmlHelper.cs @@ -941,15 +941,7 @@ public void WriteCustomer(dynamic customer, string node) _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); _writer.Write("LastLoginDateUtc", entity.LastLoginDateUtc.HasValue ? entity.LastLoginDateUtc.Value.ToString(_culture) : ""); _writer.Write("LastActivityDateUtc", entity.LastActivityDateUtc.ToString(_culture)); - //_writer.Write("Salutation", entity.Salutation); // TODO: marked as "for future use" - _writer.Write("Title", entity.Title); - _writer.Write("FirstName", entity.FirstName); - _writer.Write("LastName", entity.LastName); - _writer.Write("FullName", entity.FullName); - _writer.Write("Company", entity.Company); - _writer.Write("CustomerNumber", entity.CustomerNumber); - _writer.Write("BirthDate", entity.BirthDate.HasValue ? entity.BirthDate.Value.ToString(_culture) : ""); - _writer.Write("RewardPointsBalance", ((int)customer._RewardPointsBalance).ToString()); + _writer.Write("RewardPointsBalance", ((int)customer._RewardPointsBalance).ToString()); if (customer.CustomerRoles != null) { diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/IDataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/IDataExporter.cs index b014d487f5..3bea8233e3 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/IDataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/IDataExporter.cs @@ -14,7 +14,9 @@ public interface IDataExporter { DataExportResult Export(DataExportRequest request, CancellationToken cancellationToken); - DataExportPreviewResult Preview(DataExportRequest request, int pageIndex); + IList Preview(DataExportRequest request, int pageIndex, int? totalRecords = null); + + int GetDataCount(DataExportRequest request); /// /// Creates a product export context for fast retrieval (eager loading) of product navigation properties diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/DataExporterContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/DataExporterContext.cs index 317d4400e3..c0e56b6175 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/DataExporterContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/DataExporterContext.cs @@ -2,17 +2,17 @@ using System.Linq; using System.Threading; using SmartStore.Core; +using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Directory; using SmartStore.Core.Domain.Localization; -using SmartStore.Core.Domain.Seo; using SmartStore.Core.Domain.Stores; using SmartStore.Core.Logging; namespace SmartStore.Services.DataExchange.Export.Internal { - internal class DataExporterContext + internal class DataExporterContext { public DataExporterContext( DataExportRequest request, @@ -40,18 +40,14 @@ public DataExporterContext( ProductTemplates = new Dictionary(); CategoryTemplates = new Dictionary(); NewsletterSubscriptions = new HashSet(); - Translations = new Dictionary(); - TranslationsPerPage = new Dictionary(); - UrlRecords = new Dictionary(); - UrlRecordsPerPage = new Dictionary(); - StatsPerStore = new Dictionary(); + RecordsPerStore = new Dictionary(); EntityIdsLoaded = new List(); - EntityIdsPerSegment = new HashSet(); + EntityIdsPerSegment = new List(); Result = new DataExportResult { - FileFolder = IsFileBasedExport ? FolderContent : null + FileFolder = (IsFileBasedExport ? FolderContent : null) }; ExecuteContext = new ExportExecuteContext(Result, CancellationToken, FolderContent); @@ -66,7 +62,7 @@ public DataExporterContext( } /// - /// All entity identifiers per export. + /// All entity identifiers per export /// public List EntityIdsLoaded { get; set; } public void SetLoadedEntityIds(IEnumerable ids) @@ -78,22 +74,21 @@ public void SetLoadedEntityIds(IEnumerable ids) } /// - /// All entity identifiers per segment (to avoid exporting products multiple times). + /// All entity identifiers per segment (to avoid exporting products multiple times) /// - public HashSet EntityIdsPerSegment { get; set; } - public int LastId { get; set; } + public List EntityIdsPerSegment { get; set; } - public string ProgressInfo { get; set; } - public int RecordCount { get; set; } - public Dictionary StatsPerStore { get; set; } + public int RecordCount { get; set; } + public Dictionary RecordsPerStore { get; set; } + public string ProgressInfo { get; set; } - public DataExportRequest Request { get; private set; } + public DataExportRequest Request { get; private set; } public CancellationToken CancellationToken { get; private set; } public bool IsPreview { get; private set; } public bool Supports(ExportFeatures feature) { - return !IsPreview && Request.Provider.Metadata.ExportFeatures.HasFlag(feature); + return (!IsPreview && Request.Provider.Metadata.ExportFeatures.HasFlag(feature)); } public ExportFilter Filter { get; private set; } @@ -101,9 +96,8 @@ public bool Supports(ExportFeatures feature) public Currency ContextCurrency { get; set; } public Customer ContextCustomer { get; set; } public Language ContextLanguage { get; set; } - public int LanguageId => Projection.LanguageId ?? 0; - public TraceLogger Log { get; set; } + public TraceLogger Log { get; set; } public Store Store { get; set; } public string FolderContent { get; private set; } @@ -113,7 +107,7 @@ public bool IsFileBasedExport get { return Request.Provider == null || Request.Provider.Value == null || Request.Provider.Value.FileExtension.HasValue(); } } - // Data loaded once per export. + // data loaded once per export public Dictionary DeliveryTimes { get; set; } public Dictionary QuantityUnits { get; set; } public Dictionary Stores { get; set; } @@ -123,34 +117,14 @@ public bool IsFileBasedExport public Dictionary CategoryTemplates { get; set; } public HashSet NewsletterSubscriptions { get; set; } - /// - /// All translations for global scopes (like Category, Manufacturer etc.) - /// - public Dictionary Translations { get; set; } - public Dictionary UrlRecords { get; set; } - - // Data loaded once per page. - public ProductExportContext ProductExportContext { get; set; } - public ProductExportContext AssociatedProductContext { get; set; } - public OrderExportContext OrderExportContext { get; set; } + // data loaded once per page + public ProductExportContext ProductExportContext { get; set; } + public OrderExportContext OrderExportContext { get; set; } public ManufacturerExportContext ManufacturerExportContext { get; set; } public CategoryExportContext CategoryExportContext { get; set; } public CustomerExportContext CustomerExportContext { get; set; } - /// - /// All per page translations (like ProductVariantAttributeValue etc.) - /// - public Dictionary TranslationsPerPage { get; set; } - public Dictionary UrlRecordsPerPage { get; set; } - - public ExportExecuteContext ExecuteContext { get; set; } + public ExportExecuteContext ExecuteContext { get; set; } public DataExportResult Result { get; set; } } - - internal class RecordStats - { - public int TotalRecords { get; set; } - public int MaxId { get; set; } - public int OffsetId { get; set; } - } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ISyncMappingService.cs b/src/Libraries/SmartStore.Services/DataExchange/ISyncMappingService.cs index 9bdbc6d790..9d08368ae3 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ISyncMappingService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ISyncMappingService.cs @@ -103,7 +103,7 @@ public static SyncMapping InsertSyncMapping(this ISyncMappingService svc, T e { ContextName = contextName, EntityId = entity.Id, - EntityName = entity.GetEntityName(), + EntityName = typeof(T).Name, SourceKey = sourceKey }; @@ -125,7 +125,7 @@ public static SyncMapping GetSyncMappingByEntity(this ISyncMappingService svc throw Error.InvalidOperation("Cannot get a sync mapping record for a transient (unsaved) entity"); } - return svc.GetSyncMappingByEntity(entity.Id, entity.GetEntityName(), contextName); + return svc.GetSyncMappingByEntity(entity.Id, typeof(T).Name, contextName); } /// diff --git a/src/Libraries/SmartStore.Services/DataExchange/SyncMappingService.cs b/src/Libraries/SmartStore.Services/DataExchange/SyncMappingService.cs index 8b455a6955..395a8f208a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/SyncMappingService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/SyncMappingService.cs @@ -113,7 +113,7 @@ public void DeleteSyncMappingsFor(T entity) where T : BaseEntity return; } - _syncMappingsRepository.DeleteAll(x => x.EntityId == entity.Id && x.EntityName == entity.GetEntityName()); + _syncMappingsRepository.DeleteAll(x => x.EntityId == entity.Id && x.EntityName == typeof(T).Name); } public void DeleteSyncMappings(string contextName, string entityName = null) diff --git a/src/Libraries/SmartStore.Services/Discounts/DiscountService.cs b/src/Libraries/SmartStore.Services/Discounts/DiscountService.cs index 666c53dfdb..10a48ae762 100644 --- a/src/Libraries/SmartStore.Services/Discounts/DiscountService.cs +++ b/src/Libraries/SmartStore.Services/Discounts/DiscountService.cs @@ -210,6 +210,8 @@ public virtual Discount GetDiscountByCouponCode(string couponCode, bool showHidd private System.Diagnostics.Stopwatch _watch = new System.Diagnostics.Stopwatch(); public virtual bool IsDiscountValid(Discount discount, Customer customer) { + _watch.Start(); + var couponCodeToValidate = ""; if (customer != null) { @@ -217,6 +219,10 @@ public virtual bool IsDiscountValid(Discount discount, Customer customer) } var valid = IsDiscountValid(discount, customer, couponCodeToValidate); + + _watch.Stop(); + System.Diagnostics.Debug.WriteLine("D Elapsed: {0} ms.".FormatCurrent(_watch.ElapsedMilliseconds)); + return valid; } diff --git a/src/Libraries/SmartStore.Services/Forums/ForumService.cs b/src/Libraries/SmartStore.Services/Forums/ForumService.cs index 11b744b8ab..4c0b56e800 100644 --- a/src/Libraries/SmartStore.Services/Forums/ForumService.cs +++ b/src/Libraries/SmartStore.Services/Forums/ForumService.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using System.Data.Entity; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Customers; @@ -405,7 +404,7 @@ orderby ftGroup.Key query = query.OrderByDescending(x => x.LastPostTime); - var forumTopics = query.Take(() => count).ToList(); + var forumTopics = query.Take(count).ToList(); return forumTopics; } diff --git a/src/Libraries/SmartStore.Services/Localization/ILocalizedEntityService.cs b/src/Libraries/SmartStore.Services/Localization/ILocalizedEntityService.cs index 979ef91ca5..a65704ea59 100644 --- a/src/Libraries/SmartStore.Services/Localization/ILocalizedEntityService.cs +++ b/src/Libraries/SmartStore.Services/Localization/ILocalizedEntityService.cs @@ -42,44 +42,11 @@ public partial interface ILocalizedEntityService : IScopedService /// Localized properties IList GetLocalizedProperties(int entityId, string localeKeyGroup); - /// - /// Prefetches a collection of localized properties for a range of entities in one go - /// and caches them for the duration of the current request. - /// - /// Locale key group (scope) - /// - /// The entity ids to prefetch translations for. Can be null, - /// in which case all translations for the requested scope are loaded. - /// - /// Whether represents a range of ids (perf). - /// Whether is already sorted (perf). - /// Localized property collection - /// - /// Be careful not to load large amounts of data at once (e.g. for "Product" scope with large range). - /// - void PrefetchLocalizedProperties(string localeKeyGroup, int languageId, int[] entityIds, bool isRange = false, bool isSorted = false); - - /// - /// Gets a collection of localized properties for a range of entities in one go. - /// - /// Locale key group (scope) - /// - /// The entity ids to load translations for. Can be null, - /// in which case all translations for the requested scope are loaded. - /// - /// Whether represents a range of ids (perf). - /// Whether is already sorted (perf). - /// Localized property collection - /// - /// Be careful not to load large amounts of data at once (e.g. for "Product" scope with large range). - /// - LocalizedPropertyCollection GetLocalizedPropertyCollection(string localeKeyGroup, int[] entityIds, bool isRange = false, bool isSorted = false); - - /// - /// Inserts a localized property - /// - /// Localized property - void InsertLocalizedProperty(LocalizedProperty localizedProperty); + /// + /// Inserts a localized property + /// + /// Localized property + void InsertLocalizedProperty(LocalizedProperty localizedProperty); /// /// Updates the localized property diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizationExtensions.cs b/src/Libraries/SmartStore.Services/Localization/LocalizationExtensions.cs index eef621f93f..09b6a8b701 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizationExtensions.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizationExtensions.cs @@ -29,7 +29,7 @@ public static LocalizedValue GetLocalized(this T entity, Expression keySelector.Compile().Invoke(x), EngineContext.Current.Resolve().WorkingLanguage, @@ -57,7 +57,7 @@ public static LocalizedValue GetLocalized(this T entity, { return GetLocalizedEx( entity, - entity.GetEntityName(), + typeof(T).Name, LocaleKeyFromExpression(keySelector.Body), x => keySelector.Compile().Invoke(x), EngineContext.Current.Resolve().GetLanguageById(languageId), @@ -88,7 +88,7 @@ public static LocalizedValue GetLocalized(this T entity, { return GetLocalizedEx( entity, - entity.GetEntityName(), + typeof(T).Name, localeKey, x => fallback, language, @@ -118,7 +118,7 @@ public static LocalizedValue GetLocalized(this T entity, { return GetLocalizedEx( entity, - entity.GetEntityName(), + typeof(T).Name, LocaleKeyFromExpression(keySelector.Body), x => keySelector.Compile().Invoke(x), language, @@ -148,8 +148,8 @@ public static LocalizedValue GetLocalized(this T entity, where T : BaseEntity, ILocalizedEntity { return GetLocalizedEx( - entity, - entity.GetEntityName(), + entity, + typeof(T).Name, LocaleKeyFromExpression(keySelector.Body), x => keySelector.Compile().Invoke(x), EngineContext.Current.Resolve().GetLanguageById(languageId), @@ -180,7 +180,7 @@ public static LocalizedValue GetLocalized(this T entity, { return GetLocalizedEx( entity, - entity.GetEntityName(), + typeof(T).Name, LocaleKeyFromExpression(keySelector.Body), x => keySelector.Compile().Invoke(x), language, diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizedEntityService.cs b/src/Libraries/SmartStore.Services/Localization/LocalizedEntityService.cs index 8d3c9bd41c..8e618e792c 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizedEntityService.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizedEntityService.cs @@ -3,11 +3,9 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; -using System.Threading; using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Data; -using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Localization; namespace SmartStore.Services.Localization @@ -26,46 +24,13 @@ public partial class LocalizedEntityService : ScopedServiceBase, ILocalizedEntit private readonly IRepository _localizedPropertyRepository; private readonly ICacheManager _cacheManager; - private readonly PerformanceSettings _performanceSettings; - - private readonly IDictionary _prefetchedCollections; - private static int _lastCacheSegmentSize = -1; public LocalizedEntityService( ICacheManager cacheManager, - IRepository localizedPropertyRepository, - PerformanceSettings performanceSettings) + IRepository localizedPropertyRepository) { _cacheManager = cacheManager; _localizedPropertyRepository = localizedPropertyRepository; - _performanceSettings = performanceSettings; - - _prefetchedCollections = new Dictionary(StringComparer.OrdinalIgnoreCase); - - ValidateCacheState(); - } - - private void ValidateCacheState() - { - // Ensure that after a segment size change the cache segments are invalidated. - var size = _performanceSettings.CacheSegmentSize; - var changed = _lastCacheSegmentSize == -1; - - if (size <= 0) - { - _performanceSettings.CacheSegmentSize = size = 1; - } - - if (_lastCacheSegmentSize > 0 && _lastCacheSegmentSize != size) - { - OnClearCache(); - changed = true; - } - - if (changed) - { - Interlocked.Exchange(ref _lastCacheSegmentSize, size); - } } protected override void OnClearCache() @@ -127,15 +92,6 @@ protected virtual void ClearCacheSegment(string localeKeyGroup, string localeKey public virtual string GetLocalizedValue(int languageId, int entityId, string localeKeyGroup, string localeKey) { - if (_prefetchedCollections.TryGetValue(localeKeyGroup, out var collection)) - { - var cachedItem = collection.Find(languageId, entityId, localeKey); - if (cachedItem != null) - { - return cachedItem.LocaleValue; - } - } - if (IsInScope) { return GetLocalizedValueUncached(languageId, entityId, localeKeyGroup, localeKey); @@ -175,85 +131,16 @@ public virtual IList GetLocalizedProperties(int entityId, str if (localeKeyGroup.IsEmpty()) return new List(); - var query = from x in _localizedPropertyRepository.Table - orderby x.Id - where x.EntityId == entityId && x.LocaleKeyGroup == localeKeyGroup - select x; + var query = from lp in _localizedPropertyRepository.Table + orderby lp.Id + where lp.EntityId == entityId && + lp.LocaleKeyGroup == localeKeyGroup + select lp; var props = query.ToList(); return props; } - public virtual void PrefetchLocalizedProperties(string localeKeyGroup, int languageId, int[] entityIds, bool isRange = false, bool isSorted = false) - { - if (languageId == 0) - return; - - var collection = GetLocalizedPropertyCollectionInternal(localeKeyGroup, languageId, entityIds, isRange, isSorted); - - if (_prefetchedCollections.TryGetValue(localeKeyGroup, out var existing)) - { - collection.MergeWith(existing); - } - else - { - _prefetchedCollections[localeKeyGroup] = collection; - } - } - - public virtual LocalizedPropertyCollection GetLocalizedPropertyCollection(string localeKeyGroup, int[] entityIds, bool isRange = false, bool isSorted = false) - { - return GetLocalizedPropertyCollectionInternal(localeKeyGroup, 0, entityIds, isRange, isSorted); - } - - public virtual LocalizedPropertyCollection GetLocalizedPropertyCollectionInternal(string localeKeyGroup, int languageId, int[] entityIds, bool isRange = false, bool isSorted = false) - { - Guard.NotEmpty(localeKeyGroup, nameof(localeKeyGroup)); - - using (new DbContextScope(proxyCreation: false, lazyLoading: false)) - { - var query = from x in _localizedPropertyRepository.TableUntracked - where x.LocaleKeyGroup == localeKeyGroup - select x; - - var requestedSet = entityIds; - - if (entityIds != null && entityIds.Length > 0) - { - if (isRange) - { - if (!isSorted) - { - Array.Sort(entityIds); - } - - var min = entityIds[0]; - var max = entityIds[entityIds.Length - 1]; - - if (entityIds.Length == 2 && max > min + 1) - { - // Only min & max were passed, create the range sequence. - requestedSet = Enumerable.Range(min, max - min + 1).ToArray(); - } - - query = query.Where(x => x.EntityId >= min && x.EntityId <= max); - } - else - { - requestedSet = entityIds; - query = query.Where(x => entityIds.Contains(x.EntityId)); - } - } - - if (languageId > 0) - { - query = query.Where(x => x.LanguageId == languageId); - } - - return new LocalizedPropertyCollection(localeKeyGroup, requestedSet, query.ToList()); - } - } - protected virtual LocalizedProperty GetLocalizedProperty(int languageId, int entityId, string localeKeyGroup, string localeKey) { var query = from lp in _localizedPropertyRepository.Table @@ -342,7 +229,7 @@ public virtual void SaveLocalizedValue( throw new ArgumentException($"Expression '{keySelector}' refers to a field, not a property."); } - var keyGroup = entity.GetEntityName(); + var keyGroup = typeof(T).Name; var key = propInfo.Name; var valueStr = localeValue.Convert(); var prop = GetLocalizedProperty(languageId, entity.Id, keyGroup, key); @@ -394,7 +281,7 @@ private string GetSegmentKeyPart(string localeKeyGroup, string localeKey, int en private string GetSegmentKeyPart(string localeKeyGroup, string localeKey, int entityId, out int minId, out int maxId) { - maxId = entityId.GetRange(_performanceSettings.CacheSegmentSize, out minId); + maxId = entityId.GetRange(500, out minId); return (localeKeyGroup + "." + localeKey + "." + maxId.ToString()).ToLowerInvariant(); } } diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizedValue.cs b/src/Libraries/SmartStore.Services/Localization/LocalizedValue.cs index 625f53f986..ed3c57dcf4 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizedValue.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizedValue.cs @@ -37,7 +37,7 @@ public static string FixBrackets(string str, Language currentLanguage) [Serializable] public class LocalizedValue : IHtmlString, IEquatable>, IComparable, IComparable> { - private T _value; + private readonly T _value; private readonly Language _requestLanguage; private readonly Language _currentLanguage; @@ -75,11 +75,6 @@ public bool BidiOverride get { return _requestLanguage != _currentLanguage && _requestLanguage.Rtl != _currentLanguage.Rtl; } } - public void ChangeValue(T value) - { - _value = value; - } - public static implicit operator T(LocalizedValue obj) { if (obj == null) diff --git a/src/Libraries/SmartStore.Services/Media/PictureService.cs b/src/Libraries/SmartStore.Services/Media/PictureService.cs index 850ea22bdb..e15b780795 100644 --- a/src/Libraries/SmartStore.Services/Media/PictureService.cs +++ b/src/Libraries/SmartStore.Services/Media/PictureService.cs @@ -208,16 +208,14 @@ public virtual byte[] ValidatePicture(byte[] pictureBinary, string mimeType, out public virtual byte[] FindEqualPicture(byte[] pictureBinary, IEnumerable pictures, out int equalPictureId) { equalPictureId = 0; - - var myStream = new MemoryStream(pictureBinary); - try { foreach (var picture in pictures) { - myStream.Seek(0, SeekOrigin.Begin); + var otherPictureBinary = LoadPictureBinary(picture); - using (var otherStream = OpenPictureStream(picture)) + using (var myStream = new MemoryStream(pictureBinary)) + using (var otherStream = new MemoryStream(otherPictureBinary)) { if (myStream.ContentsEqual(otherStream)) { @@ -233,10 +231,6 @@ public virtual byte[] FindEqualPicture(byte[] pictureBinary, IEnumerable 0) - query = query.Take(() => recordsToReturn); + query = query.Take(recordsToReturn); var pics = query.ToList(); return pics; @@ -590,7 +584,7 @@ group pp by pp.ProductId into g ProductId = g.Key, Pictures = g.OrderBy(x => x.DisplayOrder) .Take(take) - .Select(x => new { x.PictureId, x.ProductId }) + .Select(x => new { PictureId = x.PictureId, ProductId = x.ProductId }) }; var groupingResult = query.ToDictionary(x => x.ProductId, x => x.Pictures); diff --git a/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs b/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs index 61fb676fbc..284269020b 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs @@ -627,7 +627,7 @@ private object GetModelFromExpression(string expression, IDictionary x.Id).Skip(() => skip).FirstOrDefault(); + result = query.OrderBy(x => x.Id).Skip(skip).FirstOrDefault(); } else { diff --git a/src/Libraries/SmartStore.Services/Orders/OrderReportService.cs b/src/Libraries/SmartStore.Services/Orders/OrderReportService.cs index a39d0e395c..46e10847da 100644 --- a/src/Libraries/SmartStore.Services/Orders/OrderReportService.cs +++ b/src/Libraries/SmartStore.Services/Orders/OrderReportService.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Data.Entity; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; @@ -72,38 +71,122 @@ public virtual OrderAverageReportLine GetOrderAverageReportLine(int storeId, int int[] paymentStatusIds, int[] shippingStatusIds, DateTime? startTimeUtc, DateTime? endTimeUtc, string billingEmail, bool ignoreCancelledOrders = false) { - var query = _orderRepository.Table; - query = query.Where(o => !o.Deleted); - if (storeId > 0) - query = query.Where(o => o.StoreId == storeId); - if (ignoreCancelledOrders) - { - int cancelledOrderStatusId = (int)OrderStatus.Cancelled; - query = query.Where(o => o.OrderStatusId != cancelledOrderStatusId); - } - if (startTimeUtc.HasValue) - query = query.Where(o => startTimeUtc.Value <= o.CreatedOnUtc); - if (endTimeUtc.HasValue) - query = query.Where(o => endTimeUtc.Value >= o.CreatedOnUtc); - if (!String.IsNullOrEmpty(billingEmail)) - query = query.Where(o => o.BillingAddress != null && !String.IsNullOrEmpty(o.BillingAddress.Email) && o.BillingAddress.Email.Contains(billingEmail)); + // TOFIX: GetOrderAverageReportLine group by error + //if (DataSettings.Current.IsMySqlServer) + //{ + // var query = _orderRepository.Table; + // query = query.Where(o => !o.Deleted); + // if (storeId > 0) + // { + // query = query.Where(o => o.StoreId == storeId); + // } + // if (ignoreCancelledOrders) + // { + // int cancelledOrderStatusId = (int)OrderStatus.Cancelled; + // query = query.Where(o => o.OrderStatusId != cancelledOrderStatusId); + // } + // if (startTimeUtc.HasValue) + // { + // query = query.Where(o => startTimeUtc.Value <= o.CreatedOnUtc); + // } + // if (endTimeUtc.HasValue) + // { + // query = query.Where(o => endTimeUtc.Value >= o.CreatedOnUtc); + // } + // if (!String.IsNullOrEmpty(billingEmail)) + // { + // query = query.Where(o => o.BillingAddress != null && !String.IsNullOrEmpty(o.BillingAddress.Email) && o.BillingAddress.Email.Contains(billingEmail)); + // } + // if (orderStatusIds != null && orderStatusIds.Count() > 0) + // { + // query = query.Where(x => orderStatusIds.Contains(x.OrderStatusId)); + // } + // if (paymentStatusIds != null && paymentStatusIds.Count() > 0) + // { + // query = query.Where(x => paymentStatusIds.Contains(x.PaymentStatusId)); + // } + // if (shippingStatusIds != null && shippingStatusIds.Count() > 0) + // { + // query = query.Where(x => shippingStatusIds.Contains(x.ShippingStatusId)); + // } + // var item = (from oq in query + // group oq by 1).Select(x => new OrderAverageReportLine() { + // SumTax = Sum(x.OrderTax()), + // CountOrders = x.OrderCount, + // SumOrders = x.OrderTotalSum + // }).FirstOrDefault(); + // item = item ?? new OrderAverageReportLine() { CountOrders = 0, SumOrders = decimal.Zero, SumTax = decimal.Zero }; + // return item; + // // return new OrderAverageReportLine() { CountOrders = 0, SumOrders = decimal.Zero, SumTax = decimal.Zero }; + //} + //else + //{ + var query = _orderRepository.Table; + query = query.Where(o => !o.Deleted); + + if (storeId > 0) + { + query = query.Where(o => o.StoreId == storeId); + } - if (orderStatusIds != null && orderStatusIds.Count() > 0) - query = query.Where(x => orderStatusIds.Contains(x.OrderStatusId)); + if (ignoreCancelledOrders) + { + int cancelledOrderStatusId = (int)OrderStatus.Cancelled; + query = query.Where(o => o.OrderStatusId != cancelledOrderStatusId); + } - if (paymentStatusIds != null && paymentStatusIds.Count() > 0) - query = query.Where(x => paymentStatusIds.Contains(x.PaymentStatusId)); + if (startTimeUtc.HasValue) + { + query = query.Where(o => startTimeUtc.Value <= o.CreatedOnUtc); + } - if (shippingStatusIds != null && shippingStatusIds.Count() > 0) - query = query.Where(x => shippingStatusIds.Contains(x.ShippingStatusId)); + if (endTimeUtc.HasValue) + { + query = query.Where(o => endTimeUtc.Value >= o.CreatedOnUtc); + } - var item = (from oq in query - group oq by 1 into result - select new { OrderCount = result.Count(), OrderTaxSum = result.Sum(o => o.OrderTax), OrderTotalSum = result.Sum(o => o.OrderTotal) } - ).Select(r => new OrderAverageReportLine(){ SumTax = r.OrderTaxSum, CountOrders=r.OrderCount, SumOrders = r.OrderTotalSum}).FirstOrDefault(); + if (!String.IsNullOrEmpty(billingEmail)) + { + query = query.Where(o => o.BillingAddress != null && !String.IsNullOrEmpty(o.BillingAddress.Email) && o.BillingAddress.Email.Contains(billingEmail)); + } + + if (orderStatusIds != null && orderStatusIds.Count() > 0) + { + query = query.Where(x => orderStatusIds.Contains(x.OrderStatusId)); + } + + if (paymentStatusIds != null && paymentStatusIds.Count() > 0) + { + query = query.Where(x => paymentStatusIds.Contains(x.PaymentStatusId)); + } + + if (shippingStatusIds != null && shippingStatusIds.Count() > 0) + { + query = query.Where(x => shippingStatusIds.Contains(x.ShippingStatusId)); + } + + if (query.Count() > 0) + { + var item = (from oq in query + group oq by 1 into result + select new + { + OrderCount = result.Count(), + OrderTaxSum = result.Sum(o => o.OrderTax), + OrderTotalSum = result.Sum(o => o.OrderTotal) + } + ).Select(r => new OrderAverageReportLine() { SumTax = r.OrderTaxSum, CountOrders = r.OrderCount, SumOrders = r.OrderTotalSum }).FirstOrDefault(); + + return item = item ?? new OrderAverageReportLine() { CountOrders = 0, SumOrders = decimal.Zero, SumTax = decimal.Zero }; + } + + return new OrderAverageReportLine() { + CountOrders = 0, + SumOrders = decimal.Zero, + SumTax = decimal.Zero + }; + //} - item = item ?? new OrderAverageReportLine() { CountOrders = 0, SumOrders = decimal.Zero, SumTax = decimal.Zero }; - return item; } /// @@ -249,7 +332,7 @@ group orderItem by orderItem.ProductId into g } if (recordsToReturn != 0 && recordsToReturn != int.MaxValue) - query2 = query2.Take(() => recordsToReturn); + query2 = query2.Take(recordsToReturn); var result = query2.ToList().Select(x => { @@ -309,7 +392,7 @@ group orderItem_p by orderItem_p.p.Id into g query3 = query3.OrderByDescending(x => x.ProductsPurchased); if (recordsToReturn > 0) - query3 = query3.Take(() => recordsToReturn); + query3 = query3.Take(recordsToReturn); var report = query3.ToList(); diff --git a/src/Libraries/SmartStore.Services/Orders/OrderService.cs b/src/Libraries/SmartStore.Services/Orders/OrderService.cs index 586f3656e1..62714bfebe 100644 --- a/src/Libraries/SmartStore.Services/Orders/OrderService.cs +++ b/src/Libraries/SmartStore.Services/Orders/OrderService.cs @@ -13,8 +13,13 @@ namespace SmartStore.Services.Orders { + /// + /// Order service + /// public partial class OrderService : IOrderService { + #region Fields + private readonly IRepository _orderRepository; private readonly IRepository _orderItemRepository; private readonly IRepository _orderNoteRepository; @@ -24,6 +29,21 @@ public partial class OrderService : IOrderService private readonly IRepository _returnRequestRepository; private readonly IEventPublisher _eventPublisher; + #endregion + + #region Ctor + + /// + /// Ctor + /// + /// Order repository + /// Order item repository + /// Order note repository + /// Product repository + /// Recurring payment repository + /// Customer repository + /// Return request repository + /// Event published public OrderService(IRepository orderRepository, IRepository orderItemRepository, IRepository orderNoteRepository, @@ -43,6 +63,17 @@ public OrderService(IRepository orderRepository, _eventPublisher = eventPublisher; } + #endregion + + #region Methods + + #region Orders + + /// + /// Gets an order + /// + /// The order identifier + /// Order public virtual Order GetOrderById(int orderId) { if (orderId == 0) @@ -51,6 +82,11 @@ public virtual Order GetOrderById(int orderId) return _orderRepository.GetById(orderId); } + /// + /// Get orders by identifiers + /// + /// Order identifiers + /// Order public virtual IList GetOrdersByIds(int[] orderIds) { if (orderIds == null || orderIds.Length == 0) @@ -86,6 +122,11 @@ public virtual Order GetOrderByNumber(string orderNumber) return order; } + /// + /// Gets an order + /// + /// The order identifier + /// Order public virtual Order GetOrderByGuid(Guid orderGuid) { if (orderGuid == Guid.Empty) @@ -98,6 +139,12 @@ public virtual Order GetOrderByGuid(Guid orderGuid) return order; } + /// + /// Get order by payment authorization data + /// + /// System name of the payment method + /// Authorization transaction Id + /// Order entity public virtual Order GetOrderByPaymentAuthorization(string paymentMethodSystemName, string authorizationTransactionId) { if (paymentMethodSystemName.IsEmpty() || authorizationTransactionId.IsEmpty()) @@ -111,6 +158,12 @@ from o in _orderRepository.Table return query.FirstOrDefault(); } + /// + /// Get order by payment capture data + /// + /// System name of the payment method + /// Capture transaction Id + /// Order entity public virtual Order GetOrderByPaymentCapture(string paymentMethodSystemName, string captureTransactionId) { if (paymentMethodSystemName.IsEmpty() || captureTransactionId.IsEmpty()) @@ -178,6 +231,23 @@ public virtual IQueryable GetOrders( return query; } + /// + /// Search orders + /// + /// Store identifier; 0 to load all orders + /// Customer identifier; 0 to load all orders + /// Order start time; null to load all orders + /// Order end time; null to load all orders + /// Filter by order status + /// Filter by payment status + /// Filter by shipping status + /// Billing email. Leave empty to load all records. + /// Search by order GUID (Global unique identifier) or part of GUID. Leave empty to load all orders. + /// Filter by order number + /// Page index + /// Page size + /// Billing name. Leave empty to load all records. + /// Order collection public virtual IPagedList SearchOrders(int storeId, int customerId, DateTime? startTime, DateTime? endTime, int[] orderStatusIds, int[] paymentStatusIds, int[] shippingStatusIds, string billingEmail, string orderGuid, string orderNumber, int pageIndex, int pageSize, string billingName = null) @@ -189,7 +259,7 @@ public virtual IPagedList SearchOrders(int storeId, int customerId, DateT if (orderGuid.HasValue()) { - // Filter by GUID. Filter in BLL because EF doesn't support casting of GUID to string + //filter by GUID. Filter in BLL because EF doesn't support casting of GUID to string var orders = query.ToList(); orders = orders.FindAll(x => x.OrderGuid.ToString().ToLowerInvariant().Contains(orderGuid.ToLowerInvariant())); @@ -197,10 +267,18 @@ public virtual IPagedList SearchOrders(int storeId, int customerId, DateT } else { + //database layer paging return new PagedList(query, pageIndex, pageSize); } } + /// + /// Gets all orders by affiliate identifier + /// + /// Affiliate identifier + /// Page index + /// Page size + /// Orders public virtual IPagedList GetAllOrders(int affiliateId, int pageIndex, int pageSize) { var query = _orderRepository.Table; @@ -212,11 +290,20 @@ public virtual IPagedList GetAllOrders(int affiliateId, int pageIndex, in return orders; } + /// + /// Load all orders + /// + /// Order collection public virtual IList LoadAllOrders() { return SearchOrders(0, 0, null, null, null, null, null, null, null, null, 0, int.MaxValue); } + /// + /// Gets all orders by affiliate identifier + /// + /// Affiliate identifier + /// Order collection public virtual IList GetOrdersByAffiliateId(int affiliateId) { var query = from o in _orderRepository.Table @@ -227,6 +314,10 @@ orderby o.CreatedOnUtc descending return orders; } + /// + /// Inserts an order + /// + /// Order public virtual void InsertOrder(Order order) { if (order == null) @@ -235,6 +326,10 @@ public virtual void InsertOrder(Order order) _orderRepository.Insert(order); } + /// + /// Updates the order + /// + /// The order public virtual void UpdateOrder(Order order) { if (order == null) @@ -245,6 +340,10 @@ public virtual void UpdateOrder(Order order) _eventPublisher.PublishOrderUpdated(order); } + /// + /// Deletes an order + /// + /// The order public virtual void DeleteOrder(Order order) { if (order == null) @@ -254,6 +353,10 @@ public virtual void DeleteOrder(Order order) UpdateOrder(order); } + /// + /// Deletes an order note + /// + /// The order note public virtual void DeleteOrderNote(OrderNote orderNote) { if (orderNote == null) @@ -267,6 +370,12 @@ public virtual void DeleteOrderNote(OrderNote orderNote) _eventPublisher.PublishOrderUpdated(order); } + /// + /// Get an order by authorization transaction ID and payment method system name + /// + /// Authorization transaction ID + /// Payment method system name + /// Order public virtual Order GetOrderByAuthorizationTransactionIdAndPaymentMethod(string authorizationTransactionId, string paymentMethodSystemName) { var query = _orderRepository.Table; @@ -296,8 +405,15 @@ public virtual void AddOrderNote(Order order, string note, bool displayToCustome } } + #endregion + #region Order items + /// + /// Gets an Order item + /// + /// Order item identifier + /// Order item public virtual OrderItem GetOrderItemById(int orderItemId) { if (orderItemId == 0) @@ -306,6 +422,11 @@ public virtual OrderItem GetOrderItemById(int orderItemId) return _orderItemRepository.GetById(orderItemId); } + /// + /// Gets an Order item + /// + /// Order item identifier + /// Order item public virtual OrderItem GetOrderItemByGuid(Guid orderItemGuid) { if (orderItemGuid == Guid.Empty) @@ -317,7 +438,19 @@ public virtual OrderItem GetOrderItemByGuid(Guid orderItemGuid) var item = query.FirstOrDefault(); return item; } - + + /// + /// Gets all Order items + /// + /// Order identifier; null to load all records + /// Customer identifier; null to load all records + /// Order start time; null to load all records + /// Order end time; null to load all records + /// Order status; null to load all records + /// Order payment status; null to load all records + /// Order shippment status; null to load all records + /// Value indicating whether to load downloadable products only + /// Order collection public virtual IList GetAllOrderItems(int? orderId, int? customerId, DateTime? startTime, DateTime? endTime, OrderStatus? os, PaymentStatus? ps, ShippingStatus? ss, @@ -372,6 +505,10 @@ where orderIds.Contains(x.OrderId) return map; } + /// + /// Delete an Order item + /// + /// The Order item public virtual void DeleteOrderItem(OrderItem orderItem) { if (orderItem == null) @@ -389,6 +526,10 @@ public virtual void DeleteOrderItem(OrderItem orderItem) #region Recurring payments + /// + /// Deletes a recurring payment + /// + /// Recurring payment public virtual void DeleteRecurringPayment(RecurringPayment recurringPayment) { if (recurringPayment == null) @@ -398,6 +539,11 @@ public virtual void DeleteRecurringPayment(RecurringPayment recurringPayment) UpdateRecurringPayment(recurringPayment); } + /// + /// Gets a recurring payment + /// + /// The recurring payment identifier + /// Recurring payment public virtual RecurringPayment GetRecurringPaymentById(int recurringPaymentId) { if (recurringPaymentId == 0) @@ -406,6 +552,10 @@ public virtual RecurringPayment GetRecurringPaymentById(int recurringPaymentId) return _recurringPaymentRepository.GetById(recurringPaymentId); } + /// + /// Inserts a recurring payment + /// + /// Recurring payment public virtual void InsertRecurringPayment(RecurringPayment recurringPayment) { if (recurringPayment == null) @@ -416,6 +566,10 @@ public virtual void InsertRecurringPayment(RecurringPayment recurringPayment) _eventPublisher.PublishOrderUpdated(recurringPayment.InitialOrder); } + /// + /// Updates the recurring payment + /// + /// Recurring payment public virtual void UpdateRecurringPayment(RecurringPayment recurringPayment) { if (recurringPayment == null) @@ -426,6 +580,15 @@ public virtual void UpdateRecurringPayment(RecurringPayment recurringPayment) _eventPublisher.PublishOrderUpdated(recurringPayment.InitialOrder); } + /// + /// Search recurring payments + /// + /// The store identifier; 0 to load all records + /// The customer identifier; 0 to load all records + /// The initial order identifier; 0 to load all records + /// Initial order status identifier; null to load all records + /// A value indicating whether to show hidden records + /// Recurring payment collection public virtual IList SearchRecurringPayments(int storeId, int customerId, int initialOrderId, OrderStatus? initialOrderStatus, bool showHidden = false) @@ -460,6 +623,10 @@ where query1.Contains(rp.Id) #region Return requests + /// + /// Deletes a return request + /// + /// Return request public virtual void DeleteReturnRequest(ReturnRequest returnRequest) { if (returnRequest == null) @@ -473,6 +640,11 @@ public virtual void DeleteReturnRequest(ReturnRequest returnRequest) _eventPublisher.PublishOrderUpdated(orderItem.Order); } + /// + /// Gets a return request + /// + /// Return request identifier + /// Return request public virtual ReturnRequest GetReturnRequestById(int returnRequestId) { if (returnRequestId == 0) @@ -481,6 +653,17 @@ public virtual ReturnRequest GetReturnRequestById(int returnRequestId) return _returnRequestRepository.GetById(returnRequestId); } + /// + /// Search return requests + /// + /// Store identifier; 0 to load all entries + /// Customer identifier; null to load all entries + /// Order item identifier; null to load all entries + /// Return request status; null to load all entries + /// Page index + /// Page size + /// Return request Id + /// Return requests public virtual IPagedList SearchReturnRequests(int storeId, int customerId, int orderItemId, ReturnRequestStatus? rs, int pageIndex, int pageSize, int id = 0) { var query = _returnRequestRepository.Table; @@ -510,5 +693,7 @@ public virtual IPagedList SearchReturnRequests(int storeId, int c } #endregion + + #endregion } } diff --git a/src/Libraries/SmartStore.Services/ScopedServiceBase.cs b/src/Libraries/SmartStore.Services/ScopedServiceBase.cs index 961fa5cd6f..3bd839061b 100644 --- a/src/Libraries/SmartStore.Services/ScopedServiceBase.cs +++ b/src/Libraries/SmartStore.Services/ScopedServiceBase.cs @@ -5,6 +5,8 @@ namespace SmartStore.Services { public abstract class ScopedServiceBase : IScopedService { + private bool _isInScope; + /// /// Creates a long running unit of work in which cache eviction is suppressed /// @@ -12,18 +14,18 @@ public abstract class ScopedServiceBase : IScopedService /// A disposable unit of work public IDisposable BeginScope(bool clearCache = true) { - if (IsInScope) + if (_isInScope) { // nested batches are not supported return ActionDisposable.Empty; } OnBeginScope(); - IsInScope = true; + _isInScope = true; return new ActionDisposable(() => { - IsInScope = false; + _isInScope = false; OnEndScope(); if (clearCache && HasChanges) { @@ -46,13 +48,12 @@ public bool HasChanges protected bool IsInScope { - get; - private set; + get { return _isInScope; } } public void ClearCache() { - if (!IsInScope) + if (!_isInScope) { OnClearCache(); HasChanges = false; diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchQuery.cs b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchQuery.cs index cfc21d3a9f..00be3a01bc 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchQuery.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchQuery.cs @@ -37,20 +37,6 @@ object ICloneable.Clone() return this.MemberwiseClone(); } - public bool IsSubPage - { - get - { - if (PageIndex > 0) - { - return true; - } - - var hasActiveFilter = FacetDescriptors.Values.Any(x => x.Values.Any(y => y.IsSelected)); - return hasActiveFilter; - } - } - #region Fluent builder public CatalogSearchQuery SortBy(ProductSortingEnum sort) @@ -206,9 +192,12 @@ public CatalogSearchQuery WithCategoryIds(bool? featuredOnly, params int[] ids) return this; } - var fieldName = featuredOnly.HasValue - ? featuredOnly.Value ? "featuredcategoryid" : "notfeaturedcategoryid" - : "categoryid"; + string fieldName = null; + + if (featuredOnly.HasValue) + fieldName = (featuredOnly.Value ? "featuredcategoryid" : "notfeaturedcategoryid"); + else + fieldName = "categoryid"; return WithFilter(SearchFilter.Combined(ids.Select(x => SearchFilter.ByField(fieldName, x).ExactMatch().NotAnalyzed()).ToArray())); } @@ -233,9 +222,12 @@ public CatalogSearchQuery WithManufacturerIds(bool? featuredOnly, params int[] i return this; } - var fieldName = featuredOnly.HasValue - ? featuredOnly.Value ? "featuredmanufacturerid" : "notfeaturedmanufacturerid" - : "manufacturerid"; + string fieldName = null; + + if (featuredOnly.HasValue) + fieldName = (featuredOnly.Value ? "featuredmanufacturerid" : "notfeaturedmanufacturerid"); + else + fieldName = "manufacturerid"; return WithFilter(SearchFilter.Combined(ids.Select(x => SearchFilter.ByField(fieldName, x).ExactMatch().NotAnalyzed()).ToArray())); } diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchResult.cs b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchResult.cs index c19da2f48f..87fe6940dc 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchResult.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchResult.cs @@ -9,7 +9,8 @@ namespace SmartStore.Services.Search { public partial class CatalogSearchResult { - private readonly Func> _hitsFactory; + private readonly int _totalHitsCount; + private readonly Func> _hitsFactory; private IPagedList _hits; public CatalogSearchResult( @@ -28,7 +29,7 @@ public CatalogSearchResult( Facets = facets ?? new Dictionary(); _hitsFactory = hitsFactory ?? (() => new List()); - TotalHitsCount = totalHitsCount; + _totalHitsCount = totalHitsCount; } /// @@ -49,23 +50,26 @@ public IPagedList Hits { if (_hits == null) { - var products = TotalHitsCount == 0 + var products = _totalHitsCount == 0 ? new List() : _hitsFactory.Invoke(); - _hits = new PagedList(products, Query.PageIndex, Query.Take, TotalHitsCount); + _hits = new PagedList(products, Query.PageIndex, Query.Take, _totalHitsCount); } return _hits; } } - public int TotalHitsCount { get; } + public int TotalHitsCount + { + get { return _totalHitsCount; } + } - /// - /// The original catalog search query - /// - public CatalogSearchQuery Query + /// + /// The original catalog search query + /// + public CatalogSearchQuery Query { get; private set; diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchService.cs b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchService.cs index b790813c4a..51ed0e2dbd 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchService.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchService.cs @@ -165,7 +165,7 @@ public CatalogSearchResult Search( if (searchQuery.Take > 0) { - using (_services.Chronometer.Step(stepPrefix + "Search")) + using (_services.Chronometer.Step(stepPrefix + "Count")) { totalCount = searchEngine.Count(); // Fix paging boundaries diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/LinqCatalogSearchService.cs b/src/Libraries/SmartStore.Services/Search/Catalog/LinqCatalogSearchService.cs index fcd96b6675..ad2d03a969 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/LinqCatalogSearchService.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/LinqCatalogSearchService.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Data.Entity; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Localization; @@ -131,8 +130,7 @@ protected virtual IQueryable GetProductQuery(CatalogSearchQuery searchQ { var ordered = false; var utcNow = DateTime.UtcNow; - var categoryId = 0; - var manufacturerId = 0; + var isGroupingRequired = false; var query = baseQuery ?? _productRepository.Table; query = query.Where(x => !x.Deleted && !x.IsSystemProduct); @@ -152,14 +150,14 @@ protected virtual IQueryable GetProductQuery(CatalogSearchQuery searchQ var categoryIds = GetIdList(filters, "categoryid"); if (categoryIds.Any()) { - categoryId = categoryIds.First(); - if (categoryIds.Count == 1 && categoryId == 0) + if (categoryIds.Count == 1 && categoryIds.First() == 0) { // Has no category. query = query.Where(x => x.ProductCategories.Count == 0); } else { + isGroupingRequired = true; query = QueryCategories(query, categoryIds, null); } } @@ -168,26 +166,26 @@ protected virtual IQueryable GetProductQuery(CatalogSearchQuery searchQ var notFeaturedCategoryIds = GetIdList(filters, "notfeaturedcategoryid"); if (featuredCategoryIds.Any()) { - categoryId = categoryId == 0 ? featuredCategoryIds.First() : categoryId; + isGroupingRequired = true; query = QueryCategories(query, featuredCategoryIds, true); } if (notFeaturedCategoryIds.Any()) { - categoryId = categoryId == 0 ? notFeaturedCategoryIds.First() : categoryId; + isGroupingRequired = true; query = QueryCategories(query, notFeaturedCategoryIds, false); } var manufacturerIds = GetIdList(filters, "manufacturerid"); if (manufacturerIds.Any()) { - manufacturerId = manufacturerIds.First(); - if (manufacturerIds.Count == 1 && manufacturerId == 0) + if (manufacturerIds.Count == 1 && manufacturerIds.First() == 0) { - // Has no manufacturer. + // has no manufacturer query = query.Where(x => x.ProductManufacturers.Count == 0); } else { + isGroupingRequired = true; query = QueryManufacturers(query, manufacturerIds, null); } } @@ -196,18 +194,19 @@ protected virtual IQueryable GetProductQuery(CatalogSearchQuery searchQ var notFeaturedManuIds = GetIdList(filters, "notfeaturedmanufacturerid"); if (featuredManuIds.Any()) { - manufacturerId = manufacturerId == 0 ? featuredManuIds.First() : manufacturerId; + isGroupingRequired = true; query = QueryManufacturers(query, featuredManuIds, true); } if (notFeaturedManuIds.Any()) { - manufacturerId = manufacturerId == 0 ? notFeaturedManuIds.First() : manufacturerId; + isGroupingRequired = true; query = QueryManufacturers(query, notFeaturedManuIds, false); } var tagIds = GetIdList(filters, "tagid"); if (tagIds.Any()) { + isGroupingRequired = true; query = from p in query from pt in p.ProductTags.Where(pt => tagIds.Contains(pt.Id)) @@ -219,6 +218,7 @@ from pt in p.ProductTags.Where(pt => tagIds.Contains(pt.Id)) var roleIds = GetIdList(filters, "roleid"); if (roleIds.Any()) { + isGroupingRequired = true; query = from p in query join acl in _aclRepository.Table on new { pid = p.Id, pname = "Product" } equals new { pid = acl.EntityId, pname = acl.EntityName } into pacl @@ -459,6 +459,7 @@ from acl in pacl.DefaultIfEmpty() var storeId = (int)filter.Term; if (storeId != 0) { + isGroupingRequired = true; query = from p in query join sm in _storeMappingRepository.Table on new { pid = p.Id, pname = "Product" } equals new { pid = sm.EntityId, pname = sm.EntityName } into psm @@ -472,12 +473,15 @@ from sm in psm.DefaultIfEmpty() #endregion - // Grouping is very slow if there are many products. - query = - from p in query - group p by p.Id into grp - orderby grp.Key - select grp.FirstOrDefault(); + if (isGroupingRequired) + { + // Grouping is very slow if there are many products. + query = + from p in query + group p by p.Id into grp + orderby grp.Key + select grp.FirstOrDefault(); + } #region Sorting @@ -486,12 +490,14 @@ orderby grp.Key if (sort.FieldName.IsEmpty()) { // Sort by relevance. - if (categoryId != 0) + if (categoryIds.Any()) { + var categoryId = categoryIds.First(); query = OrderBy(ref ordered, query, x => x.ProductCategories.Where(pc => pc.CategoryId == categoryId).FirstOrDefault().DisplayOrder); } - else if (manufacturerId != 0) + else if (manufacturerIds.Any()) { + var manufacturerId = manufacturerIds.First(); query = OrderBy(ref ordered, query, x => x.ProductManufacturers.Where(pm => pm.ManufacturerId == manufacturerId).FirstOrDefault().DisplayOrder); } else if (FindFilter(searchQuery.Filters, "parentid") != null) @@ -643,8 +649,8 @@ protected virtual IDictionary GetFacets(CatalogSearchQuery s { var count = 0; var hasActivePredefinedFacet = false; - var minPrice = _productRepository.Table.Where(x => x.Published && !x.Deleted && !x.IsSystemProduct).Min(x => (double)x.Price); - var maxPrice = _productRepository.Table.Where(x => x.Published && !x.Deleted && !x.IsSystemProduct).Max(x => (double)x.Price); + var minPrice = _productRepository.Table.Where(x => !x.Deleted && x.Published && !x.IsSystemProduct).Min(x => (double)x.Price); + var maxPrice = _productRepository.Table.Where(x => !x.Deleted && x.Published && !x.IsSystemProduct).Max(x => (double)x.Price); minPrice = FacetUtility.MakePriceEven(minPrice); maxPrice = FacetUtility.MakePriceEven(maxPrice); @@ -759,11 +765,9 @@ public CatalogSearchResult Search(CatalogSearchQuery searchQuery, ProductLoadFla if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithHits)) { - var skip = searchQuery.PageIndex * searchQuery.Take; - - query = query - .Skip(() => skip) - .Take(() => searchQuery.Take); + query = query + .Skip(searchQuery.PageIndex * searchQuery.Take) + .Take(searchQuery.Take); var ids = query.Select(x => x.Id).ToArray(); hitsFactory = () => _productService.GetProductsByIds(ids, loadFlags); diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryFactory.cs b/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryFactory.cs index 88bb3da613..4f4674d168 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryFactory.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryFactory.cs @@ -330,15 +330,13 @@ private void AddFacet( throw new SmartException($"Unknown field name for facet group '{kind.ToString()}'"); } - var descriptor = new FacetDescriptor(fieldName) - { - Label = _services.Localization.GetResource(FacetUtility.GetLabelResourceKey(kind) ?? kind.ToString()), - IsMultiSelect = isMultiSelect, - DisplayOrder = displayOrder, - OrderBy = sorting, - MinHitCount = _searchSettings.FilterMinHitCount, - MaxChoicesCount = _searchSettings.FilterMaxChoicesCount - }; + var descriptor = new FacetDescriptor(fieldName); + descriptor.Label = _services.Localization.GetResource(FacetUtility.GetLabelResourceKey(kind) ?? kind.ToString()); + descriptor.IsMultiSelect = isMultiSelect; + descriptor.DisplayOrder = displayOrder; + descriptor.OrderBy = sorting; + descriptor.MinHitCount = _searchSettings.FilterMinHitCount; + descriptor.MaxChoicesCount = _searchSettings.FilterMaxChoicesCount; addValues(descriptor); query.WithFacet(descriptor); diff --git a/src/Libraries/SmartStore.Services/Search/Forum/LinqForumSearchService.cs b/src/Libraries/SmartStore.Services/Search/Forum/LinqForumSearchService.cs index cce742613e..c39dec94b2 100644 --- a/src/Libraries/SmartStore.Services/Search/Forum/LinqForumSearchService.cs +++ b/src/Libraries/SmartStore.Services/Search/Forum/LinqForumSearchService.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Data.Entity; using SmartStore.Core.Data; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Forums; @@ -328,8 +327,7 @@ protected virtual IDictionary GetFacets(ForumSearchQuery sea // Limit the result. Do not allow to get all customers. var maxChoices = descriptor.MaxChoicesCount > 0 ? descriptor.MaxChoicesCount : 20; - var take = maxChoices * 3; - var customers = customerQuery.Take(() => take).ToList(); + var customers = customerQuery.Take(maxChoices * 3).ToList(); foreach (var customer in customers) { @@ -401,10 +399,9 @@ public ForumSearchResult Search(ForumSearchQuery searchQuery, bool direct = fals if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithHits)) { - var skip = searchQuery.PageIndex * searchQuery.Take; query = query - .Skip(() => skip) - .Take(() => searchQuery.Take); + .Skip(searchQuery.PageIndex * searchQuery.Take) + .Take(searchQuery.Take); var ids = query.Select(x => x.Id).ToArray(); hitsFactory = () => _forumService.GetPostsByIds(ids); diff --git a/src/Libraries/SmartStore.Services/Security/AclService.cs b/src/Libraries/SmartStore.Services/Security/AclService.cs index 426fa881f1..52b09eebcc 100644 --- a/src/Libraries/SmartStore.Services/Security/AclService.cs +++ b/src/Libraries/SmartStore.Services/Security/AclService.cs @@ -26,11 +26,7 @@ public partial class AclService : IAclService private bool? _hasActiveAcl; - public AclService( - ICacheManager cacheManager, - Work workContext, - IRepository aclRecordRepository, - ICustomerService customerService) + public AclService(ICacheManager cacheManager, Work workContext, IRepository aclRecordRepository, ICustomerService customerService) { _cacheManager = cacheManager; _workContext = workContext; @@ -73,7 +69,7 @@ public IList GetAclRecords(T entity) where T : BaseEntity, IAclSup Guard.NotNull(entity, nameof(entity)); int entityId = entity.Id; - string entityName = entity.GetEntityName(); + string entityName = typeof(T).Name; return GetAclRecordsFor(entityName, entityId); } @@ -131,7 +127,7 @@ public virtual void InsertAclRecord(T entity, int customerRoleId) where T : B throw new ArgumentOutOfRangeException(nameof(customerRoleId)); int entityId = entity.Id; - string entityName = entity.GetEntityName(); + string entityName = typeof(T).Name; var aclRecord = new AclRecord { diff --git a/src/Libraries/SmartStore.Services/Security/IAclService.cs b/src/Libraries/SmartStore.Services/Security/IAclService.cs index 552f467f2b..b6032be635 100644 --- a/src/Libraries/SmartStore.Services/Security/IAclService.cs +++ b/src/Libraries/SmartStore.Services/Security/IAclService.cs @@ -115,7 +115,7 @@ public static int[] GetCustomerRoleIdsWithAccess(this IAclService aclService, if (entity == null) return new int[0]; - return aclService.GetCustomerRoleIdsWithAccess(entity.GetEntityName(), entity.Id); + return aclService.GetCustomerRoleIdsWithAccess(typeof(T).Name, entity.Id); } /// @@ -132,7 +132,7 @@ public static bool Authorize(this IAclService aclService, T entity) where T : if (!entity.SubjectToAcl) return true; - return aclService.Authorize(entity.GetEntityName(), entity.Id); + return aclService.Authorize(typeof(T).Name, entity.Id); } /// @@ -150,7 +150,7 @@ public static bool Authorize(this IAclService aclService, T entity, Customer if (!entity.SubjectToAcl) return true; - return aclService.Authorize(entity.GetEntityName(), entity.Id, customer); + return aclService.Authorize(typeof(T).Name, entity.Id, customer); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs b/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs index 639dc8efc1..15d0d8bb35 100644 --- a/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs +++ b/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs @@ -63,39 +63,6 @@ public partial interface IUrlRecordService : IScopedService /// Customer collection IPagedList GetAllUrlRecords(int pageIndex, int pageSize, string slug, string entityName, int? entityId, int? languageId, bool? isActive); - /// - /// Prefetches a collection of url records properties for a range of entities in one go - /// and caches them for the duration of the current request. - /// - /// Entity name - /// - /// The entity ids to prefetch url records for. Can be null, - /// in which case all records for the requested entity name are loaded. - /// - /// Whether represents a range of ids (perf). - /// Whether is already sorted (perf). - /// Url record collection - /// - /// Be careful not to load large amounts of data at once (e.g. for "Product" scope with large range). - /// - void PrefetchUrlRecords(string entityName, int[] languageIds, int[] entityIds, bool isRange = false, bool isSorted = false); - - /// - /// Prefetches a collection of url records properties for a range of entities in one go. - /// - /// Entity name - /// - /// The entity ids to prefetch url records for. Can be null, - /// in which case all records for the requested entity name are loaded. - /// - /// Whether represents a range of ids (perf). - /// Whether is already sorted (perf). - /// Url record collection - /// - /// Be careful not to load large amounts of data at once (e.g. for "Product" scope with large range). - /// - UrlRecordCollection GetUrlRecordCollection(string entityName, int[] languageIds, int[] entityIds, bool isRange = false, bool isSorted = false); - /// /// Gets all URL records for the specified entity /// diff --git a/src/Libraries/SmartStore.Services/Seo/IXmlSitemapGenerator.cs b/src/Libraries/SmartStore.Services/Seo/IXmlSitemapGenerator.cs index f1c7b37d22..0861c8fe92 100644 --- a/src/Libraries/SmartStore.Services/Seo/IXmlSitemapGenerator.cs +++ b/src/Libraries/SmartStore.Services/Seo/IXmlSitemapGenerator.cs @@ -1,6 +1,4 @@ -using SmartStore.Services.Tasks; using System.Collections.Generic; -using System.Threading; namespace SmartStore.Services.Seo { @@ -10,41 +8,24 @@ namespace SmartStore.Services.Seo public partial interface IXmlSitemapGenerator { /// - /// Gets the sitemap partition + /// Gets the sitemap XML /// /// - /// The page index. 0 retrieves the first document, which can be a sitemap INDEX document + /// The page index. 0 or null retrieves the first document, which can be a sitemap INDEX document /// (when the sitemap size exceeded the limits). An index greater 0 retrieves the /// sitemap XML document at this index, but only when the sitemap is actually indexed (otherwise null is returned) /// - /// Sitemap partition - XmlSitemapPartition GetSitemapPart(int index = 0); + /// Sitemap XML + string GetSitemap(int? index = null); /// - /// Rebuilds the collection of XML sitemap documents for a store/language combination. If there are less than 1.000 sitemap - /// nodes, only one sitemap document will exist in the collection, otherwise a sitemap index document will be - /// the first entry in the collection and all other entries will be sitemap XML documents. - /// - /// The build context - /// - /// During rebuilding, requests are being served from the existing cache. - /// Once rebuild is completed, the cache is updated. - /// - void Rebuild(XmlSitemapBuildContext ctx); - - /// - /// Determines whether a rebuild is already running. + /// Removes the sitemap from the cache for a rebuild. /// - bool IsRebuilding(int storeId, int languageId); + void Invalidate(); /// /// Indicates whether the sitemap has been generated and cached. /// - bool IsGenerated(int storeId, int languageId); - - /// - /// Removes the sitemap from the cache for a rebuild. - /// - void Invalidate(int storeId, int languageId); + bool IsGenerated { get; } } } diff --git a/src/Libraries/SmartStore.Services/Seo/SeoExtensions.cs b/src/Libraries/SmartStore.Services/Seo/SeoExtensions.cs index d427ecc2d8..22224a45b4 100644 --- a/src/Libraries/SmartStore.Services/Seo/SeoExtensions.cs +++ b/src/Libraries/SmartStore.Services/Seo/SeoExtensions.cs @@ -131,7 +131,7 @@ public static string GetSeName(this T entity) Guard.NotNull(entity, nameof(entity)); return GetSeName( - entity.GetEntityName(), + typeof(T).Name, entity.Id, EngineContext.Current.Resolve().WorkingLanguage.Id, EngineContext.Current.Resolve(), @@ -156,7 +156,7 @@ public static string GetSeName(this T entity, Guard.NotNull(entity, nameof(entity)); return GetSeName( - entity.GetEntityName(), + typeof(T).Name, entity.Id, languageId, EngineContext.Current.Resolve(), @@ -185,7 +185,7 @@ public static string GetSeName(this T entity, Guard.NotNull(entity, nameof(entity)); return GetSeName( - entity.GetEntityName(), + typeof(T).Name, entity.Id, languageId, urlRecordService, @@ -298,7 +298,7 @@ public static string ValidateSeName(this T entity, } // ensure this sename is not reserved yet - string entityName = entity.GetEntityName(); + string entityName = typeof(T).Name; int i = 2; var tempSeName = seName; diff --git a/src/Libraries/SmartStore.Services/Seo/Tasks/RebuildXmlSitemapTask.cs b/src/Libraries/SmartStore.Services/Seo/Tasks/RebuildXmlSitemapTask.cs index bfd1186699..ff719bbe05 100644 --- a/src/Libraries/SmartStore.Services/Seo/Tasks/RebuildXmlSitemapTask.cs +++ b/src/Libraries/SmartStore.Services/Seo/Tasks/RebuildXmlSitemapTask.cs @@ -4,51 +4,30 @@ using System.Text; using System.Threading.Tasks; using SmartStore.Core.Domain.Seo; -using SmartStore.Services.Localization; -using SmartStore.Services.Stores; using SmartStore.Services.Tasks; namespace SmartStore.Services.Seo { public class RebuildXmlSitemapTask : ITask { - private readonly IStoreService _storeService; - private readonly ILanguageService _languageService; private readonly IXmlSitemapGenerator _generator; private readonly SeoSettings _seoSettings; - public RebuildXmlSitemapTask( - IStoreService storeService, - ILanguageService languageService, - IXmlSitemapGenerator generator, - SeoSettings seoSettings) + public RebuildXmlSitemapTask(IXmlSitemapGenerator generator, SeoSettings seoSettings) { - _storeService = storeService; - _languageService = languageService; _generator = generator; _seoSettings = seoSettings; } public void Execute(TaskExecutionContext ctx) { - var stores = _storeService.GetAllStores(); - - foreach (var store in stores) + if (_generator.IsGenerated) { - var languages = _languageService.GetAllLanguages(false, store.Id); - var buildContext = new XmlSitemapBuildContext(store, languages.ToArray()) - { - CancellationToken = ctx.CancellationToken, - ProgressCallback = OnProgress - }; - - _generator.Rebuild(buildContext); + _generator.Invalidate(); } - void OnProgress(int value, int max, string msg) - { - ctx.SetProgress(value, max, msg, true); - } + // enforces refresh + _generator.GetSitemap(0); } } } diff --git a/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs b/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs index bbdb23440b..86ec6e8ccf 100644 --- a/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs +++ b/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs @@ -2,11 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Threading; using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Data; -using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Seo; namespace SmartStore.Services.Seo @@ -16,58 +14,22 @@ public partial class UrlRecordService : ScopedServiceBase, IUrlRecordService /// /// 0 = segment (EntityName.IdRange), 1 = language id /// - const string URLRECORD_SEGMENT_KEY = "urlrecord:segment:{0}-lang-{1}"; - const string URLRECORD_SEGMENT_PATTERN = "urlrecord:segment:{0}*"; + const string URLRECORD_SEGMENT_KEY = "urlrecord:{0}-lang-{1}"; + const string URLRECORD_SEGMENT_PATTERN = "urlrecord:{0}*"; const string URLRECORD_ALL_PATTERN = "urlrecord:*"; const string URLRECORD_ALL_ACTIVESLUGS_KEY = "urlrecord:all-active-slugs"; private readonly IRepository _urlRecordRepository; private readonly ICacheManager _cacheManager; private readonly SeoSettings _seoSettings; - private readonly PerformanceSettings _performanceSettings; - private readonly IDictionary _prefetchedCollections; - private static int _lastCacheSegmentSize = -1; - - public UrlRecordService( - ICacheManager cacheManager, - IRepository urlRecordRepository, - SeoSettings seoSettings, - PerformanceSettings performanceSettings) + public UrlRecordService(ICacheManager cacheManager, IRepository urlRecordRepository, SeoSettings seoSettings) { - _cacheManager = cacheManager; - _urlRecordRepository = urlRecordRepository; - _seoSettings = seoSettings; - _performanceSettings = performanceSettings; - - _prefetchedCollections = new Dictionary(StringComparer.OrdinalIgnoreCase); - - ValidateCacheState(); + this._cacheManager = cacheManager; + this._urlRecordRepository = urlRecordRepository; + this._seoSettings = seoSettings; } - private void ValidateCacheState() - { - // Ensure that after a segment size change the cache segments are invalidated. - var size = _performanceSettings.CacheSegmentSize; - var changed = _lastCacheSegmentSize == -1; - - if (size <= 0) - { - _performanceSettings.CacheSegmentSize = size = 1; - } - - if (_lastCacheSegmentSize > 0 && _lastCacheSegmentSize != size) - { - _cacheManager.RemoveByPattern(URLRECORD_SEGMENT_PATTERN); - changed = true; - } - - if (changed) - { - Interlocked.Exchange(ref _lastCacheSegmentSize, size); - } - } - protected override void OnClearCache() { _cacheManager.Remove(URLRECORD_ALL_PATTERN); @@ -184,81 +146,6 @@ public virtual IList GetUrlRecordsFor(string entityName, int entityId return query.ToList(); } - public virtual void PrefetchUrlRecords(string entityName, int[] languageIds, int[] entityIds, bool isRange = false, bool isSorted = false) - { - var collection = GetUrlRecordCollectionInternal(entityName, languageIds, entityIds, isRange, isSorted); - - if (_prefetchedCollections.TryGetValue(entityName, out var existing)) - { - collection.MergeWith(existing); - } - else - { - _prefetchedCollections[entityName] = collection; - } - } - - public virtual UrlRecordCollection GetUrlRecordCollection(string entityName, int[] languageIds, int[] entityIds, bool isRange = false, bool isSorted = false) - { - return GetUrlRecordCollectionInternal(entityName, languageIds, entityIds, isRange, isSorted); - } - - public virtual UrlRecordCollection GetUrlRecordCollectionInternal(string entityName, int[] languageIds, int[] entityIds, bool isRange = false, bool isSorted = false) - { - Guard.NotEmpty(entityName, nameof(entityName)); - - using (new DbContextScope(proxyCreation: false, lazyLoading: false)) - { - var query = from x in _urlRecordRepository.TableUntracked - where x.EntityName == entityName && x.IsActive - select x; - - var requestedSet = entityIds; - - if (entityIds != null && entityIds.Length > 0) - { - if (isRange) - { - if (!isSorted) - { - Array.Sort(entityIds); - } - - var min = entityIds[0]; - var max = entityIds[entityIds.Length - 1]; - - if (entityIds.Length == 2 && max > min + 1) - { - // Only min & max were passed, create the range sequence. - requestedSet = Enumerable.Range(min, max - min + 1).ToArray(); - } - - query = query.Where(x => x.EntityId >= min && x.EntityId <= max); - } - else - { - requestedSet = entityIds; - query = query.Where(x => entityIds.Contains(x.EntityId)); - } - } - - if (languageIds != null && languageIds.Length > 0) - { - if (languageIds.Length == 1) - { - query = query.Where(x => x.LanguageId == languageIds[0]); - } - else - { - query = query.Where(x => languageIds.Contains(x.LanguageId)); - } - } - - // Don't sort DESC, because latter items overwrite exisiting ones (it's the same as sorting DESC and taking the first) - return new UrlRecordCollection(entityName, requestedSet, query.OrderBy(x => x.Id).ToList()); - } - } - public virtual UrlRecord GetBySlug(string slug) { // INFO: (mc) Caching unnecessary here. This is not a 'bottleneck' function. @@ -275,15 +162,6 @@ public virtual UrlRecord GetBySlug(string slug) public virtual string GetActiveSlug(int entityId, string entityName, int languageId) { - if (_prefetchedCollections.TryGetValue(entityName, out var collection)) - { - var cachedItem = collection.Find(languageId, entityId); - if (cachedItem != null) - { - return cachedItem.Slug.EmptyNull(); - } - } - if (IsInScope) { return GetActiveSlugUncached(entityId, entityName, languageId); @@ -295,20 +173,17 @@ public virtual string GetActiveSlug(int entityId, string entityName, int languag { var allActiveSlugs = _cacheManager.Get(URLRECORD_ALL_ACTIVESLUGS_KEY, () => { - using (new DbContextScope(proxyCreation: false, lazyLoading: false)) - { - var query = from x in _urlRecordRepository.TableUntracked - where x.IsActive - orderby x.Id descending - select x; - - var result = query.ToDictionarySafe( - x => GenerateKey(x.EntityId, x.EntityName, x.LanguageId), - x => x.Slug, - StringComparer.OrdinalIgnoreCase); - - return result; - } + var query = from x in _urlRecordRepository.TableUntracked + where x.IsActive + orderby x.Id descending + select x; + + var result = query.ToDictionarySafe( + x => GenerateKey(x.EntityId, x.EntityName, x.LanguageId), + x => x.Slug, + StringComparer.OrdinalIgnoreCase); + + return result; }); var key = GenerateKey(entityId, entityName, languageId); @@ -349,7 +224,7 @@ public virtual UrlRecord SaveSlug(T entity, string slug, int languageId) wher Guard.NotNull(entity, nameof(entity)); int entityId = entity.Id; - string entityName = entity.GetEntityName(); + string entityName = typeof(T).Name; UrlRecord result = null; var query = from ur in _urlRecordRepository.Table @@ -514,29 +389,26 @@ protected virtual IDictionary GetCacheSegment(string entityName, in return _cacheManager.Get(cacheKey, () => { - using (new DbContextScope(proxyCreation: false, lazyLoading: false)) + var query = from ur in _urlRecordRepository.TableUntracked + where + ur.EntityId >= minEntityId && + ur.EntityId <= maxEntityId && + ur.EntityName == entityName && + ur.LanguageId == languageId && + ur.IsActive + orderby ur.Id descending + select ur; + + var urlRecords = query.ToList(); + + var dict = new Dictionary(urlRecords.Count); + + foreach (var ur in urlRecords) { - var query = from ur in _urlRecordRepository.TableUntracked - where - ur.EntityId >= minEntityId && - ur.EntityId <= maxEntityId && - ur.EntityName == entityName && - ur.LanguageId == languageId && - ur.IsActive - orderby ur.Id descending - select ur; - - var urlRecords = query.ToList(); - - var dict = new Dictionary(urlRecords.Count); - - foreach (var ur in urlRecords) - { - dict[ur.EntityId] = ur.Slug.EmptyNull(); - } - - return dict; + dict[ur.EntityId] = ur.Slug.EmptyNull(); } + + return dict; }); } @@ -575,7 +447,7 @@ private string GetSegmentKeyPart(string entityName, int entityId) private string GetSegmentKeyPart(string entityName, int entityId, out int minId, out int maxId) { - maxId = entityId.GetRange(_performanceSettings.CacheSegmentSize, out minId); + maxId = entityId.GetRange(500, out minId); return (entityName + "." + maxId.ToString()).ToLowerInvariant(); } } diff --git a/src/Libraries/SmartStore.Services/Seo/XmlSitemapGenerator.cs b/src/Libraries/SmartStore.Services/Seo/XmlSitemapGenerator.cs index e0f4162c2a..b68b641ce6 100644 --- a/src/Libraries/SmartStore.Services/Seo/XmlSitemapGenerator.cs +++ b/src/Libraries/SmartStore.Services/Seo/XmlSitemapGenerator.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Threading; -using System.Data.Entity; using System.Web.Mvc; using System.Xml.Linq; using SmartStore.Core; @@ -12,29 +10,30 @@ using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Security; using SmartStore.Core.Domain.Seo; -using SmartStore.Core.Domain.Topics; using SmartStore.Core.Logging; using SmartStore.Services.Catalog; using SmartStore.Services.Customers; using SmartStore.Services.Localization; using SmartStore.Services.Search; using SmartStore.Services.Topics; -using SmartStore.Core.IO; -using SmartStore.Utilities; -using System.IO; -using SmartStore.Collections; -using SmartStore.Core.Domain.Stores; -using SmartStore.Core.Domain.Localization; -using System.Threading.Tasks; namespace SmartStore.Services.Seo { public partial class XmlSitemapGenerator : IXmlSitemapGenerator { + /// + /// Key for seo sitemap + /// + /// + /// {0} : sitemap index + /// {1} : current store id + /// {2} : current language id + /// + public const string XMLSITEMAP_DOCUMENT_KEY = "sitemap:xml-idx{0}-{1}-{2}"; + public const string XMLSITEMAP_PATTERN_KEY = "sitemap:xml*"; + private const string SiteMapsNamespace = "http://www.sitemaps.org/schemas/sitemap/0.9"; private const string XhtmlNamespace = "http://www.w3.org/1999/xhtml"; - private const string SiteMapFileNamePattern = "sitemap-{0}.xml"; - private const string LockFileNamePattern = "sitemap-{0}-{1}.lock"; /// /// The maximum number of sitemaps a sitemap index file can contain. @@ -44,30 +43,27 @@ public partial class XmlSitemapGenerator : IXmlSitemapGenerator /// /// The maximum number of sitemap nodes allowed in a sitemap file. The absolute maximum allowed is 50,000 /// according to the specification. See http://www.sitemaps.org/protocol.html but the file size must also be - /// less than 10MB. After some experimentation, a maximum of 2.000 nodes keeps the file size below 10MB. + /// less than 10MB. After some experimentation, a maximum of 1.000 nodes keeps the file size below 10MB. /// - private const int MaximumSiteMapNodeCount = 2000; + private const int MaximumSiteMapNodeCount = 1000; /// /// The maximum size of a sitemap file in bytes (10MB). /// private const int MaximumSiteMapSizeInBytes = 10485760; - private readonly ICategoryService _categoryService; + private readonly ICategoryService _categoryService; private readonly IProductService _productService; private readonly IManufacturerService _manufacturerService; private readonly ITopicService _topicService; private readonly ILanguageService _languageService; private readonly ICustomerService _customerService; private readonly ICatalogSearchService _catalogSearchService; - private readonly IUrlRecordService _urlRecordService; + private readonly SeoSettings _seoSettings; + private readonly SecuritySettings _securitySettings; private readonly ICommonServices _services; - private readonly ILockFileManager _lockFileManager; private readonly UrlHelper _urlHelper; - private readonly IVirtualFolder _tenantFolder; - private readonly string _baseDir; - public XmlSitemapGenerator( ICategoryService categoryService, IProductService productService, @@ -76,9 +72,9 @@ public XmlSitemapGenerator( ILanguageService languageService, ICustomerService customerService, ICatalogSearchService catalogSearchService, - IUrlRecordService urlRecordService, + SeoSettings commonSettings, + SecuritySettings securitySettings, ICommonServices services, - ILockFileManager lockFileManager, UrlHelper urlHelper) { _categoryService = categoryService; @@ -88,486 +84,139 @@ public XmlSitemapGenerator( _languageService = languageService; _customerService = customerService; _catalogSearchService = catalogSearchService; - _urlRecordService = urlRecordService; + _seoSettings = commonSettings; + _securitySettings = securitySettings; _services = services; - _lockFileManager = lockFileManager; _urlHelper = urlHelper; - _tenantFolder = _services.ApplicationEnvironment.TenantFolder; - _baseDir = _tenantFolder.Combine("Sitemaps"); - Logger = NullLogger.Instance; - - } + } - public ILogger Logger { get; set; } - - public virtual XmlSitemapPartition GetSitemapPart(int index = 0) + public ILogger Logger { - return GetSitemapPart(index, false); + get; + set; } - private XmlSitemapPartition GetSitemapPart(int index, bool isRetry) + public void Invalidate() { - Guard.NotNegative(index, nameof(index)); - - var store = _services.StoreContext.CurrentStore; - var language = _services.WorkContext.WorkingLanguage; - - var exists = SitemapFileExists(store.Id, language.Id, index, out var path, out var name); - - if (exists) - { - return new XmlSitemapPartition - { - Index = index, - Name = name, - LanguageId = language.Id, - StoreId = store.Id, - ModifiedOnUtc = _tenantFolder.GetFileLastWriteTimeUtc(path), - Stream = _tenantFolder.OpenFile(path) - }; - } - - if (isRetry) - { - var msg = "Could not generate XML sitemap. Index: {0}, Date: {1}".FormatInvariant(index, DateTime.UtcNow); - Logger.Error(msg); - throw new SmartException(msg); - } - - if (index > 0) - { - // File with index greater 0 has been requested, but it does not exist. - // Now we have to determine whether just the passed index is out of range - // or the files has never been created before. - // If the main file (index 0) exists, the action should return NotFoundResult, - // otherwise the rebuild process should be started or waited for. - - if (SitemapFileExists(store.Id, language.Id, 0, out path, out name)) - { - throw new IndexOutOfRangeException("The sitemap file '{0}' does not exist.".FormatInvariant(name)); - } - } - - // The main sitemap document with index 0 does not exist, meaning: the whole sitemap - // needs to be created and cached by partitions. - - var wasRebuilding = false; - var lockFilePath = GetLockFilePath(store.Id, language.Id); - - while (IsRebuilding(lockFilePath)) - { - // The rebuild process is already running, either started - // by the task scheduler or another HTTP request. - // We should wait for completion. - - wasRebuilding = true; - Thread.Sleep(1000); - } - - if (!wasRebuilding) - { - // No lock. Rebuild now. - var buildContext = new XmlSitemapBuildContext(store, new[] { language }) - { - CancellationToken = CancellationToken.None - }; - - Rebuild(buildContext); - } - - // DRY: call self to get sitemap partition object - return GetSitemapPart(index, true); + _services.Cache.RemoveByPattern(XMLSITEMAP_PATTERN_KEY); } - private bool SitemapFileExists(int storeId, int languageId, int index, out string path, out string name) + public bool IsGenerated { - path = BuildSitemapFilePath(storeId, languageId, index, out name); - - // Does not work reliably with symlinks due to framework caching - //var exists = _tenantFolder.FileExists(path); - - var exists = File.Exists(_tenantFolder.MapPath(path)); - - if (!exists) + get { - path = null; - name = null; - } - - return exists; - } - - private string BuildSitemapFilePath(int storeId, int languageId, int index, out string fileName) - { - fileName = SiteMapFileNamePattern.FormatInvariant(index); - return _tenantFolder.Combine(BuildSitemapDirPath(storeId, languageId), fileName); - } - - private string BuildSitemapDirPath(int storeId, int languageId) - { - return _tenantFolder.Combine(_baseDir, storeId + "/" + languageId); - } + string cacheKey = XMLSITEMAP_DOCUMENT_KEY.FormatInvariant(0, + _services.StoreContext.CurrentStore.Id, + _services.WorkContext.WorkingLanguage.Id); - private string GetLockFilePath(int storeId, int languageId) - { - var fileName = LockFileNamePattern.FormatInvariant(storeId, languageId); - return _tenantFolder.Combine(_baseDir, fileName); + return _services.Cache.Contains(cacheKey); + } } - public virtual void Rebuild(XmlSitemapBuildContext ctx) + public string GetSitemap(int? index = null) { - Guard.NotNull(ctx, nameof(ctx)); - - var languageData = new Dictionary(); - - foreach (var language in ctx.Languages) - { - var lockFilePath = GetLockFilePath(ctx.Store.Id, language.Id); - - if (_lockFileManager.TryAcquireLock(lockFilePath, out var lockFile)) - { - // Process only languages that are unlocked right now - // It is possible that an HTTP request triggered the generation - // of a language specific sitemap. - - var sitemapDir = BuildSitemapDirPath(ctx.Store.Id, language.Id); - var data = new LanguageData - { - Store = ctx.Store, - Language = language, - LockFile = lockFile, - LockFilePath = lockFilePath, - TempDir = sitemapDir + "~", - FinalDir = sitemapDir, - BaseUrl = BuildBaseUrl(ctx.Store, language) - }; - - _tenantFolder.TryDeleteDirectory(data.TempDir); - _tenantFolder.CreateDirectory(data.TempDir); - - languageData[language.Id] = data; - } - } - - if (languageData.Count == 0) - { - Logger.Warn("XML sitemap rebuild already in process."); - return; - } - - var languages = languageData.Values.Select(x => x.Language); - var languageIds = languages.Select(x => x.Id).Concat(new[] { 0 }).ToArray(); + var storeId = _services.StoreContext.CurrentStore.Id; + var langId = _services.WorkContext.WorkingLanguage.Id; + var cache = _services.Cache; - // All sitemaps grouped by language - var sitemaps = new Multimap(); + string cacheKey = XMLSITEMAP_DOCUMENT_KEY.FormatInvariant(0, storeId, langId); - var compositeFileLock = new ActionDisposable(() => + if (!cache.Contains(cacheKey)) { - foreach (var data in languageData.Values) + // The main sitemap document with index 0 does not exist, meaning: the whole sitemap + // needs to be created and cached by partitions. + lock (String.Intern(cacheKey)) { - data.LockFile.Release(); - } - }); - - using (compositeFileLock) - { - // Impersonate - var prevCustomer = _services.WorkContext.CurrentCustomer; - // no need to vary xml sitemap by customer roles: it's relevant to crawlers only. - _services.WorkContext.CurrentCustomer = _customerService.GetCustomerBySystemName(SystemCustomerNames.SearchEngine); - - try - { - var nodes = new List(); - - var queries = CreateQueries(ctx); - var total = queries.GetTotalRecordCount(); + var prevCustomer = _services.WorkContext.CurrentCustomer; + var bot = _customerService.GetCustomerBySystemName(SystemCustomerNames.SearchEngine); - var totalSegments = (int)Math.Ceiling(total / (double)MaximumSiteMapNodeCount); - var hasIndex = totalSegments > 1; - var indexNodes = new Multimap(); - var segment = 0; - var numProcessed = 0; - - CheckSitemapCount(totalSegments); - - using (new DbContextScope(autoDetectChanges: false, forceNoTracking: true, proxyCreation: false, lazyLoading: false)) + try { - var entities = EnumerateEntities(queries); + // no need to vary xml sitemap by customer roles: it's relevant to crawlers only. + _services.WorkContext.CurrentCustomer = bot; - foreach (var batch in entities.Slice(MaximumSiteMapNodeCount)) - { - if (ctx.CancellationToken.IsCancellationRequested) - { - break; - } - - segment++; - numProcessed = segment * MaximumSiteMapNodeCount; - ctx.ProgressCallback?.Invoke(numProcessed, total, "{0} / {1}".FormatCurrent(numProcessed, total)); - - var firstEntityName = batch.First().EntityName; - var lastEntityName = batch.Last().EntityName; - - var slugs = GetUrlRecordCollectionsForBatch(batch, languageIds); - - foreach (var data in languageData.Values) - { - var language = data.Language; - var baseUrl = data.BaseUrl; - - // Create all node entries for this segment - sitemaps[language.Id].AddRange(batch.Select(x => new XmlSitemapNode - { - LastMod = x.LastMod, - Loc = BuildNodeUrl(baseUrl, x, slugs[x.EntityName], language) - })); - - // Create index node for this segment/language combination - if (hasIndex) - { - indexNodes[language.Id].Add(new XmlSitemapNode - { - LastMod = sitemaps[language.Id].Select(x => x.LastMod).Where(x => x.HasValue).DefaultIfEmpty().Max(), - Loc = GetSitemapIndexUrl(segment, baseUrl), - }); - } - - if (segment % 5 == 0 || segment == totalSegments) - { - // Commit every 5th segment (10.000 nodes) temporarily to disk to minimize RAM usage - var documents = GetSiteMapDocuments((IReadOnlyCollection)sitemaps[language.Id]); - SaveTemp(documents, data, segment - documents.Count + (hasIndex ? 1 : 0)); - - documents.Clear(); - sitemaps.RemoveAll(language.Id); - } - } - - slugs.Clear(); - - GC.Collect(); - GC.WaitForPendingFinalizers(); - } + // we need a scoped lock, because we're going to split the cache entries. + var documents = Generate(); - // Process custom nodes - if (!ctx.CancellationToken.IsCancellationRequested) + for (int i = 0; i < documents.Count; i++) { - ctx.ProgressCallback?.Invoke(numProcessed, total, "Processing custom nodes".FormatCurrent(numProcessed, total)); - ProcessCustomNodes(ctx, sitemaps); - - foreach (var data in languageData.Values) - { - if (sitemaps.ContainsKey(data.Language.Id) && sitemaps[data.Language.Id].Count > 0) - { - var documents = GetSiteMapDocuments((IReadOnlyCollection)sitemaps[data.Language.Id]); - SaveTemp(documents, data, (segment + 1) - documents.Count + (hasIndex ? 1 : 0)); - } - else if (segment == 0) - { - // Ensure that at least one entry exists. Otherwise, - // the system will try to rebuild again. - var homeNode = new XmlSitemapNode { LastMod = DateTime.UtcNow, Loc = data.BaseUrl }; - var documents = GetSiteMapDocuments(new List { homeNode }); - SaveTemp(documents, data, 0); - } - - } + // Put segment into cache + cacheKey = XMLSITEMAP_DOCUMENT_KEY.FormatInvariant(i, storeId, langId); + cache.Put(cacheKey, documents[i], TimeSpan.FromDays(1)); } } - - ctx.CancellationToken.ThrowIfCancellationRequested(); - - ctx.ProgressCallback?.Invoke(totalSegments, totalSegments, "Finalizing...'"); - - foreach (var data in languageData.Values) + finally { - // Create index documents (if any) - if (hasIndex && indexNodes.Any()) - { - var indexDocument = CreateSitemapIndexDocument(indexNodes[data.Language.Id]); - SaveTemp(new List { indexDocument }, data, 0); - } - - // Save finally (actually renames temp folder) - SaveFinal(data); - } - } - finally - { - // Undo impersonation - _services.WorkContext.CurrentCustomer = prevCustomer; - sitemaps.Clear(); - - foreach (var data in languageData.Values) - { - if (_tenantFolder.DirectoryExists(data.TempDir)) - { - _tenantFolder.TryDeleteDirectory(data.TempDir); - } + // Undo impersonation + _services.WorkContext.CurrentCustomer = prevCustomer; } - - GC.Collect(); - GC.WaitForPendingFinalizers(); } } - } - - private string BuildBaseUrl(Store store, Language language) - { - var host = _services.StoreService.GetHost(store).EnsureEndsWith("/"); - var locSettings = _services.Settings.LoadSetting(store.Id); - if (locSettings.SeoFriendlyUrlsForLanguagesEnabled) + var page = index ?? 0; + cacheKey = XMLSITEMAP_DOCUMENT_KEY.FormatInvariant(page, storeId, langId); + + if (cache.Contains(cacheKey)) { - var defaultLangId = _languageService.GetDefaultLanguageId(store.Id); - if (language.Id != defaultLangId || locSettings.DefaultLanguageRedirectBehaviour < DefaultLanguageRedirectBehaviour.StripSeoCode) - { - host += language.GetTwoLetterISOLanguageName() + "/"; - } + return cache.Get(cacheKey); } - return host; + return null; } - private string BuildNodeUrl(string baseUrl, NamedEntity entity, UrlRecordCollection slugs, Language language) - { - return baseUrl + slugs.GetSlug(language.Id, entity.Id, true); - } - - private void SaveTemp(List documents, LanguageData data, int start) - { - for (int i = 0; i < documents.Count; i++) - { - // Save segment to disk - var fileName = SiteMapFileNamePattern.FormatInvariant(i + start); - var filePath = _tenantFolder.Combine(data.TempDir, fileName); - - _tenantFolder.CreateTextFile(filePath, documents[i]); - } - } - - private void SaveFinal(LanguageData data) + /// + /// Gets the collection of XML sitemap documents for the current site. If there are less than 1.000 sitemap + /// nodes, only one sitemap document will exist in the collection, otherwise a sitemap index document will be + /// the first entry in the collection and all other entries will be sitemap XML documents. + /// + /// A collection of XML sitemap documents. + protected IList Generate() { - // Delete current sitemap dir - _tenantFolder.TryDeleteDirectory(data.FinalDir); - - var source = _tenantFolder.MapPath(data.TempDir); - var dest = _tenantFolder.MapPath(data.FinalDir); + var protocol = _services.StoreContext.CurrentStore.ForceSslForAllPages ? "https" : "http"; - // Move/Rename new (temp) dir to current - System.IO.Directory.Move(source, dest); + var nodes = new List(); - int retries = 0; - while (!SitemapFileExists(data.Store.Id, data.Language.Id, 0, out _, out _)) + using (var scope = new DbContextScope(autoDetectChanges: false, forceNoTracking: true, proxyCreation: false, lazyLoading: false)) { - if (retries > 20) - { - break; - } - - // IO breathe: directly after a folder rename a file check fails. Wait a sec... - Task.Delay(500).Wait(); - retries++; - } - } - - private IEnumerable EnumerateEntities(QueryHolder queries) - { - var entities = Enumerable.Empty(); - if (queries.Categories != null) - { - var categories = queries.Categories.Select(x => new { x.Id, x.UpdatedOnUtc }).ToList(); - foreach (var x in categories) + if (_seoSettings.XmlSitemapIncludesCategories) { - yield return new NamedEntity { EntityName = "Category", Id = x.Id, LastMod = x.UpdatedOnUtc }; + nodes.AddRange(GetCategoryNodes(0, protocol)); } - } - if (queries.Manufacturers != null) - { - var manufacturers = queries.Manufacturers.Select(x => new { x.Id, x.UpdatedOnUtc }).ToList(); - foreach (var x in manufacturers) + if (_seoSettings.XmlSitemapIncludesManufacturers) { - yield return new NamedEntity { EntityName = "Manufacturer", Id = x.Id, LastMod = x.UpdatedOnUtc }; + nodes.AddRange(GetManufacturerNodes(protocol)); } - } - if (queries.Topics != null) - { - var topics = queries.Topics.Select(x => new { x.Id }).ToList(); - foreach (var x in topics) + if (_seoSettings.XmlSitemapIncludesTopics) { - yield return new NamedEntity { EntityName = "Topic", Id = x.Id, LastMod = DateTime.UtcNow }; + nodes.AddRange(GetTopicNodes(protocol)); } - } - - if (queries.Products != null) - { - var query = queries.Products.AsNoTracking(); - var maxId = int.MaxValue; - //var limit = 0; - while (maxId > 1) + if (_seoSettings.XmlSitemapIncludesProducts) { - var products = query - .Where(x => x.Id < maxId) - .OrderByDescending(x => x.Id) - .Take(() => MaximumSiteMapNodeCount) - .Select(x => new { x.Id, x.UpdatedOnUtc }) - .ToList(); - - //limit++; - //if (limit >= 100) - //{ - // break; - //} - - if (products.Count == 0) - { - break; - } - - maxId = products.Last().Id; - - foreach (var x in products) - { - yield return new NamedEntity { EntityName = "Product", Id = x.Id, LastMod = x.UpdatedOnUtc }; - } + nodes.AddRange(GetProductNodes(protocol)); } } - } - private IDictionary GetUrlRecordCollectionsForBatch(IEnumerable batch, int[] languageIds) - { - var result = new Dictionary(); - - if (batch.First().EntityName == "Product") + var customNodes = GetCustomNodes(protocol); + if (customNodes != null) { - // nothing comes after product - int min = batch.Last().Id; - int max = batch.First().Id; - - result["Product"] = _urlRecordService.GetUrlRecordCollection("Product", languageIds, new[] { min, max }, true, true); + nodes.AddRange(customNodes); } - var entityGroups = batch.ToMultimap(x => x.EntityName, x => x.Id); - foreach (var group in entityGroups) - { - var isRange = group.Key == "Product"; - var entityIds = isRange ? new[] { group.Value.Last(), group.Value.First() } : group.Value.ToArray(); - - result[group.Key] = _urlRecordService.GetUrlRecordCollection(group.Key, languageIds, entityIds, isRange, isRange); - } + var documents = GetSiteMapDocuments(nodes.AsReadOnly()); - return result; + return documents; } protected virtual List GetSiteMapDocuments(IReadOnlyCollection nodes) { + var protocol = _services.StoreContext.CurrentStore.ForceSslForAllPages ? "https" : "http"; + int siteMapCount = (int)Math.Ceiling(nodes.Count / (double)MaximumSiteMapNodeCount); CheckSitemapCount(siteMapCount); @@ -582,14 +231,66 @@ protected virtual List GetSiteMapDocuments(IReadOnlyCollection(siteMapCount); + if (siteMapCount > 1) + { + var xml = this.GetSitemapIndexDocument(siteMaps, protocol); + siteMapDocuments.Add(xml); + } + foreach (var kvp in siteMaps) { - siteMapDocuments.Add(this.GetSitemapDocument(kvp.Value)); + var xml = this.GetSitemapDocument(kvp.Value); + siteMapDocuments.Add(xml); } return siteMapDocuments; } + /// + /// Gets the sitemap index XML document, containing links to all the sitemap XML documents. + /// + /// The collection of sitemaps containing their index and nodes. + /// The sitemap index XML document, containing links to all the sitemap XML documents. + private string GetSitemapIndexDocument(IEnumerable>> siteMaps, string protocol) + { + XNamespace ns = SiteMapsNamespace; + + XElement root = new XElement(ns + "sitemapindex"); + + foreach (KeyValuePair> map in siteMaps) + { + // Get the latest LastModified DateTime from the sitemap nodes or null if there is none. + DateTime? lastModified = map.Value + .Select(x => x.LastMod) + .Where(x => x.HasValue) + .DefaultIfEmpty() + .Max(); + + var xel = new XElement( + ns + "sitemap", + new XElement(ns + "loc", this.GetSitemapUrl(map.Key, protocol)), + lastModified.HasValue ? + new XElement( + ns + "lastmod", + lastModified.Value.ToLocalTime().ToString("yyyy-MM-ddTHH:mm:sszzz")) : + null); + + root.Add(xel); + } + + var document = new XDocument(root); + var xml = document.ToString(SaveOptions.DisableFormatting); + CheckDocumentSize(xml); + + return xml; + } + + private string GetSitemapUrl(int index, string protocol) + { + var url = _urlHelper.RouteUrl("SitemapSEO", new { index = index }, protocol); + return url; + } + /// /// Gets the sitemap XML document for the specified set of nodes. /// @@ -598,7 +299,7 @@ protected virtual List GetSiteMapDocuments(IReadOnlyCollection nodes) { //var languages = _languageService.GetAllLanguages(); - + XNamespace ns = SiteMapsNamespace; XNamespace xhtml = XhtmlNamespace; @@ -653,91 +354,117 @@ private string GetSitemapDocument(IEnumerable nodes) return xml; } - /// - /// Gets the sitemap index XML document, containing links to all the sitemap XML documents. - /// - /// The collection of sitemaps containing their index and nodes. - /// The sitemap index XML document, containing links to all the sitemap XML documents. - private string CreateSitemapIndexDocument(IEnumerable nodes) + protected virtual IEnumerable GetCategoryNodes(int parentCategoryId, string protocol) { - XNamespace ns = SiteMapsNamespace; + var categories = _categoryService.GetAllCategories(showHidden: false, storeId: _services.StoreContext.CurrentStore.Id); - XElement root = new XElement(ns + "sitemapindex"); + _services.DbContext.DetachAll(); - foreach (var node in nodes) + return categories.Select(x => { - var xel = new XElement( - ns + "sitemap", - new XElement(ns + "loc", node.Loc), - node.LastMod.HasValue ? - new XElement( - ns + "lastmod", - node.LastMod.Value.ToLocalTime().ToString("yyyy-MM-ddTHH:mm:sszzz")) : - null); - - root.Add(xel); - } + var node = new XmlSitemapNode + { + Loc = _urlHelper.RouteUrl("Category", new { SeName = x.GetSeName() }, protocol), + LastMod = x.UpdatedOnUtc, + //ChangeFreq = ChangeFrequency.Weekly, + //Priority = 0.8f + }; - var document = new XDocument(root); - var xml = document.ToString(SaveOptions.DisableFormatting); - CheckDocumentSize(xml); + // TODO: add hreflang links if LangCount is > 1 and PrependSeoCode is true - return xml; + return node; + }); } - private string GetSitemapIndexUrl(int index, string baseUrl) + protected virtual IEnumerable GetManufacturerNodes(string protocol) { - var url = _urlHelper.RouteUrl("XmlSitemap", new { index }).TrimStart('/'); - return baseUrl + url; - } + var manufacturers = _manufacturerService.GetAllManufacturers(false); - private QueryHolder CreateQueries(XmlSitemapBuildContext ctx) - { - var storeId = ctx.Store.Id; + _services.DbContext.DetachAll(); - if (_services.StoreService.IsSingleStoreMode()) + return manufacturers.Select(x => { - storeId = 0; - } + var node = new XmlSitemapNode + { + Loc = _urlHelper.RouteUrl("Manufacturer", new { SeName = x.GetSeName() }, protocol), + LastMod = x.UpdatedOnUtc, + //ChangeFreq = ChangeFrequency.Weekly, + //Priority = 0.8f + }; - // Always work with store-dependant setting - var seoSettings = _services.Settings.LoadSetting(storeId); + // TODO: add hreflang links if LangCount is > 1 and PrependSeoCode is true - var holder = new QueryHolder(); + return node; + }); + } - if (seoSettings.XmlSitemapIncludesCategories) + protected virtual IEnumerable GetTopicNodes(string protocol) + { + var topics = _topicService.GetAllTopics(_services.StoreContext.CurrentStore.Id).AlterQuery(q => { - holder.Categories = _categoryService.GetAllCategories(showHidden: false, storeId: storeId).SourceQuery; - } + return q.Where(t => t.IncludeInSitemap && !t.RenderAsWidget); + }); - if (seoSettings.XmlSitemapIncludesManufacturers) - { - holder.Manufacturers = _manufacturerService.GetManufacturers(false).OrderBy(x => x.DisplayOrder).ThenBy(x => x.Name); - } + _services.DbContext.DetachAll(); - if (seoSettings.XmlSitemapIncludesTopics) + return topics.Select(x => { - holder.Topics = _topicService.GetAllTopics(storeId).AlterQuery(q => + var node = new XmlSitemapNode { - return q.Where(t => t.IncludeInSitemap && !t.RenderAsWidget); - }).SourceQuery; - } + Loc = _urlHelper.RouteUrl("Topic", new { SeName = x.GetSeName() }, protocol), + LastMod = DateTime.UtcNow, + //ChangeFreq = ChangeFrequency.Weekly, + //Priority = 0.8f + }; + + // TODO: add hreflang links if LangCount is > 1 and PrependSeoCode is true + + return node; + }); + } + + protected virtual IEnumerable GetProductNodes(string protocol) + { + var nodes = new List(); + + var searchQuery = new CatalogSearchQuery() + .VisibleOnly() + .VisibleIndividuallyOnly(true) + .HasStoreId(_services.StoreContext.CurrentStoreIdIfMultiStoreMode); - if (seoSettings.XmlSitemapIncludesProducts) + var query = _catalogSearchService.PrepareQuery(searchQuery); + query = query.OrderByDescending(x => x.CreatedOnUtc); + + for (var pageIndex = 0; pageIndex < 9999999; ++pageIndex) { - var searchQuery = new CatalogSearchQuery() - .VisibleOnly() - .VisibleIndividuallyOnly(true) - .HasStoreId(storeId); + var products = new PagedList(query, pageIndex, 1000); + + nodes.AddRange(products.Select(x => + { + var node = new XmlSitemapNode + { + Loc = _urlHelper.RouteUrl("Product", new { SeName = x.GetSeName() }, protocol), + LastMod = x.UpdatedOnUtc, + //ChangeFreq = ChangeFrequency.Weekly, + //Priority = 0.8f + }; + + // TODO: add hreflang links if LangCount is > 1 and PrependSeoCode is true + return node; + })); - holder.Products = _catalogSearchService.PrepareQuery(searchQuery); + _services.DbContext.DetachAll(); + + if (!products.HasNextPage) + break; } - return holder; + return nodes; } - protected void ProcessCustomNodes(XmlSitemapBuildContext ctx, Multimap sitemaps) + protected virtual IEnumerable GetCustomNodes(string protocol) { + return Enumerable.Empty(); } /// @@ -764,76 +491,5 @@ private void CheckSitemapCount(int sitemapCount) Logger.Warn(ex, ex.Message); } } - - public bool IsRebuilding(int storeId, int languageId) - { - return IsRebuilding(GetLockFilePath(storeId, languageId)); - } - - private bool IsRebuilding(string lockFilePath) - { - return _lockFileManager.IsLocked(lockFilePath); - } - - public virtual bool IsGenerated(int storeId, int languageId) - { - return SitemapFileExists(storeId, languageId, 0, out _, out _); - } - - public virtual void Invalidate(int storeId, int languageId) - { - var dir = BuildSitemapDirPath(storeId, languageId); - - if (_tenantFolder.DirectoryExists(dir)) - { - _tenantFolder.DeleteDirectory(dir); - } - } - - #region Nested classes - - class LanguageData - { - public Store Store { get; set; } - public Language Language { get; set; } - public ILockFile LockFile { get; set; } - public string LockFilePath { get; set; } - public string TempDir { get; set; } - public string FinalDir { get; set; } - public string BaseUrl { get; set; } - } - - class QueryHolder - { - public IQueryable Categories { get; set; } - public IQueryable Manufacturers { get; set; } - public IQueryable Topics { get; set; } - public IQueryable Products { get; set; } - - public int GetTotalRecordCount() - { - int num = 0; - if (Categories != null) num += Categories.Count(); - if (Manufacturers != null) num += Manufacturers.Count(); - if (Topics != null) num += Topics.Count(); - if (Products != null) num += Products.Count(); - //if (Products != null) num += 200000; - - return num; - } - } - - class NamedEntity : BaseEntity, ISlugSupported - { - public string EntityName { get; set; } - public DateTime LastMod { get; set; } - - public override string GetEntityName() - { - return EntityName; - } - } - - #endregion - } + } } diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index 28cc59a3ab..52ebebc4b5 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -112,13 +112,18 @@ ..\..\packages\Microsoft.Web.Xdt.2.1.1\lib\net40\Microsoft.Web.XmlTransform.dll + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + ..\..\packages\ncrontab.3.3.0\lib\net35\NCrontab.dll True - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - True + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll ..\..\packages\NReco.PdfGenerator.1.1.15\lib\net20\NReco.PdfGenerator.dll @@ -582,8 +587,6 @@ - - diff --git a/src/Libraries/SmartStore.Services/Stores/IStoreMappingService.cs b/src/Libraries/SmartStore.Services/Stores/IStoreMappingService.cs index 02e9f61346..7eed774f70 100644 --- a/src/Libraries/SmartStore.Services/Stores/IStoreMappingService.cs +++ b/src/Libraries/SmartStore.Services/Stores/IStoreMappingService.cs @@ -107,7 +107,7 @@ public static int[] GetStoresIdsWithAccess(this IStoreMappingService svc, T e if (entity == null) return new int[0]; - return svc.GetStoresIdsWithAccess(entity.GetEntityName(), entity.Id); + return svc.GetStoresIdsWithAccess(typeof(T).Name, entity.Id); } /// @@ -124,7 +124,7 @@ public static bool Authorize(this IStoreMappingService svc, T entity) where T if (!entity.LimitedToStores) return true; - return svc.Authorize(entity.GetEntityName(), entity.Id); + return svc.Authorize(typeof(T).Name, entity.Id); } /// @@ -142,7 +142,7 @@ public static bool Authorize(this IStoreMappingService svc, T entity, int sto if (!entity.LimitedToStores) return true; - return svc.Authorize(entity.GetEntityName(), entity.Id, storeId); + return svc.Authorize(typeof(T).Name, entity.Id, storeId); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Stores/StoreMappingService.cs b/src/Libraries/SmartStore.Services/Stores/StoreMappingService.cs index d5578486ca..04bf8acfb0 100644 --- a/src/Libraries/SmartStore.Services/Stores/StoreMappingService.cs +++ b/src/Libraries/SmartStore.Services/Stores/StoreMappingService.cs @@ -60,7 +60,7 @@ public virtual IList GetStoreMappings(T entity) where T : BaseE Guard.NotNull(entity, nameof(entity)); int entityId = entity.Id; - string entityName = entity.GetEntityName(); + string entityName = typeof(T).Name; var query = from sm in _storeMappingRepository.Table where sm.EntityId == entityId && @@ -121,7 +121,7 @@ public virtual void InsertStoreMapping(T entity, int storeId) where T : BaseE throw new ArgumentOutOfRangeException(nameof(storeId)); int entityId = entity.Id; - string entityName = entity.GetEntityName(); + string entityName = typeof(T).Name; var storeMapping = new StoreMapping { diff --git a/src/Libraries/SmartStore.Services/Tasks/ScheduleTaskService.cs b/src/Libraries/SmartStore.Services/Tasks/ScheduleTaskService.cs index 7d5b7373a7..e405f0191a 100644 --- a/src/Libraries/SmartStore.Services/Tasks/ScheduleTaskService.cs +++ b/src/Libraries/SmartStore.Services/Tasks/ScheduleTaskService.cs @@ -479,23 +479,23 @@ public virtual int DeleteHistoryEntries() idsToDelete.AddRange(ids); } - // We have to group by task otherwise we would only keep entries from very frequently executed tasks. - if (_commonSettings.Value.MaxNumberOfScheduleHistoryEntries > 0) - { - var query = - from th in _taskHistoryRepository.TableUntracked - where !th.IsRunning - group th by th.ScheduleTaskId into grp - select grp - .OrderByDescending(x => x.StartedOnUtc) - .ThenByDescending(x => x.Id) - .Skip(_commonSettings.Value.MaxNumberOfScheduleHistoryEntries) - .Select(x => x.Id); - - var ids = query.SelectMany(x => x).ToList(); - idsToDelete.AddRange(ids); - } + // TOFIX: DeleteHistoryEntries - query rerturns null + // We have to group by task otherwise we would only keep entries from very frequently executed tasks. + //if (_commonSettings.Value.MaxNumberOfScheduleHistoryEntries > 0) + //{ + // var query = + // from th in _taskHistoryRepository.TableUntracked + // where !th.IsRunning + // group th by th.ScheduleTaskId into grp + // select grp + // .OrderByDescending(x => x.StartedOnUtc) + // .ThenByDescending(x => x.Id) + // .Skip(_commonSettings.Value.MaxNumberOfScheduleHistoryEntries) + // .Select(x => x.Id); + // var ids = query.SelectMany(x => x)?.ToList(); + // idsToDelete.AddRange(ids); + //} try { diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs b/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs index 466e1db24f..9313933498 100644 --- a/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs +++ b/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs @@ -7,13 +7,10 @@ namespace SmartStore.Services.Tasks { - public delegate void ProgressCallback(int value, int maximum, string message); - - - /// - /// Provides the context for the Execute method of the interface. - /// - public class TaskExecutionContext + /// + /// Provides the context for the Execute method of the interface. + /// + public class TaskExecutionContext { private readonly IComponentContext _componentContext; private readonly ScheduleTaskHistory _originalTaskHistory; diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskExecutor.cs b/src/Libraries/SmartStore.Services/Tasks/TaskExecutor.cs index 361141e2c8..6190bf0e94 100644 --- a/src/Libraries/SmartStore.Services/Tasks/TaskExecutor.cs +++ b/src/Libraries/SmartStore.Services/Tasks/TaskExecutor.cs @@ -128,7 +128,7 @@ public void Execute( exception = ex; faulted = true; canceled = ex is OperationCanceledException; - lastError = ex.ToAllMessages(true); + lastError = ex.Message.Truncate(995, "..."); if (canceled) { diff --git a/src/Libraries/SmartStore.Services/app.config b/src/Libraries/SmartStore.Services/app.config index 48f67fefef..0b87140575 100644 --- a/src/Libraries/SmartStore.Services/app.config +++ b/src/Libraries/SmartStore.Services/app.config @@ -41,6 +41,11 @@ - + - + + + + + + diff --git a/src/Libraries/SmartStore.Services/packages.config b/src/Libraries/SmartStore.Services/packages.config index c369c9c9bc..767dc638b2 100644 --- a/src/Libraries/SmartStore.Services/packages.config +++ b/src/Libraries/SmartStore.Services/packages.config @@ -14,8 +14,10 @@ + + - + diff --git a/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj b/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj index 4f7473a224..8cb7d6c9f6 100644 --- a/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj +++ b/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj @@ -78,8 +78,8 @@ MinimumRecommendedRules.ruleset - - ..\..\packages\AmazonPay.3.3.1\lib\net20\AmazonPay.dll + + ..\..\packages\AmazonPay.3.4.3.2\lib\net461\AmazonPay.dll True @@ -92,12 +92,28 @@ ..\..\packages\Common.Logging.2.0.0\lib\2.0\Common.Logging.dll True + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + True + + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + True + ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - False + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + True + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + True + + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + True diff --git a/src/Plugins/SmartStore.AmazonPay/Views/AmazonPay/Configure.cshtml b/src/Plugins/SmartStore.AmazonPay/Views/AmazonPay/Configure.cshtml index 1a342b477f..213e0c1cf5 100644 --- a/src/Plugins/SmartStore.AmazonPay/Views/AmazonPay/Configure.cshtml +++ b/src/Plugins/SmartStore.AmazonPay/Views/AmazonPay/Configure.cshtml @@ -47,11 +47,11 @@ - + @T("Plugins.Payments.AmazonPay.RegisterNow") diff --git a/src/Plugins/SmartStore.AmazonPay/Views/AmazonPayCheckout/PaymentMethod.cshtml b/src/Plugins/SmartStore.AmazonPay/Views/AmazonPayCheckout/PaymentMethod.cshtml index 233d0251c7..a736fed4e8 100644 --- a/src/Plugins/SmartStore.AmazonPay/Views/AmazonPayCheckout/PaymentMethod.cshtml +++ b/src/Plugins/SmartStore.AmazonPay/Views/AmazonPayCheckout/PaymentMethod.cshtml @@ -57,7 +57,7 @@ try { new OffAmazonPayments.Widgets.Wallet({ sellerId: '@Model.SellerId', - scope: 'profile payments:widget payments:shipping_address payments:billing_address', + scope: '', amazonOrderReferenceId: '@Model.OrderReferenceId', design: { designMode: 'responsive' diff --git a/src/Plugins/SmartStore.AmazonPay/packages.config b/src/Plugins/SmartStore.AmazonPay/packages.config index d984137ea4..02cfbb03da 100644 --- a/src/Plugins/SmartStore.AmazonPay/packages.config +++ b/src/Plugins/SmartStore.AmazonPay/packages.config @@ -1,13 +1,16 @@  - + + - + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/web.config b/src/Plugins/SmartStore.AmazonPay/web.config index ae0230ca02..2e345ebcf3 100644 --- a/src/Plugins/SmartStore.AmazonPay/web.config +++ b/src/Plugins/SmartStore.AmazonPay/web.config @@ -1,5 +1,9 @@  + +
+ + @@ -89,7 +93,7 @@ - + @@ -149,4 +153,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.Clickatell/AdminMenu.cs b/src/Plugins/SmartStore.Clickatell/AdminMenu.cs index f336662023..0795b68f34 100644 --- a/src/Plugins/SmartStore.Clickatell/AdminMenu.cs +++ b/src/Plugins/SmartStore.Clickatell/AdminMenu.cs @@ -10,7 +10,7 @@ protected override void BuildMenuCore(TreeNode pluginsNode) var menuItem = new MenuItem().ToBuilder() .Text("Clickatell SMS Provider") .ResKey("Plugins.FriendlyName.SmartStore.Clickatell") - .Icon("far fa-paper-plane") + .Icon("paper-plane-o") .Action("ConfigurePlugin", "Plugin", new { systemName = "SmartStore.Clickatell", area = "Admin" }) .ToItem(); diff --git a/src/Plugins/SmartStore.Clickatell/SmartStore.Clickatell.csproj b/src/Plugins/SmartStore.Clickatell/SmartStore.Clickatell.csproj index 00f3bbd9a6..5bc2bae6ae 100644 --- a/src/Plugins/SmartStore.Clickatell/SmartStore.Clickatell.csproj +++ b/src/Plugins/SmartStore.Clickatell/SmartStore.Clickatell.csproj @@ -43,6 +43,7 @@ + true @@ -82,11 +83,28 @@ MinimumRecommendedRules.ruleset + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + True + + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + True + ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + True + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + True + + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + True diff --git a/src/Plugins/SmartStore.Clickatell/packages.config b/src/Plugins/SmartStore.Clickatell/packages.config index 322a3f4acf..527dd6536c 100644 --- a/src/Plugins/SmartStore.Clickatell/packages.config +++ b/src/Plugins/SmartStore.Clickatell/packages.config @@ -1,8 +1,11 @@  + - + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.Clickatell/web.config b/src/Plugins/SmartStore.Clickatell/web.config index a476d4989f..3c0be0ee83 100644 --- a/src/Plugins/SmartStore.Clickatell/web.config +++ b/src/Plugins/SmartStore.Clickatell/web.config @@ -1,6 +1,10 @@  + +
+ + @@ -86,7 +90,7 @@ - + @@ -134,4 +138,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/AdminMenu.cs b/src/Plugins/SmartStore.DevTools/AdminMenu.cs index 8110443480..8808b9731c 100644 --- a/src/Plugins/SmartStore.DevTools/AdminMenu.cs +++ b/src/Plugins/SmartStore.DevTools/AdminMenu.cs @@ -9,7 +9,7 @@ protected override void BuildMenuCore(TreeNode pluginsNode) { var menuItem = new MenuItem().ToBuilder() .Text("Developer Tools") - .Icon("far fa-terminal") + .Icon("terminal") .Action("ConfigurePlugin", "Plugin", new { systemName = "SmartStore.DevTools", area = "Admin" }) .ToItem(); @@ -18,7 +18,7 @@ protected override void BuildMenuCore(TreeNode pluginsNode) // uncomment to add to admin menu (see plugin sub-menu) //var backendExtensionItem = new MenuItem().ToBuilder() // .Text("Backend extension") - // .Icon("chart-area") + // .Icon("area-chart") // .Action("BackendExtension", "DevTools", new { area = "SmartStore.DevTools" }) // .ToItem(); //pluginsNode.Append(backendExtensionItem); diff --git a/src/Plugins/SmartStore.DevTools/SmartStore.DevTools.csproj b/src/Plugins/SmartStore.DevTools/SmartStore.DevTools.csproj index e08ca063bb..8121fc5075 100644 --- a/src/Plugins/SmartStore.DevTools/SmartStore.DevTools.csproj +++ b/src/Plugins/SmartStore.DevTools/SmartStore.DevTools.csproj @@ -100,6 +100,14 @@ ..\..\packages\MiniProfiler.EF6.3.0.11\lib\net40\MiniProfiler.EntityFramework6.dll True + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + True + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + True + diff --git a/src/Plugins/SmartStore.DevTools/Views/DevTools/BackendExtension.cshtml b/src/Plugins/SmartStore.DevTools/Views/DevTools/BackendExtension.cshtml index 0af50e879f..26a5c5311a 100644 --- a/src/Plugins/SmartStore.DevTools/Views/DevTools/BackendExtension.cshtml +++ b/src/Plugins/SmartStore.DevTools/Views/DevTools/BackendExtension.cshtml @@ -8,7 +8,7 @@
- + My SmartStore.NET backend extension page
diff --git a/src/Plugins/SmartStore.DevTools/Web.config b/src/Plugins/SmartStore.DevTools/Web.config index 2f0ab2777e..21a482124c 100644 --- a/src/Plugins/SmartStore.DevTools/Web.config +++ b/src/Plugins/SmartStore.DevTools/Web.config @@ -90,7 +90,7 @@ - + @@ -142,4 +142,13 @@ - \ No newline at end of file + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/packages.config b/src/Plugins/SmartStore.DevTools/packages.config index 78e5bfe16a..90563d7f0c 100644 --- a/src/Plugins/SmartStore.DevTools/packages.config +++ b/src/Plugins/SmartStore.DevTools/packages.config @@ -9,4 +9,6 @@ + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/SmartStore.DiscountRules.csproj b/src/Plugins/SmartStore.DiscountRules/SmartStore.DiscountRules.csproj index 06e2098cb6..764c586534 100644 --- a/src/Plugins/SmartStore.DiscountRules/SmartStore.DiscountRules.csproj +++ b/src/Plugins/SmartStore.DiscountRules/SmartStore.DiscountRules.csproj @@ -86,13 +86,31 @@ MinimumRecommendedRules.ruleset + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + True + + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + True + ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + True + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + True + + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + True + diff --git a/src/Plugins/SmartStore.DiscountRules/packages.config b/src/Plugins/SmartStore.DiscountRules/packages.config index 524c11aedc..eaec9ee5a9 100644 --- a/src/Plugins/SmartStore.DiscountRules/packages.config +++ b/src/Plugins/SmartStore.DiscountRules/packages.config @@ -1,9 +1,12 @@  + - + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/web.config b/src/Plugins/SmartStore.DiscountRules/web.config index a476d4989f..3c0be0ee83 100644 --- a/src/Plugins/SmartStore.DiscountRules/web.config +++ b/src/Plugins/SmartStore.DiscountRules/web.config @@ -1,6 +1,10 @@  + +
+ + @@ -86,7 +90,7 @@ - + @@ -134,4 +138,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.FacebookAuth/SmartStore.FacebookAuth.csproj b/src/Plugins/SmartStore.FacebookAuth/SmartStore.FacebookAuth.csproj index 41e2ae7d49..7f9ee1c717 100644 --- a/src/Plugins/SmartStore.FacebookAuth/SmartStore.FacebookAuth.csproj +++ b/src/Plugins/SmartStore.FacebookAuth/SmartStore.FacebookAuth.csproj @@ -121,13 +121,31 @@ ..\..\packages\DotNetOpenAuth.OpenId.RelyingParty.4.3.4.13329\lib\net45-full\DotNetOpenAuth.OpenId.RelyingParty.dll True + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + True + + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + True + ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + True + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + True + + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + True + diff --git a/src/Plugins/SmartStore.FacebookAuth/Views/ExternalAuthFacebook/PublicInfo.cshtml b/src/Plugins/SmartStore.FacebookAuth/Views/ExternalAuthFacebook/PublicInfo.cshtml index 2d894ad278..167d36243f 100644 --- a/src/Plugins/SmartStore.FacebookAuth/Views/ExternalAuthFacebook/PublicInfo.cshtml +++ b/src/Plugins/SmartStore.FacebookAuth/Views/ExternalAuthFacebook/PublicInfo.cshtml @@ -6,6 +6,6 @@ - + \ No newline at end of file diff --git a/src/Plugins/SmartStore.FacebookAuth/packages.config b/src/Plugins/SmartStore.FacebookAuth/packages.config index d771bf4501..7c6a0d837b 100644 --- a/src/Plugins/SmartStore.FacebookAuth/packages.config +++ b/src/Plugins/SmartStore.FacebookAuth/packages.config @@ -8,6 +8,7 @@ + @@ -15,5 +16,7 @@ - + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.FacebookAuth/web.config b/src/Plugins/SmartStore.FacebookAuth/web.config index beb5523ac3..d76240abb2 100644 --- a/src/Plugins/SmartStore.FacebookAuth/web.config +++ b/src/Plugins/SmartStore.FacebookAuth/web.config @@ -1,6 +1,11 @@  + + +
+ + @@ -90,7 +95,7 @@ - + @@ -142,4 +147,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleAnalytics/SmartStore.GoogleAnalytics.csproj b/src/Plugins/SmartStore.GoogleAnalytics/SmartStore.GoogleAnalytics.csproj index cb8fa2c279..7f37247e1b 100644 --- a/src/Plugins/SmartStore.GoogleAnalytics/SmartStore.GoogleAnalytics.csproj +++ b/src/Plugins/SmartStore.GoogleAnalytics/SmartStore.GoogleAnalytics.csproj @@ -43,6 +43,7 @@ + true @@ -82,10 +83,27 @@ MinimumRecommendedRules.ruleset + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + True + + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + True + ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + True + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + True + + diff --git a/src/Plugins/SmartStore.GoogleAnalytics/packages.config b/src/Plugins/SmartStore.GoogleAnalytics/packages.config index 4bc06fc1a7..ee597d8cea 100644 --- a/src/Plugins/SmartStore.GoogleAnalytics/packages.config +++ b/src/Plugins/SmartStore.GoogleAnalytics/packages.config @@ -1,7 +1,10 @@  + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleAnalytics/web.config b/src/Plugins/SmartStore.GoogleAnalytics/web.config index 02745f9ebf..323cd5f114 100644 --- a/src/Plugins/SmartStore.GoogleAnalytics/web.config +++ b/src/Plugins/SmartStore.GoogleAnalytics/web.config @@ -1,5 +1,9 @@  + +
+ + @@ -85,7 +89,7 @@ - + @@ -133,4 +137,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/AdminMenu.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/AdminMenu.cs index 6a62863d9a..9c8413dd67 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/AdminMenu.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/AdminMenu.cs @@ -9,7 +9,7 @@ protected override void BuildMenuCore(TreeNode pluginsNode) { var menuItem = new MenuItem().ToBuilder() .Text("Google Merchant Center") - .Icon("fab fa-google") + .Icon("google") .ResKey("Plugins.FriendlyName.SmartStore.GoogleMerchantCenter") .Action("ConfigurePlugin", "Plugin", new { systemName = GoogleMerchantCenterFeedPlugin.SystemName, area = "Admin" }) .ToItem(); diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/Configuration.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/Configuration.cs index 00c0104a40..17a4b5ad34 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/Configuration.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/Configuration.cs @@ -12,7 +12,9 @@ public Configuration() AutomaticMigrationsEnabled = false; MigrationsDirectory = @"Data\Migrations"; ContextKey = "SmartStore.FeedGoogle"; // DO NOT CHANGE! - } + + //SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); + } protected override void Seed(GoogleProductObjectContext context) { diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs index f129d4c009..62ec60a9a6 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs @@ -27,7 +27,7 @@ public void HandleEvent(TabStripCreated eventMessage) eventMessage.ItemFactory.Add().Text("GMC") .Name("tab-gmc") - .Icon("fab fa-google fa-lg fa-fw") + .Icon("fa fa-google fa-lg fa-fw") .LinkHtmlAttributes(new { data_tab_name = "GMC" }) .Route("SmartStore.GoogleMerchantCenter", new { action = "ProductEditTab", productId = productId }) .Ajax(); diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj b/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj index dcd34d683c..5684a691c2 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj @@ -105,8 +105,17 @@ ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + True + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + True + + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + True diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/packages.config b/src/Plugins/SmartStore.GoogleMerchantCenter/packages.config index b3f23c4249..5c24068d9c 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/packages.config +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/packages.config @@ -8,5 +8,7 @@ - + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/web.config b/src/Plugins/SmartStore.GoogleMerchantCenter/web.config index a476d4989f..5b14ca4f42 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/web.config +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/web.config @@ -86,7 +86,7 @@ - + @@ -134,4 +134,13 @@ - \ No newline at end of file + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.OfflinePayment/SmartStore.OfflinePayment.csproj b/src/Plugins/SmartStore.OfflinePayment/SmartStore.OfflinePayment.csproj index 24bf962f53..b29e891798 100644 --- a/src/Plugins/SmartStore.OfflinePayment/SmartStore.OfflinePayment.csproj +++ b/src/Plugins/SmartStore.OfflinePayment/SmartStore.OfflinePayment.csproj @@ -86,14 +86,31 @@ ..\..\packages\Autofac.4.5.0\lib\net45\Autofac.dll + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + True + + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + True + ..\..\packages\FluentValidation.7.4.0\lib\net45\FluentValidation.dll ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + True + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + True + + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + True diff --git a/src/Plugins/SmartStore.OfflinePayment/packages.config b/src/Plugins/SmartStore.OfflinePayment/packages.config index ee4c4aa5f3..7e090c44e1 100644 --- a/src/Plugins/SmartStore.OfflinePayment/packages.config +++ b/src/Plugins/SmartStore.OfflinePayment/packages.config @@ -1,10 +1,13 @@  + - + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.OfflinePayment/web.config b/src/Plugins/SmartStore.OfflinePayment/web.config index a476d4989f..3c0be0ee83 100644 --- a/src/Plugins/SmartStore.OfflinePayment/web.config +++ b/src/Plugins/SmartStore.OfflinePayment/web.config @@ -1,6 +1,10 @@  + +
+ + @@ -86,7 +90,7 @@ - + @@ -134,4 +138,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.PayPal/SmartStore.PayPal.csproj b/src/Plugins/SmartStore.PayPal/SmartStore.PayPal.csproj index 0e8654e92c..3bbf13f48e 100644 --- a/src/Plugins/SmartStore.PayPal/SmartStore.PayPal.csproj +++ b/src/Plugins/SmartStore.PayPal/SmartStore.PayPal.csproj @@ -66,14 +66,31 @@ ..\..\packages\Autofac.Mvc5.4.0.2\lib\net45\Autofac.Integration.Mvc.dll + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + True + + + ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + True + ..\..\packages\FluentValidation.7.4.0\lib\net45\FluentValidation.dll ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + True + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + True + + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + True diff --git a/src/Plugins/SmartStore.PayPal/Views/PayPalPlus/Configure.cshtml b/src/Plugins/SmartStore.PayPal/Views/PayPalPlus/Configure.cshtml index efce2e5bbc..9458875371 100644 --- a/src/Plugins/SmartStore.PayPal/Views/PayPalPlus/Configure.cshtml +++ b/src/Plugins/SmartStore.PayPal/Views/PayPalPlus/Configure.cshtml @@ -95,7 +95,7 @@ - + @T(Model.ExperienceProfileId.HasValue() ? "Common.Refresh" : "Common.AddNew") @@ -103,7 +103,7 @@ { - + @T("Admin.Common.Delete") } @@ -125,7 +125,7 @@ { - + @T("Admin.Common.Delete") } diff --git a/src/Plugins/SmartStore.PayPal/packages.config b/src/Plugins/SmartStore.PayPal/packages.config index 4b452fd305..1e0a2347d4 100644 --- a/src/Plugins/SmartStore.PayPal/packages.config +++ b/src/Plugins/SmartStore.PayPal/packages.config @@ -2,11 +2,14 @@ + - + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.PayPal/web.config b/src/Plugins/SmartStore.PayPal/web.config index 7ea242037d..a53b73b5b2 100644 --- a/src/Plugins/SmartStore.PayPal/web.config +++ b/src/Plugins/SmartStore.PayPal/web.config @@ -4,6 +4,8 @@
+
+ @@ -102,7 +104,7 @@ - + @@ -162,4 +164,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.Shipping/Data/Migrations/Configuration.cs b/src/Plugins/SmartStore.Shipping/Data/Migrations/Configuration.cs index 5a33487b47..67cb363fa7 100644 --- a/src/Plugins/SmartStore.Shipping/Data/Migrations/Configuration.cs +++ b/src/Plugins/SmartStore.Shipping/Data/Migrations/Configuration.cs @@ -9,9 +9,11 @@ internal sealed class Configuration : DbMigrationsConfiguration + true @@ -97,6 +98,14 @@ ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + True + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + True + diff --git a/src/Plugins/SmartStore.Shipping/packages.config b/src/Plugins/SmartStore.Shipping/packages.config index 79c5c7d2ef..ebe64a266f 100644 --- a/src/Plugins/SmartStore.Shipping/packages.config +++ b/src/Plugins/SmartStore.Shipping/packages.config @@ -7,4 +7,6 @@ + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.Shipping/web.config b/src/Plugins/SmartStore.Shipping/web.config index 02745f9ebf..1ceccfb858 100644 --- a/src/Plugins/SmartStore.Shipping/web.config +++ b/src/Plugins/SmartStore.Shipping/web.config @@ -85,7 +85,7 @@ - + @@ -133,4 +133,13 @@ - \ No newline at end of file + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.ShippingByWeight/Data/Migrations/Configuration.cs b/src/Plugins/SmartStore.ShippingByWeight/Data/Migrations/Configuration.cs index 8e4c78ea05..2e6c9177cf 100644 --- a/src/Plugins/SmartStore.ShippingByWeight/Data/Migrations/Configuration.cs +++ b/src/Plugins/SmartStore.ShippingByWeight/Data/Migrations/Configuration.cs @@ -12,6 +12,8 @@ public Configuration() AutomaticMigrationsEnabled = false; MigrationsDirectory = @"Data\Migrations"; ContextKey = "SmartStore.ShippingByWeight"; + + //SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); } protected override void Seed(ShippingByWeightObjectContext context) diff --git a/src/Plugins/SmartStore.ShippingByWeight/SmartStore.ShippingByWeight.csproj b/src/Plugins/SmartStore.ShippingByWeight/SmartStore.ShippingByWeight.csproj index ad884b14ee..3ed9167e9b 100644 --- a/src/Plugins/SmartStore.ShippingByWeight/SmartStore.ShippingByWeight.csproj +++ b/src/Plugins/SmartStore.ShippingByWeight/SmartStore.ShippingByWeight.csproj @@ -43,6 +43,7 @@ + true @@ -98,6 +99,14 @@ ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll False + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + True + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + True + diff --git a/src/Plugins/SmartStore.ShippingByWeight/packages.config b/src/Plugins/SmartStore.ShippingByWeight/packages.config index 42df152c4d..a08b3f61c7 100644 --- a/src/Plugins/SmartStore.ShippingByWeight/packages.config +++ b/src/Plugins/SmartStore.ShippingByWeight/packages.config @@ -7,4 +7,6 @@ + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.ShippingByWeight/web.config b/src/Plugins/SmartStore.ShippingByWeight/web.config index 02745f9ebf..1ceccfb858 100644 --- a/src/Plugins/SmartStore.ShippingByWeight/web.config +++ b/src/Plugins/SmartStore.ShippingByWeight/web.config @@ -85,7 +85,7 @@ - + @@ -133,4 +133,13 @@ - \ No newline at end of file + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.Tax/Data/Migrations/Configuration.cs b/src/Plugins/SmartStore.Tax/Data/Migrations/Configuration.cs index 465429494b..3f092bd6fb 100644 --- a/src/Plugins/SmartStore.Tax/Data/Migrations/Configuration.cs +++ b/src/Plugins/SmartStore.Tax/Data/Migrations/Configuration.cs @@ -12,7 +12,9 @@ public Configuration() AutomaticMigrationsEnabled = false; MigrationsDirectory = @"Data\Migrations"; ContextKey = "SmartStore.TaxCountryStateZip"; // DO NOT CHANGE - } + + //SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); + } protected override void Seed(TaxRateObjectContext context) { diff --git a/src/Plugins/SmartStore.Tax/SmartStore.Tax.csproj b/src/Plugins/SmartStore.Tax/SmartStore.Tax.csproj index c976e5800c..9b7608abbb 100644 --- a/src/Plugins/SmartStore.Tax/SmartStore.Tax.csproj +++ b/src/Plugins/SmartStore.Tax/SmartStore.Tax.csproj @@ -95,8 +95,17 @@ ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + True + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + True + + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + True diff --git a/src/Plugins/SmartStore.Tax/packages.config b/src/Plugins/SmartStore.Tax/packages.config index 5ea7037968..f67e15867e 100644 --- a/src/Plugins/SmartStore.Tax/packages.config +++ b/src/Plugins/SmartStore.Tax/packages.config @@ -6,5 +6,7 @@ - + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.Tax/web.config b/src/Plugins/SmartStore.Tax/web.config index a476d4989f..5b14ca4f42 100644 --- a/src/Plugins/SmartStore.Tax/web.config +++ b/src/Plugins/SmartStore.Tax/web.config @@ -86,7 +86,7 @@ - + @@ -134,4 +134,13 @@ - \ No newline at end of file + + + + + + + + + + \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index 8d9efa14a3..2711f6811a 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -828,7 +828,6 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().SingleInstance(); // Sitemaps builder.RegisterType().As().InstancePerRequest(); diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlExtensions.cs index 40e05538a9..3e3d884333 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlExtensions.cs @@ -82,7 +82,6 @@ public static HelperResult LocalizedEditor(this HtmlHel var language = languageService.GetLanguageById(locale.LanguageId); x.Add().Text(language.Name) - .LinkHtmlAttributes(new { title = language.Name }) .ContentHtmlAttributes(new { @class = "locale-editor-content", data_lang = language.LanguageCulture, data_rtl = language.Rtl.ToString().ToLower() }) .Content(localizedTemplate(i)) .ImageUrl("~/Content/images/flags/" + language.FlagImageFileName) @@ -817,7 +816,7 @@ public static MvcHtmlString IconForFileExtension(this HtmlHelper helper, string Guard.NotNull(helper, nameof(helper)); Guard.NotEmpty(fileExtension, nameof(fileExtension)); - var icon = "far fa-file"; + var icon = "file-o"; var ext = fileExtension; if (ext != null && ext.StartsWith(".")) @@ -830,7 +829,7 @@ public static MvcHtmlString IconForFileExtension(this HtmlHelper helper, string switch (ext.ToLowerInvariant()) { case "pdf": - icon = "far fa-file-pdf"; + icon = "file-pdf-o"; break; case "doc": case "docx": @@ -839,18 +838,18 @@ public static MvcHtmlString IconForFileExtension(this HtmlHelper helper, string case "dot": case "dotx": case "dotm": - icon = "far fa-file-word"; + icon = "file-word-o"; break; case "xls": case "xlsx": case "xlsm": case "xlsb": case "ods": - icon = "far fa-file-excel"; + icon = "file-excel-o"; break; case "csv": case "tab": - icon = "fa fa-file-csv"; + icon = "table"; break; case "ppt": case "pptx": @@ -862,25 +861,25 @@ public static MvcHtmlString IconForFileExtension(this HtmlHelper helper, string case "potm": case "pps": case "ppsm": - icon = "far fa-file-powerpoint"; + icon = "file-powerpoint-o"; break; case "zip": case "rar": case "7z": - icon = "far fa-file-archive"; + icon = "file-archive-o"; break; case "png": case "jpg": case "jpeg": case "bmp": case "psd": - icon = "far fa-file-image"; + icon = "file-image-o"; break; case "mp3": case "wav": case "ogg": case "wma": - icon = "far fa-file-audio"; + icon = "file-audio-o"; break; case "mp4": case "mkv": @@ -889,25 +888,25 @@ public static MvcHtmlString IconForFileExtension(this HtmlHelper helper, string case "asf": case "mpg": case "mpeg": - icon = "far fa-file-video"; + icon = "file-video-o"; break; case "txt": - icon = "far fa-file-alt"; + icon = "file-text-o"; break; case "exe": - icon = "fa fa-cog"; + icon = "gear"; break; case "xml": case "html": case "htm": - icon = "far fa-file-code"; + icon = "file-code-o"; break; } } var label = ext.NaIfEmpty().ToUpper(); - var result = "".FormatInvariant( + var result = "".FormatInvariant( icon, extraCssClasses.HasValue() ? " " + extraCssClasses : "", label); diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlZoneExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlZoneExtensions.cs index fd9b6f3717..472a99d9c8 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlZoneExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlZoneExtensions.cs @@ -28,7 +28,6 @@ private class DocumentZone : IDisposable private readonly WebViewPage _page; private readonly string _targetZone; private readonly ZoneInjectMode _injectMode; - private readonly bool _isVoid; public DocumentZone(HtmlHelper html, string targetZone, ZoneInjectMode injectMode, string key) { @@ -47,19 +46,7 @@ public DocumentZone(HtmlHelper html, string targetZone, ZoneInjectMode injectMod if (key.HasValue()) { - if (HasUniqueKey(key)) - { - _isVoid = true; - } - else - { - UniqueKeys.Add(key); - } - } - - if (_page.Request.IsAjaxRequest()) - { - _isVoid = true; + UniqueKeys.Add(key); } } @@ -99,7 +86,7 @@ public void Dispose() return; var writer = ((StringWriter)_page.OutputStack.Pop()); - var content = _isVoid ? string.Empty : writer.ToString(); + var content = writer.ToString(); _viewContext.Writer = _originalViewContextWriter; @@ -127,6 +114,11 @@ public static IDisposable BeginZoneContent(this HtmlHelper helper, ZoneInjectMode injectMode = ZoneInjectMode.Append, string key = null) { + if ((key.HasValue() && DocumentZone.HasUniqueKey(key)) || helper.ViewContext.HttpContext.Request.IsAjaxRequest()) + { + return ActionDisposable.Empty; + } + return new DocumentZone(helper, targetZone, injectMode, key); } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/CompressAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/CompressAttribute.cs index 24ba430c44..a2f0849df8 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/CompressAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/CompressAttribute.cs @@ -11,17 +11,19 @@ public class CompressAttribute : ActionFilterAttribute public override void OnActionExecuting(ActionExecutingContext filterContext) { var response = HttpContext.Current.Response; - var acceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"].EmptyNull().ToLower(); + string acceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"].EmptyNull().ToLower(); if (acceptEncoding.Contains("gzip") && !(PreferDeflate && acceptEncoding.Contains("deflate"))) { response.Filter = new GZipStream(response.Filter, CompressionMode.Compress); + response.Headers.Remove("Content-Encoding"); response.AppendHeader("Content-Encoding", "gzip"); } else if (acceptEncoding.Contains("deflate")) { response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress); + response.Headers.Remove("Content-Encoding"); response.AppendHeader("Content-Encoding", "deflate"); } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/CookieConsentFilter.cs b/src/Presentation/SmartStore.Web.Framework/Filters/CookieConsentFilter.cs index 7d8b4e8751..fa35700505 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/CookieConsentFilter.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/CookieConsentFilter.cs @@ -1,14 +1,14 @@ -using System; -using System.Web; -using System.Web.Mvc; -using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.Customers; using SmartStore.Services; using SmartStore.Services.Common; using SmartStore.Web.Framework.UI; +using System; +using System.Web; +using System.Web.Mvc; namespace SmartStore.Web.Framework.Filters { - public class CookieConsentFilter : IActionFilter, IResultFilter + public class CookieConsentFilter : IActionFilter, IResultFilter { private readonly IUserAgent _userAgent; private readonly ICommonServices _services; @@ -94,6 +94,33 @@ public void OnActionExecuting(ActionExecutingContext filterContext) viewBag.HasCookieConsent = false; } } + + // supress action of widgets which are setting cookies + // TODO: What to do if cookies are set from scripts within topics??? + if (consentCookie != null && consentCookie.Value != "true") + { + switch (controllerName) + { + case "AmazonPay": + case "AmazonPayCheckout": + case "AmazonPayShoppingCart": + case "WidgetsGoogleAnalytics": + case "ExternalAuthFacebook": + case "WidgetsETracker": + filterContext.Result = new EmptyResult(); + break; + case "Product": + if (actionName == "ShareButton") + { + filterContext.Result = new EmptyResult(); + } + break; + + default: + //nothing to do here + break; + } + } } public void OnActionExecuted(ActionExecutedContext filterContext) diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/HandleInstallFilter.cs b/src/Presentation/SmartStore.Web.Framework/Filters/HandleInstallFilter.cs index af2e1fbaf5..faf969a4c7 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/HandleInstallFilter.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/HandleInstallFilter.cs @@ -17,7 +17,10 @@ public void OnAuthorization(AuthorizationContext filterContext) return; } - if (!DataSettings.DatabaseIsInstalled() && controllerName != "Install") + + + // MODE: Dont route to install if test + if (!DataSettings.DatabaseIsInstalled() && controllerName != "Install" && controllerName != "Test") { filterContext.Result = new RedirectToRouteResult( new RouteValueDictionary { diff --git a/src/Presentation/SmartStore.Web.Framework/Localization/LocalizedRoute.cs b/src/Presentation/SmartStore.Web.Framework/Localization/LocalizedRoute.cs index 382574d0bd..c29c145a8c 100644 --- a/src/Presentation/SmartStore.Web.Framework/Localization/LocalizedRoute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Localization/LocalizedRoute.cs @@ -124,14 +124,15 @@ public override VirtualPathData GetVirtualPath(RequestContext requestContext, Ro if (data != null && DataSettings.DatabaseIsInstalled() && SeoFriendlyUrlsForLanguagesEnabled) { var helper = new LocalizedUrlHelper(requestContext.HttpContext.Request, true); - if (helper.IsLocalizedUrl(out string cultureCode)) - { + string cultureCode; + if (helper.IsLocalizedUrl(out cultureCode)) + { if (!requestContext.RouteData.Values.ContainsKey("StripInvalidSeoCode")) { data.VirtualPath = String.Concat(cultureCode, "/", data.VirtualPath).TrimEnd('/'); } - } - } + } + } return data; } diff --git a/src/Presentation/SmartStore.Web.Framework/Seo/GenericPathRoute.cs b/src/Presentation/SmartStore.Web.Framework/Seo/GenericPathRoute.cs index ccf42a7bfd..b81cef572d 100644 --- a/src/Presentation/SmartStore.Web.Framework/Seo/GenericPathRoute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Seo/GenericPathRoute.cs @@ -18,7 +18,8 @@ namespace SmartStore.Web.Framework.Seo public class GenericPathRoute : LocalizedRoute { // Key = Prefix, Value = EntityType - private static readonly Multimap _urlPrefixes = new Multimap(StringComparer.OrdinalIgnoreCase); + private readonly Multimap _urlPrefixes = + new Multimap(StringComparer.OrdinalIgnoreCase, x => new HashSet(x, StringComparer.OrdinalIgnoreCase)); /// /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern and handler class. @@ -67,22 +68,12 @@ public GenericPathRoute(string url, RouteValueDictionary defaults, RouteValueDic { } - public static void RegisterUrlPrefix(string prefix, params string[] entityNames) + public void RegisterUrlPrefix(string prefix, params string[] entityNames) { Guard.NotEmpty(prefix, nameof(prefix)); _urlPrefixes.AddRange(prefix, entityNames); } - - public static string GetUrlPrefixFor(string entityName) - { - Guard.NotEmpty(entityName, nameof(entityName)); - - if (_urlPrefixes.Count == 0) - return null; - - return _urlPrefixes.FirstOrDefault(x => x.Value.Contains(entityName, StringComparer.OrdinalIgnoreCase)).Key; - } /// /// Returns information about the requested route. @@ -138,7 +129,7 @@ public override RouteData GetRouteData(HttpContextBase httpContext) } // Verify prefix matches any assigned entity name - if (entityNames != null && !entityNames.Contains(urlRecord.EntityName, StringComparer.OrdinalIgnoreCase)) + if (entityNames != null && !entityNames.Contains(urlRecord.EntityName)) { // does NOT match return NotFound(data); diff --git a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj index c9c5625a5d..4367f0c68e 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj +++ b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj @@ -135,9 +135,14 @@ True ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - True + + ..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + + + ..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + + + ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll @@ -359,9 +364,6 @@ - - - diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPage.cs b/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPage.cs index 6593d8fad7..b0404c4afe 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPage.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPage.cs @@ -304,47 +304,6 @@ public LocalizationFileResolveResult ResolveLocalizationFile( { return _helper.LocalizationFileResolver.Resolve(culture, virtualPath, pattern, true, fallbackCulture); } - - public bool HasMetadata(string name) - { - return TryGetMetadata(name, out _); - } - - /// - /// Looks up an entry in ViewData dictionary first, then in ViewData.ModelMetadata.AdditionalValues dictionary - /// - /// Actual type of value - /// Name of entry - /// Result - public T GetMetadata(string name) - { - TryGetMetadata(name, out var value); - return value; - } - - /// - /// Looks up an entry in ViewData dictionary first, then in ViewData.ModelMetadata.AdditionalValues dictionary - /// - /// Actual type of value - /// Name of entry - /// true if the entry exists in any of the dictionaries, false otherwise - public bool TryGetMetadata(string name, out T value) - { - value = default(T); - - var exists = ViewData.TryGetValue(name, out var raw); - if (!exists) - { - exists = ViewData.ModelMetadata?.AdditionalValues?.TryGetValue(name, out raw) == true; - } - - if (raw != null) - { - value = raw.Convert(); - } - - return exists; - } } public abstract class WebViewPage : WebViewPage diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Blocks/IBlockEntity.cs b/src/Presentation/SmartStore.Web.Framework/UI/Blocks/IBlockEntity.cs index 81e8a86cdb..d465a2d0c8 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Blocks/IBlockEntity.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Blocks/IBlockEntity.cs @@ -9,7 +9,6 @@ namespace SmartStore.Web.Framework.UI.Blocks public interface IBlockEntity { int Id { get; set; } - int StoryId { get; } string BlockType { get; set; } string Model { get; set; } string TagLine { get; set; } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStripRenderer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStripRenderer.cs index cb070f79c0..c55f9d03a0 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStripRenderer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStripRenderer.cs @@ -367,17 +367,20 @@ protected virtual string RenderItemLink(HtmlTextWriter writer, Tab item, int ind writer.RenderBeginTag("img"); writer.RenderEndTag(); // img } - - // Caption - writer.AddAttribute("class", "tab-caption"); - writer.RenderBeginTag("span"); - writer.WriteEncodedText(item.Text); - writer.RenderEndTag(); + //writer.WriteEncodedText(item.Text); // Badge if (item.BadgeText.HasValue()) { - // Label/badge + //writer.Write(" "); + + // caption + writer.AddAttribute("class", "tab-caption"); + writer.RenderBeginTag("span"); + writer.WriteEncodedText(item.Text); + writer.RenderEndTag(); // span > badge + + // label/badge temp = "ml-2 badge"; temp += " badge-" + item.BadgeStyle.ToString().ToLower(); if (base.Component.Position == TabsPosition.Left) @@ -389,8 +392,12 @@ protected virtual string RenderItemLink(HtmlTextWriter writer, Tab item, int ind writer.WriteEncodedText(item.BadgeText); writer.RenderEndTag(); // span > badge } + else + { + writer.WriteEncodedText(item.Text); + } - // Nav link short summary for collapsed state + // nav link short summary for collapsed state if (this.Component.IsResponsive && item.Summary.HasValue()) { writer.AddAttribute("class", "nav-link-summary"); diff --git a/src/Presentation/SmartStore.Web.Framework/UI/WidgetProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/WidgetProvider.cs index fd38661772..5e842f0af4 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/WidgetProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/WidgetProvider.cs @@ -41,16 +41,18 @@ public void RegisterAction(string[] widgetZones, string actionName, string contr _zoneWidgetsMap = new Multimap(); } + var routeInfo = new WidgetRouteInfo + { + ActionName = actionName, + ControllerName = controllerName, + RouteValues = routeValues ?? new RouteValueDictionary(), + Order = order + }; + foreach (var zone in widgetZones) { if (zone.HasValue()) - _zoneWidgetsMap.Add(zone, new WidgetRouteInfo - { - ActionName = actionName, - ControllerName = controllerName, - RouteValues = new RouteValueDictionary(routeValues ?? new RouteValueDictionary()), - Order = order - }); + _zoneWidgetsMap.Add(zone, routeInfo); } } diff --git a/src/Presentation/SmartStore.Web.Framework/WebStoreContext.cs b/src/Presentation/SmartStore.Web.Framework/WebStoreContext.cs index 565e3f5bfe..39592efb10 100644 --- a/src/Presentation/SmartStore.Web.Framework/WebStoreContext.cs +++ b/src/Presentation/SmartStore.Web.Framework/WebStoreContext.cs @@ -3,7 +3,6 @@ using System.Web; using SmartStore.Core; using SmartStore.Core.Domain.Stores; -using SmartStore.Core.Infrastructure.DependencyManagement; using SmartStore.Services.Stores; namespace SmartStore.Web.Framework @@ -15,13 +14,13 @@ public partial class WebStoreContext : IStoreContext { internal const string OverriddenStoreIdKey = "OverriddenStoreId"; - private readonly Work _storeService; + private readonly IStoreService _storeService; private readonly IWebHelper _webHelper; private readonly HttpContextBase _httpContext; private Store _currentStore; - public WebStoreContext(Work storeService, IWebHelper webHelper, HttpContextBase httpContext) + public WebStoreContext(IStoreService storeService, IWebHelper webHelper, HttpContextBase httpContext) { _storeService = storeService; _webHelper = webHelper; @@ -110,14 +109,14 @@ public Store CurrentStore if (storeOverride.HasValue) { // the store to be used can be overwritten on request basis (e.g. for theme preview, editing etc.) - _currentStore = _storeService.Value.GetStoreById(storeOverride.Value); + _currentStore = _storeService.GetStoreById(storeOverride.Value); } if (_currentStore == null) { // ty to determine the current store by HTTP_HOST var host = _webHelper.ServerVariables("HTTP_HOST"); - var allStores = _storeService.Value.GetAllStores(); + var allStores = _storeService.GetAllStores(); var store = allStores.FirstOrDefault(s => s.ContainsHostValue(host)); if (store == null) @@ -148,7 +147,7 @@ public int CurrentStoreIdIfMultiStoreMode { get { - return _storeService.Value.IsSingleStoreMode() ? 0 : CurrentStore.Id; + return _storeService.IsSingleStoreMode() ? 0 : CurrentStore.Id; } } diff --git a/src/Presentation/SmartStore.Web.Framework/app.config b/src/Presentation/SmartStore.Web.Framework/app.config index d2ed8f831e..046418b87c 100644 --- a/src/Presentation/SmartStore.Web.Framework/app.config +++ b/src/Presentation/SmartStore.Web.Framework/app.config @@ -24,7 +24,7 @@ - + @@ -64,4 +64,13 @@ - + + + + + + + + + + diff --git a/src/Presentation/SmartStore.Web.Framework/packages.config b/src/Presentation/SmartStore.Web.Framework/packages.config index c24bbb1532..8bb223685b 100644 --- a/src/Presentation/SmartStore.Web.Framework/packages.config +++ b/src/Presentation/SmartStore.Web.Framework/packages.config @@ -28,7 +28,9 @@ - + + + diff --git a/src/Presentation/SmartStore.Web/Administration/Content/_admin.scss b/src/Presentation/SmartStore.Web/Administration/Content/_admin.scss index fbf1736a30..34243efe86 100644 --- a/src/Presentation/SmartStore.Web/Administration/Content/_admin.scss +++ b/src/Presentation/SmartStore.Web/Administration/Content/_admin.scss @@ -145,7 +145,7 @@ body { grid-column: 1 / -1; grid-row: 2 / -1; z-index: 9; - //overflow-x: hidden; // observe and maybe uncomment later + overflow-x: hidden; .popup & { margin: 0; @@ -265,8 +265,10 @@ body { z-index: 600; left: var(--content-padding-x); right: var(--content-padding-x); - padding: calc(var(--content-padding-y) / 2) var(--content-padding-x); + padding-left: var(--content-padding-x); + padding-right: var(--content-padding-x); background: #fff; + //border-bottom: 1px solid rgba(0,0,0, 0.08); box-shadow: 0 8px 16px 0 rgba(#32325d, .1), 0 2px 7px 0 rgba(#000, .07); .modal-open & { @@ -789,11 +791,6 @@ legend { } } - .nav-item:nth-child(n+8) > .nav-link { - > img, > i { margin-left: 0 !important; margin-right: 0 !important; } - > span { display: none; } - } - .nav-link:hover, .nav-link.active { color: inherit; diff --git a/src/Presentation/SmartStore.Web/Administration/Content/_variables.scss b/src/Presentation/SmartStore.Web/Administration/Content/_variables.scss index d14b8cd8b4..7e0de9e97f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Content/_variables.scss +++ b/src/Presentation/SmartStore.Web/Administration/Content/_variables.scss @@ -110,17 +110,8 @@ $h5-font-size: 1.25rem; $h6-font-size: 1rem; $small-font-size: 90%; -$headings-font-weight: 400; - - -// Shadows - -$box-shadow-sm: 0 0 .625rem 0 rgba(#000, .05); -$box-shadow: 0 1rem 2.25rem rgba(#32325d, .03), 0 0.3125rem 1rem rgba(#000, .12); -$box-shadow-lg: 0 1rem 3rem rgba(#000, .125); - -// Tables +$headings-font-weight: 400; $table-cell-padding: .75rem; $table-cell-padding-sm: .3rem; @@ -159,8 +150,8 @@ $btn-border-radius-lg: $input-border-radius-lg; //.1875rem; $btn-border-radius-sm: $input-border-radius-sm; //.125rem; $btn-disabled-opacity: 0.5; -$dropdown-box-shadow: $box-shadow; // 0 3px 12px rgba(27,31,35, 0.15); //0 2px 6px rgba(#000, .15); -$dropdown-border-color: rgba(#000, 0.05); +$dropdown-box-shadow: 0 3px 12px rgba(27,31,35, 0.15); //0 2px 6px rgba(#000, .15); +$dropdown-border-color: rgba(#000, 0.08); $dropdown-link-color: $body-color; //$gray-800; $dropdown-link-hover-color: darken($dropdown-link-color, 5%); $dropdown-link-hover-bg: lighten($gray-200, 3%); @@ -184,12 +175,6 @@ $component-active-bg: lighten($primary, 36%); $component-active-color: inherit; -$modal-content-border-width: 0; -$modal-backdrop-opacity: .55; -$modal-content-box-shadow-xs: 0 12px 26px rgba(#32325d, .12), 0 3px 10px rgba(#000, .08); -$modal-content-box-shadow-sm-up: 0 50px 100px rgba(#32325d, .12), 0 15px 35px rgba(#32325d, .27), 0 5px 15px rgba(#000, .25); - - /*$custom-control-indicator-bg: $gray-200; $custom-control-indicator-disabled-bg: $gray-100; $custom-control-indicator-checked-color: #fff; diff --git a/src/Presentation/SmartStore.Web/Administration/Content/filemanager/js/file.js b/src/Presentation/SmartStore.Web/Administration/Content/filemanager/js/file.js index fa252adf39..9d27380ad6 100644 --- a/src/Presentation/SmartStore.Web/Administration/Content/filemanager/js/file.js +++ b/src/Presentation/SmartStore.Web/Administration/Content/filemanager/js/file.js @@ -101,7 +101,7 @@ function File(filePath, fileSize, modTime, w, h, mime) { ]; var html = [ '
  • ', - '
    ', + '
    ', '' + RoxyUtils.FormatDate(new Date(this.time * 1000)) + '', '' + this.name + '', '' + RoxyUtils.FormatFileSize(this.size) + '', @@ -176,7 +176,7 @@ function File(filePath, fileSize, modTime, w, h, mime) { var icon = RoxyIconHints[fileType]; var li = $('li[data-path="' + item.fullPath + '"]'); li.toggleClass('file-image', fileType == 'image'); - $('.file-icon', li).attr('class', "").addClass('file-icon fa-fw ' + icon.name).css('color', icon.color); + $('.file-icon', li).attr('class', "").addClass('file-icon fa fa-fw fa-' + icon.name).css('color', icon.color); $('.name', li).text(newName); $('li[data-path="' + newPath + '"]').attr('data-path', newPath); ret = true; diff --git a/src/Presentation/SmartStore.Web/Administration/Content/filemanager/js/utils.js b/src/Presentation/SmartStore.Web/Administration/Content/filemanager/js/utils.js index bd7c547e9e..d056f763ce 100644 --- a/src/Presentation/SmartStore.Web/Administration/Content/filemanager/js/utils.js +++ b/src/Presentation/SmartStore.Web/Administration/Content/filemanager/js/utils.js @@ -107,21 +107,21 @@ RoxyLang = { } var RoxyIconHints = { - "pdf": { name: "far fa-file-pdf", color: "#F44336" }, - "document": { name: "far fa-file-word", color: "#2B579A" }, - "spreadsheet": { name: "far fa-file-excel", color: "#217346" }, - "database": { name: "fa fa-database", color: "#3ba074" }, - "presentation": { name: "far fa-file-powerpoint", color: "#D24726" }, - "archive": { name: "far fa-file-archive", color: "#3F51B5" }, - "audio": { name: "far fa-file-audio", color: "#009688" }, - "markup": { name: "far fa-file-code", color: "#4CAF50" }, - "code": { name: "fa fa-bolt", color: "#4CAF50" }, - "exe": { name: "fa fa-cog", color: "#58595B" }, - "image": { name: "far fa-file-image", color: "#e77c00" }, - "text": { name: "far fa-file-alt", color: "#607D8B" }, - "video": { name: "far fa-file-video", color: "#FF5722" }, - "font": { name: "fa fa-font", color: "#797985" }, - "misc": { name: "far fa-file", color: "#ccc" } + "pdf": { name: "file-pdf-o", color: "#F44336" }, + "document": { name: "file-word-o", color: "#2B579A" }, + "spreadsheet": { name: "file-excel-o", color: "#217346" }, + "database": { name: "database", color: "#3ba074" }, + "presentation": { name: "file-powerpoint-o", color: "#D24726" }, + "archive": { name: "file-archive-o", color: "#3F51B5" }, + "audio": { name: "file-audio-o", color: "#009688" }, + "markup": { name: "file-code-o", color: "#4CAF50" }, + "code": { name: "bolt", color: "#4CAF50" }, + "exe": { name: "gear", color: "#58595B" }, + "image": { name: "file-image-o", color: "#e77c00" }, + "text": { name: "file-text-o", color: "#607D8B" }, + "video": { name: "file-video-o", color: "#FF5722" }, + "font": { name: "font", color: "#797985" }, + "misc": { name: "file-o", color: "#ccc" } } function RoxyUtils() { } diff --git a/src/Presentation/SmartStore.Web/Administration/Content/theme.scss b/src/Presentation/SmartStore.Web/Administration/Content/theme.scss index dabb611b58..923ebaec36 100644 --- a/src/Presentation/SmartStore.Web/Administration/Content/theme.scss +++ b/src/Presentation/SmartStore.Web/Administration/Content/theme.scss @@ -41,20 +41,20 @@ } } -/*.btn-secondary { +.btn-secondary { @include button-variant($secondary, $secondary, $secondary, rgba(#000, 0.15), darken($secondary, 10%), rgba(#000, 0.2)); -}*/ +} -/*.btn-light { +.btn-light { @include button-variant($light, $light, $light, rgba(#000, 0.15), darken($light, 10%), rgba(#000, 0.2)); -}*/ +} -/*.btn-secondary, +.btn-secondary, .btn-light { &:focus, &.focus { border-color: rgba(#000, 0.2); } -}*/ +} // Light button icons lighter (looks nice) // ----------------------------------------------------- @@ -68,7 +68,7 @@ &:not(:hover):not(.hover):not(:active):not(.active):not(:focus):not(.focus):not([disabled]):not(.disabled) { > i { //opacity: 0.6; - color: lighten($body-color, 12%); + color: lighten($body-color, 20%); } } } @@ -79,7 +79,6 @@ @import '../../Content/shared/_variables-shared.scss'; @import '../../Content/shared/_mixins.scss'; -@import '../../Content/shared/_fa.scss'; @import '../../Content/shared/_typo.scss'; @import '../../Content/shared/_alert.scss'; @import '../../Content/shared/_buttons.scss'; diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs index 68e51ef76c..315f05af42 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/CommonController.cs @@ -337,6 +337,7 @@ public ActionResult SystemInfo() // DB size & used RAM try { + // TODO: change to mysql var mbSize = _services.DbContext.SqlQuery("Select Sum(size)/128.0 From sysfiles").FirstOrDefault(); model.DatabaseSize = Convert.ToInt64(mbSize * 1024 *1024); @@ -497,6 +498,8 @@ public ActionResult Warnings() { var msg = T("Admin.System.Warnings.TaskScheduler.Fail", _taskScheduler.BaseUrl, exception.Message); + var xxx = T("Admin.System.Warnings.TaskScheduler.Fail"); + model.Add(new SystemWarningModel { Level = SystemWarningLevel.Fail, @@ -511,7 +514,7 @@ public ActionResult Warnings() string sitemapUrl = null; try { - sitemapUrl = WebHelper.GetAbsoluteUrl(Url.RouteUrl("XmlSitemap"), this.Request); + sitemapUrl = WebHelper.GetAbsoluteUrl(Url.RouteUrl("SitemapSEO"), this.Request); var request = WebHelper.CreateHttpRequestForSafeLocalCall(new Uri(sitemapUrl)); request.Method = "HEAD"; request.Timeout = 15000; diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs index f0caf9fc5a..20c2d56343 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ExportController.cs @@ -979,22 +979,16 @@ public ActionResult DeleteConfirmed(int id) public ActionResult Preview(int id) { - if (!Services.Permissions.Authorize(StandardPermissionProvider.ManageExports)) - { - return AccessDeniedView(); - } + if (!Services.Permissions.Authorize(StandardPermissionProvider.ManageExports)) + return AccessDeniedView(); var profile = _exportService.GetExportProfileById(id); - if (profile == null) - { - return RedirectToAction("List"); - } + if (profile == null) + return RedirectToAction("List"); var provider = _exportService.LoadProvider(profile.ProviderSystemName); - if (provider == null || provider.Metadata.IsHidden) - { - return RedirectToAction("List"); - } + if (provider == null || provider.Metadata.IsHidden) + return RedirectToAction("List"); if (!profile.Enabled) { @@ -1004,6 +998,7 @@ public ActionResult Preview(int id) } var request = new DataExportRequest(profile, provider); + var totalRecords = _dataExporter.GetDataCount(request); var model = new ExportPreviewModel { @@ -1012,6 +1007,7 @@ public ActionResult Preview(int id) ThumbnailUrl = GetThumbnailUrl(provider), GridPageSize = DataExporter.PageSize, EntityType = provider.Value.EntityType, + TotalRecords = totalRecords, LogFileExists = System.IO.File.Exists(profile.GetExportLogPath()), UsernamesEnabled = _customerSettings.Value.UsernamesEnabled }; @@ -1020,7 +1016,7 @@ public ActionResult Preview(int id) } [HttpPost, GridAction(EnableCustomBinding = true)] - public ActionResult PreviewList(GridCommand command, int id) + public ActionResult PreviewList(GridCommand command, int id, int totalRecords) { if (!Services.Permissions.Authorize(StandardPermissionProvider.ManageExports)) { @@ -1038,20 +1034,19 @@ public ActionResult PreviewList(GridCommand command, int id) return new JsonResult { Data = Enumerable.Empty() }; } - object gridData = null; - var pageIndex = command.Page - 1; - var request = new DataExportRequest(profile, provider); - var result = _dataExporter.Preview(request, pageIndex); - - var normalizedTotal = profile.Limit > 0 && result.TotalRecords > profile.Limit + var request = new DataExportRequest(profile, provider); + var normalizedTotal = profile.Limit > 0 && totalRecords > profile.Limit ? profile.Limit - : result.TotalRecords; + : totalRecords; + var pageIndex = command.Page - 1; + object gridData = null; - if (provider.Value.EntityType == ExportEntityType.Product) + if (provider.Value.EntityType == ExportEntityType.Product) { var models = new List(); + var items = _dataExporter.Preview(request, pageIndex, totalRecords); - foreach (var item in result.Data) + foreach (var item in items) { var product = item.Entity as Product; var model = new ExportPreviewProductModel(); @@ -1067,14 +1062,14 @@ public ActionResult PreviewList(GridCommand command, int id) model.AdminComment = item.AdminComment; models.Add(model); } - gridData = new GridModel { Data = models, Total = normalizedTotal }; } else if (provider.Value.EntityType == ExportEntityType.Order) { var models = new List(); + var items = _dataExporter.Preview(request, pageIndex, totalRecords); - foreach (var item in result.Data) + foreach (var item in items) { var model = new ExportPreviewOrderModel(); model.Id = item.Id; @@ -1089,14 +1084,14 @@ public ActionResult PreviewList(GridCommand command, int id) model.StoreName = (string)item.Store.Name; models.Add(model); } - gridData = new GridModel { Data = models, Total = normalizedTotal }; } else if (provider.Value.EntityType == ExportEntityType.Category) { var models = new List(); + var items = _dataExporter.Preview(request, pageIndex, totalRecords); - foreach (var item in result.Data) + foreach (var item in items) { var category = item.Entity as Category; var model = new ExportPreviewCategoryModel(); @@ -1114,8 +1109,9 @@ public ActionResult PreviewList(GridCommand command, int id) else if (provider.Value.EntityType == ExportEntityType.Manufacturer) { var models = new List(); + var items = _dataExporter.Preview(request, pageIndex, totalRecords); - foreach (var item in result.Data) + foreach (var item in items) { var model = new ExportPreviewManufacturerModel(); model.Id = item.Id; @@ -1125,14 +1121,14 @@ public ActionResult PreviewList(GridCommand command, int id) model.LimitedToStores = item.LimitedToStores; models.Add(model); } - gridData = new GridModel { Data = models, Total = normalizedTotal }; } else if (provider.Value.EntityType == ExportEntityType.Customer) { var models = new List(); + var items = _dataExporter.Preview(request, pageIndex, totalRecords); - foreach (var item in result.Data) + foreach (var item in items) { var customer = item.Entity as Customer; var customerRoles = item.CustomerRoles as List; @@ -1149,14 +1145,14 @@ public ActionResult PreviewList(GridCommand command, int id) model.Username = customer.Username; models.Add(model); } - gridData = new GridModel { Data = models, Total = normalizedTotal }; } else if (provider.Value.EntityType == ExportEntityType.NewsLetterSubscription) { var models = new List(); + var items = _dataExporter.Preview(request, pageIndex, totalRecords); - foreach (var item in result.Data) + foreach (var item in items) { var subscription = item.Entity as NewsLetterSubscription; var model = new ExportPreviewNewsLetterSubscriptionModel(); @@ -1167,17 +1163,18 @@ public ActionResult PreviewList(GridCommand command, int id) model.StoreName = (string)item.Store.Name; models.Add(model); } - - gridData = new GridModel { Data = models, Total = normalizedTotal }; + gridData = new GridModel { Data = models, Total = normalizedTotal }; } else if (provider.Value.EntityType == ExportEntityType.ShoppingCartItem) { var guest = T("Admin.Customers.Guest").Text; var cartTypeName = ShoppingCartType.ShoppingCart.GetLocalizedEnum(Services.Localization, Services.WorkContext); var wishlistTypeName = ShoppingCartType.Wishlist.GetLocalizedEnum(Services.Localization, Services.WorkContext); + var models = new List(); + var items = _dataExporter.Preview(request, pageIndex, totalRecords); - foreach (var item in result.Data) + foreach (var item in items) { var cartItem = item.Entity as ShoppingCartItem; var model = new ExportPreviewShoppingCartItemModel(); @@ -1199,7 +1196,6 @@ public ActionResult PreviewList(GridCommand command, int id) model.StoreName = (string)item.Store.Name; models.Add(model); } - gridData = new GridModel { Data = models, Total = normalizedTotal }; } @@ -1209,18 +1205,15 @@ public ActionResult PreviewList(GridCommand command, int id) [HttpPost] public ActionResult Execute(int id, string selectedIds) { - // Permissions checked internally by DataExporter. + // permissions checked internally by DataExporter + var profile = _exportService.GetExportProfileById(id); - if (profile == null) - { - return RedirectToAction("List"); - } + if (profile == null) + return RedirectToAction("List"); var provider = _exportService.LoadProvider(profile.ProviderSystemName); - if (provider == null || provider.Metadata.IsHidden) - { - return RedirectToAction("List"); - } + if (provider == null || provider.Metadata.IsHidden) + return RedirectToAction("List"); var taskParams = new Dictionary { @@ -1228,20 +1221,16 @@ public ActionResult Execute(int id, string selectedIds) { TaskExecutor.CurrentStoreIdParamName, Services.StoreContext.CurrentStore.Id.ToString() } }; - if (selectedIds.HasValue()) - { - taskParams.Add("SelectedIds", selectedIds); - } + if (selectedIds.HasValue()) + taskParams.Add("SelectedIds", selectedIds); _taskScheduler.RunSingleTask(profile.SchedulingTaskId, taskParams); NotifyInfo(T("Admin.System.ScheduleTasks.RunNow.Progress.DataExportTask")); var referrer = Services.WebHelper.GetUrlReferrer(); - if (referrer.HasValue()) - { - return Redirect(referrer); - } + if (referrer.HasValue()) + return Redirect(referrer); return RedirectToAction("List"); } diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs index f5388be5d7..277db80038 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/LanguageController.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using System.Web.Mvc; using System.Xml; -using System.Data.Entity; using Autofac; using Newtonsoft.Json; using SmartStore.Admin.Models.Localization; @@ -335,8 +334,6 @@ public ActionResult List() var lastImportInfos = GetLastResourcesImportInfos(); var languages = _languageService.GetAllLanguages(true); - var defaultLanguageId = _languageService.GetDefaultLanguageId(); - var model = languages.Select(x => { var langModel = x.ToModel(); @@ -348,11 +345,6 @@ public ActionResult List() langModel.LastResourcesImportOnString = langModel.LastResourcesImportOn.Value.RelativeFormat(false, "f"); } - if (x.Id == defaultLanguageId) - { - ViewBag.DefaultLanguageNote = T("Admin.Configuration.Languages.DefaultLanguage.Note", langModel.Name).Text; - } - return langModel; }) .ToList(); @@ -582,7 +574,7 @@ public ActionResult Resources(int languageId) var gridModel = new GridModel { Data = resourceQuery - .Take(() => _adminAreaSettings.GridPageSize) + .Take(_adminAreaSettings.GridPageSize) .ToList() .Select(x => new LanguageResourceModel { diff --git a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs index 5e2e76d4a1..1d78779b91 100644 --- a/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs +++ b/src/Presentation/SmartStore.Web/Administration/Controllers/ProductController.cs @@ -692,7 +692,7 @@ protected void PrepareProductModel(ProductModel model, Product product, bool set if (product != null) { model.CopyProductModel.Id = product.Id; - model.CopyProductModel.Name = T("Admin.Common.CopyOf", product.Name); + model.CopyProductModel.Name = "{0} {1}".FormatInvariant(T("Admin.Common.CopyOf"), product.Name); model.CopyProductModel.Published = true; model.CopyProductModel.CopyImages = true; } diff --git a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportPreviewModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportPreviewModel.cs index a3041c6234..dfe06e3d42 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportPreviewModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/DataExchange/ExportPreviewModel.cs @@ -10,6 +10,7 @@ public class ExportPreviewModel : EntityModelBase public string Name { get; set; } public string ThumbnailUrl { get; set; } public int GridPageSize { get; set; } + public int TotalRecords { get; set; } public ExportEntityType EntityType { get; set; } public bool LogFileExists { get; set; } public bool UsernamesEnabled { get; set; } diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Orders/OrderModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Orders/OrderModel.cs index 7d595a21f8..fd2e79a904 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Orders/OrderModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Orders/OrderModel.cs @@ -183,9 +183,9 @@ public string PaymentStatusLabelClass case Core.Domain.Payments.PaymentStatus.Paid: return "fa fa-fw fa-check text-success"; case Core.Domain.Payments.PaymentStatus.PartiallyRefunded: - return "fa fa-fw fa-exchange-alt text-warning"; + return "fa fa-fw fa-exchange text-warning"; case Core.Domain.Payments.PaymentStatus.Refunded: - return "fa fa-fw fa-exchange-alt text-success"; + return "fa fa-fw fa-exchange text-success"; case Core.Domain.Payments.PaymentStatus.Voided: return "fa fa-fw fa-ban muted"; default: diff --git a/src/Presentation/SmartStore.Web/Administration/Models/Settings/CatalogSettingsModel.cs b/src/Presentation/SmartStore.Web/Administration/Models/Settings/CatalogSettingsModel.cs index 8c2e1acf84..d7f56e0c9b 100644 --- a/src/Presentation/SmartStore.Web/Administration/Models/Settings/CatalogSettingsModel.cs +++ b/src/Presentation/SmartStore.Web/Administration/Models/Settings/CatalogSettingsModel.cs @@ -167,15 +167,6 @@ public CatalogSettingsModel() [SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.GridStyleListColumnSpan")] public GridColumnSpan GridStyleListColumnSpan { get; set; } - [SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.ShowSubCategoriesInSubPages")] - public bool ShowSubCategoriesInSubPages { get; set; } - - [SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.ShowDescriptionInSubPages")] - public bool ShowDescriptionInSubPages { get; set; } - - [SmartResourceDisplayName("Admin.Configuration.Settings.Catalog.IncludeFeaturedProductsInSubPages")] - public bool IncludeFeaturedProductsInSubPages { get; set; } - #endregion #region Products diff --git a/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj b/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj index 1b53de7ab0..207cd95cab 100644 --- a/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj +++ b/src/Presentation/SmartStore.Web/Administration/SmartStore.Admin.csproj @@ -93,9 +93,14 @@ False ..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - False + + ..\..\..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll + + + ..\..\..\packages\MySql.Data.Entity.6.9.12\lib\net45\MySql.Data.Entity.EF6.dll + + + ..\..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll ..\..\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll diff --git a/src/Presentation/SmartStore.Web/Administration/Views/ActivityLog/ListLogs.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/ActivityLog/ListLogs.cshtml index 104618e045..d66c8247a9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/ActivityLog/ListLogs.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/ActivityLog/ListLogs.cshtml @@ -13,11 +13,11 @@
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Affiliate/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Affiliate/Edit.cshtml index c6c90d06ee..d3a0e807e8 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Affiliate/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Affiliate/Edit.cshtml @@ -18,7 +18,7 @@ @T("Admin.Common.SaveContinue") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Blog/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Blog/Edit.cshtml index 95c5235662..a7001577b3 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Blog/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Blog/Edit.cshtml @@ -21,7 +21,7 @@ @T("Admin.Common.SaveContinue") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Blog/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Blog/List.cshtml index 1301a3b84e..eb2bc78b07 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Blog/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Blog/List.cshtml @@ -8,7 +8,7 @@ }
    - + @T("Admin.ContentManagement.Blog.BlogPosts")
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Campaign/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Campaign/Edit.cshtml index 8a1f55e5f5..b0b8562938 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Campaign/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Campaign/Edit.cshtml @@ -19,7 +19,7 @@ - + @T("Admin.Common.Preview") } @@ -31,7 +31,7 @@ @T("Admin.Common.SaveContinue")
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Category/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Category/Edit.cshtml index 16788e029a..24a9d2773f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Category/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Category/Edit.cshtml @@ -13,7 +13,7 @@ @{ Html.RenderWidget("admin_button_toolbar_before"); } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Category/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Category/_CreateOrUpdate.cshtml index 7a0d9bfd6b..f40907d2e0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Category/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Category/_CreateOrUpdate.cshtml @@ -7,7 +7,7 @@ @Html.SmartStore().TabStrip().Name("category-edit").OnAjaxSuccess("categoryEditTab_onAjaxSuccess").Style(TabsStyle.Material).Position(TabsPosition.Left).Items(x => { - x.Add().Text(T("Admin.Catalog.Categories.Info").Text).Icon("fa fa-pencil-alt fa-lg fa-fw").Content(TabInfo()).Selected(true); + x.Add().Text(T("Admin.Catalog.Categories.Info").Text).Icon("fa fa-pencil fa-lg fa-fw").Content(TabInfo()).Selected(true); x.Add().Text(T("Admin.Common.SEO").Text).Icon("fa fa-search fa-lg fa-fw").Content(TabSeo()); x.Add().Text(T("Admin.Catalog.Categories.Products").Text).Icon("fa fa-cube fa-lg fa-fw").Content(TabProducts()); x.Add().Text(T("Admin.Catalog.Categories.Discounts").Text).Icon("fa fa-percent fa-lg fa-fw").Content(TabDiscounts()); @@ -81,7 +81,7 @@ @@ -138,7 +138,7 @@ diff --git a/src/Presentation/SmartStore.Web/Administration/Views/CheckoutAttribute/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/CheckoutAttribute/Edit.cshtml index 0eafa758be..a669015600 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/CheckoutAttribute/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/CheckoutAttribute/Edit.cshtml @@ -20,7 +20,7 @@ @T("Admin.Common.SaveContinue")
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Common/CheckUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Common/CheckUpdate.cshtml index 55a33211b3..6d753c9fc9 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Common/CheckUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Common/CheckUpdate.cshtml @@ -6,7 +6,7 @@
    - + @ViewBag.Title
    @@ -41,7 +41,7 @@ @Html.Raw(T("Admin.CheckUpdate.AutoUpdatePossibleInfo"))

    - + @T("Admin.CheckUpdate.UpdateNow")

    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Common/Maintenance.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Common/Maintenance.cshtml index 6d9f727847..1a0526f6dd 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Common/Maintenance.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Common/Maintenance.cshtml @@ -42,7 +42,7 @@ @@ -93,7 +93,7 @@ @@ -143,7 +143,7 @@ diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Country/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Country/Edit.cshtml index 841d226c66..ca742f081f 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Country/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Country/Edit.cshtml @@ -18,7 +18,7 @@ @T("Admin.Common.SaveContinue") - + @T("Admin.Common.Delete") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Currency/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Currency/Edit.cshtml index e09d8871f5..b3221627a1 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Currency/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Currency/Edit.cshtml @@ -18,7 +18,7 @@ @T("Admin.Common.SaveContinue") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Currency/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Currency/List.cshtml index 7ad1e8c55d..2bad1d1227 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Currency/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Currency/List.cshtml @@ -9,7 +9,7 @@
    - + @T("Admin.Configuration.Currencies")
    @@ -22,7 +22,7 @@ @T("Admin.Common.Save") - + @T("Admin.Configuration.Currencies.GetLiveRates")
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Currency/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Currency/_CreateOrUpdate.cshtml index 045995d078..3be6d4f87a 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Currency/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Currency/_CreateOrUpdate.cshtml @@ -20,7 +20,7 @@ @Html.SmartStore().TabStrip().Name("currency-edit").Style(TabsStyle.Material).Position(TabsPosition.Top).Items(x => { x.Add().Text(T("Admin.Common.Info").Text) - //.Icon("fa fa-pencil-alt fa-lg fa-fw") + //.Icon("fa fa-pencil fa-lg fa-fw") .Content(TabInfo()) .Selected(true); @@ -28,7 +28,7 @@ //.Icon("fa fa-globe fa-lg fa-fw") .Content(TabStores()); - //generate an event + //generate an event EngineContext.Current.Resolve().Publish(new TabStripCreated(x, "currency-edit", this.Html, this.Model)); }) diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Customer/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Customer/Edit.cshtml index 30f350f1b4..7e1f08386e 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Customer/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Customer/Edit.cshtml @@ -40,7 +40,7 @@ @if (!Model.Deleted) { } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Customer/Reports.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Customer/Reports.cshtml index 9bee9eea4f..c6243d97b1 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Customer/Reports.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Customer/Reports.cshtml @@ -5,7 +5,7 @@ }
    - + @T("Admin.Customers.Reports")
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Customer/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Customer/_CreateOrUpdate.cshtml index 0eafdd7c64..4533ced3fc 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Customer/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Customer/_CreateOrUpdate.cshtml @@ -97,7 +97,7 @@ { @@ -719,7 +719,7 @@

    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/CustomerRole/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/CustomerRole/Edit.cshtml index 77861faa53..a9d5fe0350 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/CustomerRole/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/CustomerRole/Edit.cshtml @@ -18,7 +18,7 @@ @T("Admin.Common.SaveContinue")
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/DeliveryTime/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/DeliveryTime/Edit.cshtml index 79ff89dac5..466a0c5d8b 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/DeliveryTime/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/DeliveryTime/Edit.cshtml @@ -17,7 +17,7 @@ @T("Admin.Common.SaveContinue")
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/DeliveryTime/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/DeliveryTime/List.cshtml index 796ded1627..fcc0bdc0c4 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/DeliveryTime/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/DeliveryTime/List.cshtml @@ -3,7 +3,7 @@ }
    - + @T("Admin.Configuration.DeliveryTimes")
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Discount/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Discount/Edit.cshtml index 9bd378f0fb..423fe32eb4 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Discount/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Discount/Edit.cshtml @@ -19,7 +19,7 @@ @T("Admin.Common.SaveContinue") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Discount/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Discount/_CreateOrUpdate.cshtml index d4f58841c8..7aff619be0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Discount/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Discount/_CreateOrUpdate.cshtml @@ -409,7 +409,7 @@ name="deleterequirement{{= discountRequirementId }}" id="deleterequirement{{= discountRequirementId }}" onclick="deleteRequirement({{= discountRequirementId }})"> - +
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/EmailAccount/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/EmailAccount/Edit.cshtml index 215f4acbd4..37f27e47ac 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/EmailAccount/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/EmailAccount/Edit.cshtml @@ -17,7 +17,7 @@ @T("Admin.Common.SaveContinue") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml index 928cefbf41..d7780bc634 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/Edit.cshtml @@ -22,13 +22,13 @@ if (Model.Id != 0) { } - + @T("Admin.Common.Preview") } @@ -36,7 +36,7 @@ @if (Model.LogFileExists) { - + @T("Admin.Configuration.ActivityLog") } @@ -53,7 +53,7 @@ @if (!Model.IsSystemProfile) { } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/EditDeployment.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/EditDeployment.cshtml index a63c46efc1..08c85de8d2 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/EditDeployment.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/EditDeployment.cshtml @@ -22,7 +22,7 @@ @T("Admin.Common.SaveContinue") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml index c0a53dc65a..988403c533 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/Preview.cshtml @@ -19,16 +19,16 @@ @if (Model.LogFileExists) { - + @T("Admin.Configuration.ActivityLog") } @@ -137,7 +137,7 @@ columns.Bound(x => x.AdminComment); }) .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id })) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) .ClientEvents(events => events.OnDataBound("OnDataBound")) .EnableCustomBinding(true)) } @@ -172,7 +172,7 @@ .RightAlign(); }) .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id })) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) .ClientEvents(events => events.OnDataBound("OnDataBound")) .EnableCustomBinding(true)) } @@ -209,7 +209,7 @@ .Centered(); }) .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id })) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) .ClientEvents(events => events.OnDataBound("OnDataBound")) .EnableCustomBinding(true)) } @@ -244,7 +244,7 @@ .Centered(); }) .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id })) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) .ClientEvents(events => events.OnDataBound("OnDataBound")) .EnableCustomBinding(true)) } @@ -278,7 +278,7 @@ columns.Bound(x => x.LastActivityDate); }) .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id })) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) .ClientEvents(events => events.OnDataBound("OnDataBound")) .EnableCustomBinding(true)) } @@ -308,7 +308,7 @@ columns.Bound(x => x.CreatedOn); }) .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id })) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) .ClientEvents(events => events.OnDataBound("OnDataBound")) .EnableCustomBinding(true)) } @@ -344,7 +344,7 @@ columns.Bound(x => x.CreatedOn); }) .Pageable(settings => settings.PageSize(Model.GridPageSize).Position(GridPagerPosition.Both)) - .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id })) + .DataBinding(dataBinding => dataBinding.Ajax().Select("PreviewList", "Export", new { id = Model.Id, totalRecords = Model.TotalRecords })) .ClientEvents(events => events.OnDataBound("OnDataBound")) .EnableCustomBinding(true)) } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/ProfileFileDetails.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/ProfileFileDetails.cshtml index 58b01c5355..e50f06f489 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/ProfileFileDetails.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/ProfileFileDetails.cshtml @@ -49,7 +49,7 @@
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/_CreateOrUpdate.Deployment.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/_CreateOrUpdate.Deployment.cshtml index e312ffa95b..8c60028e15 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/_CreateOrUpdate.Deployment.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/_CreateOrUpdate.Deployment.cshtml @@ -185,7 +185,7 @@ option = $('select[name="@(Html.FieldNameFor(x => x.DeploymentType))"]').find('option[value="' + item.id + '"]'); } - html += ' '; + html += ' '; html += item.text; html += ''; diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/_ProfileList.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/_ProfileList.cshtml index 5b12410204..256bed40e3 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/_ProfileList.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/_ProfileList.cshtml @@ -88,7 +88,7 @@ - + @T("Admin.Configuration.ActivityLog") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Deployment.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Deployment.cshtml index f890ef7440..5a7b260832 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Deployment.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Export/_Tab.Deployment.cshtml @@ -40,7 +40,7 @@
    @Model.FileNamePatternExample @@ -144,7 +144,7 @@ @deployment.Name
    -  @deployment.DeploymentTypeName +  @deployment.DeploymentTypeName
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Forum/EditForum.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Forum/EditForum.cshtml index 11fa1679fa..29a844c8a7 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Forum/EditForum.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Forum/EditForum.cshtml @@ -20,7 +20,7 @@ @T("Admin.Common.SaveContinue") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Forum/EditForumGroup.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Forum/EditForumGroup.cshtml index f0b3ca5909..de3588e0f0 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Forum/EditForumGroup.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Forum/EditForumGroup.cshtml @@ -20,7 +20,7 @@ @T("Admin.Common.SaveContinue") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/Edit.cshtml index 22da56cf86..640f4f3bdc 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/Edit.cshtml @@ -18,7 +18,7 @@ @T("Admin.Common.SaveContinue") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/_CreateOrUpdate.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/_CreateOrUpdate.cshtml index 14c43c7f65..06a72ac536 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/_CreateOrUpdate.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/GiftCard/_CreateOrUpdate.cshtml @@ -116,7 +116,7 @@
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Home/Index.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Home/Index.cshtml index 750aa84167..f8c5f0b314 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Home/Index.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Home/Index.cshtml @@ -4,7 +4,7 @@
    - + @T("Admin.Dashboard.StoreStatistics")
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Home/UaTester.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Home/UaTester.cshtml index 95af86f0b8..f4c0bc92fb 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Home/UaTester.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Home/UaTester.cshtml @@ -7,7 +7,7 @@
    - + User Agent Tester
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Import/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Import/Edit.cshtml index 0889579148..f4f7e253bd 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Import/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Import/Edit.cshtml @@ -17,14 +17,14 @@ @if (Model.Id != 0) { } @if (Model.LogFileExists) { - + @T("Admin.Configuration.ActivityLog") } @@ -36,7 +36,7 @@ @T("Admin.Common.SaveContinue") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Import/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Import/List.cshtml index 2f1d5fdc18..a01b8aa3c2 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Import/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Import/List.cshtml @@ -93,7 +93,7 @@ - + @T("Admin.Configuration.ActivityLog") diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Import/_ColumnMappings.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Import/_ColumnMappings.cshtml index 5d00bffdf5..27941ed1cf 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Import/_ColumnMappings.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Import/_ColumnMappings.cshtml @@ -25,7 +25,7 @@ @if (Model.ColumnMappings.Any()) { } @@ -119,7 +119,7 @@ @@ -410,7 +410,7 @@ leftLabel.attr('title', ''); } - context.find('i').removeClass('fa-globe fa-unlink').addClass(isLocalized ? 'fa-globe' : 'fa-unlink'); + context.find('i').removeClass('fa-globe fa-chain-broken').addClass(isLocalized ? 'fa-globe' : 'fa-chain-broken'); } function initSelectBox(element, isProperty) { @@ -478,7 +478,7 @@ if (option.length === 0) { html += '
    '; html += ''; - html += ' '; + html += ' '; html += ''; } else { @@ -501,7 +501,7 @@ html += ''; html += '' : ' text-warning" />'); html += ''; } diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Language/Edit.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Language/Edit.cshtml index 98fbea9085..5e37409a78 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Language/Edit.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Language/Edit.cshtml @@ -27,7 +27,7 @@ @T("Admin.Configuration.Languages.Import")
    diff --git a/src/Presentation/SmartStore.Web/Administration/Views/Language/List.cshtml b/src/Presentation/SmartStore.Web/Administration/Views/Language/List.cshtml index 9f7a2db4d1..af4be8644d 100644 --- a/src/Presentation/SmartStore.Web/Administration/Views/Language/List.cshtml +++ b/src/Presentation/SmartStore.Web/Administration/Views/Language/List.cshtml @@ -2,8 +2,6 @@ @model List @{ ViewBag.Title = T("Admin.Configuration.Languages").Text; - - var defaultLanguageNote = ViewBag.DefaultLanguageNote as string; }
    @@ -20,12 +18,6 @@
    @T("Admin.Configuration.Languages.InstalledLanguages")
    - @if (defaultLanguageNote.HasValue()) - { -
    - @Html.Raw(defaultLanguageNote) -
    - }
    @@ -100,7 +92,10 @@ +@using (Html.BeginZoneContent("end")) +{ + +} \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Byte.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Byte.cshtml index e2c1ba5f6d..c7360886a3 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Byte.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Byte.cshtml @@ -14,15 +14,23 @@ } } + private string Postfix + { + get + { + return ViewData["postfix"] as string; + } + } + private string CssClass { get { var cls = "numerictextbox-group flex-grow-1"; - if (TryGetMetadata("size", out var size)) + if (ViewData.ContainsKey("size")) { - cls += " numerictextbox-group-" + size; + cls += " numerictextbox-group-" + ViewData["size"].Convert(); } return cls; @@ -34,14 +42,14 @@ @(Html.Telerik().NumericTextBox() .Name(ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty)) .EmptyMessage(T("Common.EnterValue")) - .MinValue(GetMetadata("min") ?? 0) - .MaxValue(GetMetadata("max") ?? 255) - .IncrementStep(GetMetadata("step") ?? 1) + .MinValue(0) + .MaxValue(255) + .IncrementStep(ViewData["step"].Convert() ?? 1) .Value(Value) ) - @if (TryGetMetadata("postfix", out var postfix)) + @if (Postfix.HasValue()) { - @postfix + @Postfix } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/DateTime.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/DateTime.cshtml index 62f3df9680..90b7d17811 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/DateTime.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/DateTime.cshtml @@ -59,7 +59,7 @@
    @Html.TextBox("", value, new { @class = "form-control datetimepicker-input", data_target = "#" + id + "-parent", data_format = format })
    - +
    diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Decimal.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Decimal.cshtml index 09129ae333..efce4fc607 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Decimal.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Decimal.cshtml @@ -14,15 +14,23 @@ } } + private string Postfix + { + get + { + return ViewData["postfix"] as string; + } + } + private string CssClass { get { var cls = "numerictextbox-group flex-grow-1"; - if (TryGetMetadata("size", out var size)) + if (ViewData.ContainsKey("size")) { - cls += " numerictextbox-group-" + size; + cls += " numerictextbox-group-" + ViewData["size"].Convert(); } return cls; @@ -35,15 +43,15 @@ .Name(ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty)) .EmptyMessage(T("Common.EnterValue")) .Value(Value) - .MinValue(GetMetadata("min")) - .MaxValue(GetMetadata("max")) - .IncrementStep(GetMetadata("step") ?? 1) + .MinValue(ViewData["min"].Convert()) + .MaxValue(ViewData["max"].Convert()) + .IncrementStep(ViewData["step"].Convert() ?? 1) .DecimalDigits(4) //always display 4 digits ) - @if (TryGetMetadata("postfix", out var postfix)) + @if (Postfix.HasValue()) { - @postfix + @Postfix } diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Double.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Double.cshtml index e82db60a99..2396030733 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Double.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Double.cshtml @@ -14,15 +14,23 @@ } } + private string Postfix + { + get + { + return ViewData["postfix"] as string; + } + } + private string CssClass { get { var cls = "numerictextbox-group flex-grow-1"; - if (TryGetMetadata("size", out var size)) + if (ViewData.ContainsKey("size")) { - cls += " numerictextbox-group-" + size; + cls += " numerictextbox-group-" + ViewData["size"].Convert(); } return cls; @@ -35,15 +43,15 @@ .Name(ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty)) .EmptyMessage(T("Common.EnterValue")) .Value(Value) - .MinValue(GetMetadata("min")) - .MaxValue(GetMetadata("max")) - .IncrementStep(GetMetadata("step") ?? 1) + .MinValue(ViewData["min"].Convert()) + .MaxValue(ViewData["max"].Convert()) + .IncrementStep(ViewData["step"].Convert() ?? 1) .DecimalDigits(4) //always display 4 digits ) - @if (TryGetMetadata("postfix", out var postfix)) + @if (Postfix.HasValue()) { - @postfix + @Postfix } diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/FontAwesomeIcon.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/FontAwesomeIcon.cshtml new file mode 100644 index 0000000000..e04f9abb4f --- /dev/null +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/FontAwesomeIcon.cshtml @@ -0,0 +1,45 @@ +@using Newtonsoft.Json.Linq; +@using SmartStore.Web.Framework; + +@functions{ + private string Value + { + get + { + string value = null; + if (ViewData.Model != null) + { + value = ViewData.Model; + } + return value; + } + } + + private List Icons + { + get + { + var list = new List(); + var jsonIcons = JObject.Parse(@"{ '4.7.0':['fa-500px','fa-address-book','fa-address-book-o','fa-address-card','fa-address-card-o','fa-adjust','fa-adn','fa-align-center','fa-align-justify','fa-align-left','fa-align-right','fa-amazon','fa-ambulance','fa-american-sign-language-interpreting','fa-anchor','fa-android','fa-angellist','fa-angle-double-down','fa-angle-double-left','fa-angle-double-right','fa-angle-double-up','fa-angle-down','fa-angle-left','fa-angle-right','fa-angle-up','fa-apple','fa-archive','fa-area-chart','fa-arrow-circle-down','fa-arrow-circle-left','fa-arrow-circle-o-down','fa-arrow-circle-o-left','fa-arrow-circle-o-right','fa-arrow-circle-o-up','fa-arrow-circle-right','fa-arrow-circle-up','fa-arrow-down','fa-arrow-left','fa-arrow-right','fa-arrow-up','fa-arrows','fa-arrows-alt','fa-arrows-h','fa-arrows-v','fa-asl-interpreting','fa-assistive-listening-systems','fa-asterisk','fa-at','fa-audio-description','fa-automobile','fa-backward','fa-balance-scale','fa-ban','fa-bandcamp','fa-bank','fa-bar-chart','fa-bar-chart-o','fa-barcode','fa-bars','fa-bath','fa-bathtub','fa-battery','fa-battery-0','fa-battery-1','fa-battery-2','fa-battery-3','fa-battery-4','fa-battery-empty','fa-battery-full','fa-battery-half','fa-battery-quarter','fa-battery-three-quarters','fa-bed','fa-beer','fa-behance','fa-behance-square','fa-bell','fa-bell-o','fa-bell-slash','fa-bell-slash-o','fa-bicycle','fa-binoculars','fa-birthday-cake','fa-bitbucket','fa-bitbucket-square','fa-bitcoin','fa-black-tie','fa-blind','fa-bluetooth','fa-bluetooth-b','fa-bold','fa-bolt','fa-bomb','fa-book','fa-bookmark','fa-bookmark-o','fa-braille','fa-briefcase','fa-btc','fa-bug','fa-building','fa-building-o','fa-bullhorn','fa-bullseye','fa-bus','fa-buysellads','fa-cab','fa-calculator','fa-calendar','fa-calendar-check-o','fa-calendar-minus-o','fa-calendar-o','fa-calendar-plus-o','fa-calendar-times-o','fa-camera','fa-camera-retro','fa-car','fa-caret-down','fa-caret-left','fa-caret-right','fa-caret-square-o-down','fa-caret-square-o-left','fa-caret-square-o-right','fa-caret-square-o-up','fa-caret-up','fa-cart-arrow-down','fa-cart-plus','fa-cc','fa-cc-amex','fa-cc-diners-club','fa-cc-discover','fa-cc-jcb','fa-cc-mastercard','fa-cc-paypal','fa-cc-stripe','fa-cc-visa','fa-certificate','fa-chain','fa-chain-broken','fa-check','fa-check-circle','fa-check-circle-o','fa-check-square','fa-check-square-o','fa-chevron-circle-down','fa-chevron-circle-left','fa-chevron-circle-right','fa-chevron-circle-up','fa-chevron-down','fa-chevron-left','fa-chevron-right','fa-chevron-up','fa-child','fa-chrome','fa-circle','fa-circle-o','fa-circle-o-notch','fa-circle-thin','fa-clipboard','fa-clock-o','fa-clone','fa-close','fa-cloud','fa-cloud-download','fa-cloud-upload','fa-cny','fa-code','fa-code-fork','fa-codepen','fa-codiepie','fa-coffee','fa-cog','fa-cogs','fa-columns','fa-comment','fa-comment-o','fa-commenting','fa-commenting-o','fa-comments','fa-comments-o','fa-compass','fa-compress','fa-connectdevelop','fa-contao','fa-copy','fa-copyright','fa-creative-commons','fa-credit-card','fa-credit-card-alt','fa-crop','fa-crosshairs','fa-css3','fa-cube','fa-cubes','fa-cut','fa-cutlery','fa-dashboard','fa-dashcube','fa-database','fa-deaf','fa-deafness','fa-dedent','fa-delicious','fa-desktop','fa-deviantart','fa-diamond','fa-digg','fa-dollar','fa-dot-circle-o','fa-download','fa-dribbble','fa-drivers-license','fa-drivers-license-o','fa-dropbox','fa-drupal','fa-edge','fa-edit','fa-eercast','fa-eject','fa-ellipsis-h','fa-ellipsis-v','fa-empire','fa-envelope','fa-envelope-o','fa-envelope-open','fa-envelope-open-o','fa-envelope-square','fa-envira','fa-eraser','fa-etsy','fa-eur','fa-euro','fa-exchange','fa-exclamation','fa-exclamation-circle','fa-exclamation-triangle','fa-expand','fa-expeditedssl','fa-external-link','fa-external-link-square','fa-eye','fa-eye-slash','fa-eyedropper','fa-fa','fa-facebook','fa-facebook-f','fa-facebook-official','fa-facebook-square','fa-fast-backward','fa-fast-forward','fa-fax','fa-feed','fa-female','fa-fighter-jet','fa-file','fa-file-archive-o','fa-file-audio-o','fa-file-code-o','fa-file-excel-o','fa-file-image-o','fa-file-movie-o','fa-file-o','fa-file-pdf-o','fa-file-photo-o','fa-file-picture-o','fa-file-powerpoint-o','fa-file-sound-o','fa-file-text','fa-file-text-o','fa-file-video-o','fa-file-word-o','fa-file-zip-o','fa-files-o','fa-film','fa-filter','fa-fire','fa-fire-extinguisher','fa-firefox','fa-first-order','fa-flag','fa-flag-checkered','fa-flag-o','fa-flash','fa-flask','fa-flickr','fa-floppy-o','fa-folder','fa-folder-o','fa-folder-open','fa-folder-open-o','fa-font','fa-font-awesome','fa-fonticons','fa-fort-awesome','fa-forumbee','fa-forward','fa-foursquare','fa-free-code-camp','fa-frown-o','fa-futbol-o','fa-gamepad','fa-gavel','fa-gbp','fa-ge','fa-gear','fa-gears','fa-genderless','fa-get-pocket','fa-gg','fa-gg-circle','fa-gift','fa-git','fa-git-square','fa-github','fa-github-alt','fa-github-square','fa-gitlab','fa-gittip','fa-glass','fa-glide','fa-glide-g','fa-globe','fa-google','fa-google-plus','fa-google-plus-circle','fa-google-plus-official','fa-google-plus-square','fa-google-wallet','fa-graduation-cap','fa-gratipay','fa-grav','fa-group','fa-h-square','fa-hacker-news','fa-hand-grab-o','fa-hand-lizard-o','fa-hand-o-down','fa-hand-o-left','fa-hand-o-right','fa-hand-o-up','fa-hand-paper-o','fa-hand-peace-o','fa-hand-pointer-o','fa-hand-rock-o','fa-hand-scissors-o','fa-hand-spock-o','fa-hand-stop-o','fa-handshake-o','fa-hard-of-hearing','fa-hashtag','fa-hdd-o','fa-header','fa-headphones','fa-heart','fa-heart-o','fa-heartbeat','fa-history','fa-home','fa-hospital-o','fa-hotel','fa-hourglass','fa-hourglass-1','fa-hourglass-2','fa-hourglass-3','fa-hourglass-end','fa-hourglass-half','fa-hourglass-o','fa-hourglass-start','fa-houzz','fa-html5','fa-i-cursor','fa-id-badge','fa-id-card','fa-id-card-o','fa-ils','fa-image','fa-imdb','fa-inbox','fa-indent','fa-industry','fa-info','fa-info-circle','fa-inr','fa-instagram','fa-institution','fa-internet-explorer','fa-intersex','fa-ioxhost','fa-italic','fa-joomla','fa-jpy','fa-jsfiddle','fa-key','fa-keyboard-o','fa-krw','fa-language','fa-laptop','fa-lastfm','fa-lastfm-square','fa-leaf','fa-leanpub','fa-legal','fa-lemon-o','fa-level-down','fa-level-up','fa-life-bouy','fa-life-buoy','fa-life-ring','fa-life-saver','fa-lightbulb-o','fa-line-chart','fa-link','fa-linkedin','fa-linkedin-square','fa-linode','fa-linux','fa-list','fa-list-alt','fa-list-ol','fa-list-ul','fa-location-arrow','fa-lock','fa-long-arrow-down','fa-long-arrow-left','fa-long-arrow-right','fa-long-arrow-up','fa-low-vision','fa-magic','fa-magnet','fa-mail-forward','fa-mail-reply','fa-mail-reply-all','fa-male','fa-map','fa-map-marker','fa-map-o','fa-map-pin','fa-map-signs','fa-mars','fa-mars-double','fa-mars-stroke','fa-mars-stroke-h','fa-mars-stroke-v','fa-maxcdn','fa-meanpath','fa-medium','fa-medkit','fa-meetup','fa-meh-o','fa-mercury','fa-microchip','fa-microphone','fa-microphone-slash','fa-minus','fa-minus-circle','fa-minus-square','fa-minus-square-o','fa-mixcloud','fa-mobile','fa-mobile-phone','fa-modx','fa-money','fa-moon-o','fa-mortar-board','fa-motorcycle','fa-mouse-pointer','fa-music','fa-navicon','fa-neuter','fa-newspaper-o','fa-object-group','fa-object-ungroup','fa-odnoklassniki','fa-odnoklassniki-square','fa-opencart','fa-openid','fa-opera','fa-optin-monster','fa-outdent','fa-pagelines','fa-paint-brush','fa-paper-plane','fa-paper-plane-o','fa-paperclip','fa-paragraph','fa-paste','fa-pause','fa-pause-circle','fa-pause-circle-o','fa-paw','fa-paypal','fa-pencil','fa-pencil-square','fa-pencil-square-o','fa-percent','fa-phone','fa-phone-square','fa-photo','fa-picture-o','fa-pie-chart','fa-pied-piper','fa-pied-piper-alt','fa-pied-piper-pp','fa-pinterest','fa-pinterest-p','fa-pinterest-square','fa-plane','fa-play','fa-play-circle','fa-play-circle-o','fa-plug','fa-plus','fa-plus-circle','fa-plus-square','fa-plus-square-o','fa-podcast','fa-power-off','fa-print','fa-product-hunt','fa-puzzle-piece','fa-qq','fa-qrcode','fa-question','fa-question-circle','fa-question-circle-o','fa-quora','fa-quote-left','fa-quote-right','fa-ra','fa-random','fa-ravelry','fa-rebel','fa-recycle','fa-reddit','fa-reddit-alien','fa-reddit-square','fa-refresh','fa-registered','fa-remove','fa-renren','fa-reorder','fa-repeat','fa-reply','fa-reply-all','fa-resistance','fa-retweet','fa-rmb','fa-road','fa-rocket','fa-rotate-left','fa-rotate-right','fa-rouble','fa-rss','fa-rss-square','fa-rub','fa-ruble','fa-rupee','fa-s15','fa-safari','fa-save','fa-scissors','fa-scribd','fa-search','fa-search-minus','fa-search-plus','fa-sellsy','fa-send','fa-send-o','fa-server','fa-share','fa-share-alt','fa-share-alt-square','fa-share-square','fa-share-square-o','fa-shekel','fa-sheqel','fa-shield','fa-ship','fa-shirtsinbulk','fa-shopping-bag','fa-shopping-basket','fa-shopping-cart','fa-shower','fa-sign-in','fa-sign-language','fa-sign-out','fa-signal','fa-signing','fa-simplybuilt','fa-sitemap','fa-skyatlas','fa-skype','fa-slack','fa-sliders','fa-slideshare','fa-smile-o','fa-snapchat','fa-snapchat-ghost','fa-snapchat-square','fa-snowflake-o','fa-soccer-ball-o','fa-sort','fa-sort-alpha-asc','fa-sort-alpha-desc','fa-sort-amount-asc','fa-sort-amount-desc','fa-sort-asc','fa-sort-desc','fa-sort-down','fa-sort-numeric-asc','fa-sort-numeric-desc','fa-sort-up','fa-soundcloud','fa-space-shuttle','fa-spinner','fa-spoon','fa-spotify','fa-square','fa-square-o','fa-stack-exchange','fa-stack-overflow','fa-star','fa-star-half','fa-star-half-empty','fa-star-half-full','fa-star-half-o','fa-star-o','fa-steam','fa-steam-square','fa-step-backward','fa-step-forward','fa-stethoscope','fa-sticky-note','fa-sticky-note-o','fa-stop','fa-stop-circle','fa-stop-circle-o','fa-street-view','fa-strikethrough','fa-stumbleupon','fa-stumbleupon-circle','fa-subscript','fa-subway','fa-suitcase','fa-sun-o','fa-superpowers','fa-superscript','fa-support','fa-table','fa-tablet','fa-tachometer','fa-tag','fa-tags','fa-tasks','fa-taxi','fa-telegram','fa-television','fa-tencent-weibo','fa-terminal','fa-text-height','fa-text-width','fa-th','fa-th-large','fa-th-list','fa-themeisle','fa-thermometer','fa-thermometer-0','fa-thermometer-1','fa-thermometer-2','fa-thermometer-3','fa-thermometer-4','fa-thermometer-empty','fa-thermometer-full','fa-thermometer-half','fa-thermometer-quarter','fa-thermometer-three-quarters','fa-thumb-tack','fa-thumbs-down','fa-thumbs-o-down','fa-thumbs-o-up','fa-thumbs-up','fa-ticket','fa-times','fa-times-circle','fa-times-circle-o','fa-times-rectangle','fa-times-rectangle-o','fa-tint','fa-toggle-down','fa-toggle-left','fa-toggle-off','fa-toggle-on','fa-toggle-right','fa-toggle-up','fa-trademark','fa-train','fa-transgender','fa-transgender-alt','fa-trash','fa-trash-o','fa-tree','fa-trello','fa-tripadvisor','fa-trophy','fa-truck','fa-try','fa-tty','fa-tumblr','fa-tumblr-square','fa-turkish-lira','fa-tv','fa-twitch','fa-twitter','fa-twitter-square','fa-umbrella','fa-underline','fa-undo','fa-universal-access','fa-university','fa-unlink','fa-unlock','fa-unlock-alt','fa-unsorted','fa-upload','fa-usb','fa-usd','fa-user','fa-user-circle','fa-user-circle-o','fa-user-md','fa-user-o','fa-user-plus','fa-user-secret','fa-user-times','fa-users','fa-vcard','fa-vcard-o','fa-venus','fa-venus-double','fa-venus-mars','fa-viacoin','fa-viadeo','fa-viadeo-square','fa-video-camera','fa-vimeo','fa-vimeo-square','fa-vine','fa-vk','fa-volume-control-phone','fa-volume-down','fa-volume-off','fa-volume-up','fa-warning','fa-wechat','fa-weibo','fa-weixin','fa-whatsapp','fa-wheelchair','fa-wheelchair-alt','fa-wifi','fa-wikipedia-w','fa-window-close','fa-window-close-o','fa-window-maximize','fa-window-minimize','fa-window-restore','fa-windows','fa-won','fa-wordpress','fa-wpbeginner','fa-wpexplorer','fa-wpforms','fa-wrench','fa-xing','fa-xing-square','fa-y-combinator','fa-y-combinator-square','fa-yahoo','fa-yc','fa-yc-square','fa-yelp','fa-yen','fa-yoast','fa-youtube','fa-youtube-play','fa-youtube-square'] }"); + + foreach (var icon in jsonIcons.First.Children().First()) + { + list.Add(new SelectListItem { Text = icon.ToString(), Value = icon.ToString(), Selected = Value.HasValue() ? Value.Equals(icon.ToString()) : false }); + } + + return list; + } + } +} + +@{ + var id = ViewData.TemplateInfo.GetFullHtmlFieldId(string.Empty); + var name = ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty); +} + + diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Html.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Html.cshtml index 1f7db1659f..214c140f24 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Html.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Html.cshtml @@ -41,7 +41,7 @@ @if (htmlIsEmpty) {
    - + @T("HtmlEditor.ClickToEdit")
    } diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Int32.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Int32.cshtml index 091cb0dc19..166826c1c8 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Int32.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Int32.cshtml @@ -14,15 +14,23 @@ } } + private string Postfix + { + get + { + return ViewData["postfix"] as string; + } + } + private string CssClass { get { var cls = "numerictextbox-group flex-grow-1"; - if (TryGetMetadata("size", out var size)) + if (ViewData.ContainsKey("size")) { - cls += " numerictextbox-group-" + size; + cls += " numerictextbox-group-" + ViewData["size"].Convert(); } return cls; @@ -34,14 +42,14 @@ @(Html.Telerik().IntegerTextBox() .Name(ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty)) .EmptyMessage(T("Common.EnterValue")) - .MinValue(GetMetadata("min")) - .MaxValue(GetMetadata("max")) - .IncrementStep(GetMetadata("step") ?? 1) + .MinValue(ViewData["min"].Convert()) + .MaxValue(ViewData["max"].Convert()) + .IncrementStep(ViewData["step"].Convert() ?? 1) .Value(Value) ) - @if (TryGetMetadata("postfix", out var postfix)) + @if (Postfix.HasValue()) { - @postfix + @Postfix } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/QtyInput.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/QtyInput.cshtml index 9d7443cf63..f801f6ac14 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/QtyInput.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/QtyInput.cshtml @@ -40,7 +40,7 @@
    @Html.DropDownListFor(model => Model.EnteredQuantity, Model.AllowedQuantities, new { @class = "form-control qty-dropdown noskin" })
    diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Range.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Range.cshtml index dd3630406f..ee7ccb2199 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Range.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Range.cshtml @@ -23,11 +23,13 @@ var id = ViewData.TemplateInfo.GetFullHtmlFieldId(string.Empty); var name = ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty); - var min = (GetMetadata("min") ?? 0m).ToString(CultureInfo.InvariantCulture); - var max = (GetMetadata("max") ?? 100m).ToString(CultureInfo.InvariantCulture); - var step = (GetMetadata("step") ?? 1m).ToString(CultureInfo.InvariantCulture); - var ticks = GetMetadata("ticks").SplitSafe(",").Select(x => x.Trim()).ToArray(); - var format = GetMetadata("format") ?? "{0}"; + var metadataValues = ViewData.ModelMetadata.AdditionalValues; + + var min = (ViewData["min"].Convert() ?? metadataValues.Get("min").Convert() ?? 0m).ToString(CultureInfo.InvariantCulture); + var max = (ViewData["max"].Convert() ?? metadataValues.Get("max").Convert() ?? 100m).ToString(CultureInfo.InvariantCulture); + var step = (ViewData["step"].Convert() ?? metadataValues.Get("step").Convert() ?? 1m).ToString(CultureInfo.InvariantCulture); + var ticks = (ViewData["ticks"].Convert() ?? metadataValues.Get("ticks").Convert()).SplitSafe(",").Select(x => x.Trim()).ToArray(); + var format = ViewData["format"].Convert() ?? metadataValues.Get("format").Convert() ?? "{0}"; var invariantValue = ViewData.Model.Convert().ToString(CultureInfo.InvariantCulture); } diff --git a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Time.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Time.cshtml index 4cf02ee029..6bb6dfac23 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Time.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/EditorTemplates/Time.cshtml @@ -40,7 +40,7 @@
    @Html.TextBox("", value, new { @class = "form-control datetimepicker-input", data_target = "#" + id + "-parent", data_format = "LT" })
    - +
    diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Layouts/_Document.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Layouts/_Document.cshtml index af9251b06f..4a16d4be4d 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/Layouts/_Document.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/Layouts/_Document.cshtml @@ -1,19 +1,88 @@ @using SmartStore.Core; +@{ + var jsRoot = "~/Scripts/"; + var vendorsRoot = "~/Content/vendors/"; + var contentRoot = "~/Content/"; + + // add css assets + Html.AddCssFileParts( + contentRoot + "fontastic/fontastic.css", + vendorsRoot + "font-awesome/font-awesome.css", + vendorsRoot + "pnotify/css/pnotify.css", + vendorsRoot + "pnotify/css/pnotify.mobile.css", + vendorsRoot + "pnotify/css/pnotify.buttons.css"); + + Html.AppendScriptParts(ResourceLocation.Head, + vendorsRoot + "modernizr/modernizr.js", + vendorsRoot + "jquery/jquery-3.2.1.js"/*, + vendorsRoot + "jquery/jquery-migrate-3.0.0.js"*/); + + Html.AppendScriptParts(ResourceLocation.Foot, + // Vendors + vendorsRoot + "underscore/underscore.js", + vendorsRoot + "underscore/underscore.string.js", + vendorsRoot + "jquery/jquery.addeasing.js", + vendorsRoot + "jquery-ui/effect.js", + vendorsRoot + "jquery-ui/effect-shake.js", + vendorsRoot + "jquery/jquery.unobtrusive-ajax.js", + vendorsRoot + "jquery/jquery.validate.js", + vendorsRoot + "jquery/jquery.validate.unobtrusive.js", + vendorsRoot + "jquery/jquery.ba-outside-events.js", + vendorsRoot + "jquery/jquery.scrollTo.js", + vendorsRoot + "moment/moment.js", + vendorsRoot + "datetimepicker/js/tempusdominus-bootstrap-4.js", + vendorsRoot + "select2/js/select2.js", + vendorsRoot + "pnotify/js/pnotify.js", + vendorsRoot + "pnotify/js/pnotify.mobile.js", + vendorsRoot + "pnotify/js/pnotify.buttons.js", + vendorsRoot + "pnotify/js/pnotify.animate.js", + vendorsRoot + "slick/slick.js", + vendorsRoot + "touchspin/jquery.bootstrap-touchspin.js", + vendorsRoot + "aos/js/aos.js", + contentRoot + "bs4/js/bootstrap.bundle.js", + // Common + jsRoot + "underscore.mixins.js", + jsRoot + "smartstore.system.js", + jsRoot + "smartstore.touchevents.js", + jsRoot + "smartstore.jquery.utils.js", + jsRoot + "smartstore.globalization.js", + jsRoot + "jquery.validate.unobtrusive.custom.js", + jsRoot + "smartstore.viewport.js", + jsRoot + "smartstore.doajax.js", + jsRoot + "smartstore.eventbroker.js", + jsRoot + "smartstore.hacks.js", + jsRoot + "smartstore.common.js", + jsRoot + "smartstore.selectwrapper.js", + jsRoot + "smartstore.throbber.js", + jsRoot + "smartstore.thumbzoomer.js", + jsRoot + "smartstore.responsiveNav.js", + jsRoot + "smartstore.keynav.js", + jsRoot + "smartstore.articlelist.js", + jsRoot + "smartstore.megamenu.js", + jsRoot + "smartstore.offcanvas.js", + jsRoot + "smartstore.parallax.js", + // Shop + jsRoot + "public.common.js", + jsRoot + "public.search.js", + jsRoot + "public.offcanvas-cart.js", + jsRoot + "public.offcanvas-menu.js", + jsRoot + "public.product.js"); + + //Html.AddBodyCssClass(this.WorkContext.WorkingLanguage.Rtl ? "rtl" : ""); +} - @Html.SmartTitle(true) + @Html.SmartTitle(true) - - - + + + - @{ Html.RenderPartial("_Assets"); } - - @*This is used so that themes can inject content into the header*@ + @*This is used so that themes can inject content into the header*@ @{ Html.RenderPartial("Head"); } @Html.SmartMetaRobots() @@ -21,36 +90,36 @@ @{ Html.RenderPartial("_ClientRes"); } @{ Html.RenderPartial("_GoogleFonts"); } - @Html.SmartCssFiles(this.Url, ResourceLocation.Head) - @Html.SmartScripts(this.Url, ResourceLocation.Head) + @Html.SmartCssFiles(this.Url, ResourceLocation.Head) + @Html.SmartScripts(this.Url, ResourceLocation.Head) - @{ Html.RenderWidget("head_html_tag"); } + @{ Html.RenderWidget("head_html_tag"); } - @Html.CanonicalUrls() + @Html.CanonicalUrls() @Html.LinkRels() - @{ + @{ Html.RenderAction("RssHeaderLink", "News", new { area = "" }); Html.RenderAction("RssHeaderLink", "Blog", new { area = "" }); } - @*Favicon - upload favicon.ico or favicon-[StoreId].ico file either to the root web or your theme directory*@ + @*Favicon - upload favicon.ico or favicon-[StoreId].ico file either to the root web or your theme directory*@ @{ Html.RenderAction("Favicon", "Common", new { area = "" }); } - + @Html.CustomHead() @{ Html.RenderZone("head"); } - + - + @{ Html.RenderZone("start"); } - @RenderBody() + @RenderBody() @{ Html.RenderZone("aftercontent"); } - - @Html.SmartCssFiles(this.Url, ResourceLocation.Foot) - @Html.SmartScripts(this.Url, ResourceLocation.Foot) - @Html.LocalizationScript(WorkContext.WorkingLanguage.UniqueSeoCode, "~/Content/vendors/select2/js/i18n", "*.js", null) - @Html.LocalizationScript(WorkContext.WorkingLanguage.UniqueSeoCode, "~/Content/vendors/moment/locale", "*.js", null) + + @Html.SmartCssFiles(this.Url, ResourceLocation.Foot) + @Html.SmartScripts(this.Url, ResourceLocation.Foot) + @Html.LocalizationScript(WorkContext.WorkingLanguage.UniqueSeoCode, vendorsRoot + "select2/js/i18n", "*.js", null) + @Html.LocalizationScript(WorkContext.WorkingLanguage.UniqueSeoCode, vendorsRoot + "moment/locale", "*.js", null) @{ Html.RenderZone("end"); } diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Layouts/_Layout.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Layouts/_Layout.cshtml index f0d14c3065..d4e3d1424f 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/Layouts/_Layout.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/Layouts/_Layout.cshtml @@ -1,32 +1,32 @@ @{ - Layout = "_Document"; - - Html.RenderWidget("body_start_html_tag_after"); - - var hasLeftCol = IsSectionDefined("left"); - var hasRightCol = IsSectionDefined("right"); - var hideLeftCol = ViewBag.HideLeftCol == true; - var hideRightCol = ViewBag.HideRightCol == true; - - int mainColspan = 12; - int cols = 1; - if (hasLeftCol) - { - mainColspan -= 3; - cols += 1; - } - if (hasRightCol) - { - mainColspan -= 3; - cols += 1; - } - - Html.AddBodyCssClass("lyt-cols-" + cols); - - if (GetThemeVariable("boxed")) - { - Html.AddBodyCssClass("boxed"); - } + Layout = "_Document"; + + Html.RenderWidget("body_start_html_tag_after"); + + var hasLeftCol = IsSectionDefined("left"); + var hasRightCol = IsSectionDefined("right"); + var hideLeftCol = ViewBag.HideLeftCol == true; + var hideRightCol = ViewBag.HideRightCol == true; + + int mainColspan = 12; + int cols = 1; + if (hasLeftCol) + { + mainColspan -= 3; + cols += 1; + } + if (hasRightCol) + { + mainColspan -= 3; + cols += 1; + } + + Html.AddBodyCssClass("lyt-cols-" + cols); + + if (GetThemeVariable("boxed")) + { + Html.AddBodyCssClass("boxed"); + } }
    @@ -43,14 +43,14 @@
    @{ - Html.RenderAction("ShopBar", "Common", new { area = "" }); + Html.RenderAction("ShopBar", "Common", new { area = "" }); }
    @@ -58,7 +58,7 @@ @@ -66,19 +66,20 @@ } + @{ + Html.RenderAction("JavaScriptDisabledWarning", "Common", new { area = "" }); + Html.RenderWidget("content_before"); + } +
    - @{ - Html.RenderAction("JavaScriptDisabledWarning", "Common", new { area = "" }); - Html.RenderWidget("content_before"); - } -
    +
    @{ @RenderSection("teaser", required: false); Html.RenderAction("Breadcrumb", "Common", new { area = "" }); Html.RenderZone("teaser"); Html.RenderWidget("teaser"); } -
    +
    @if (hasLeftCol) { @@ -110,12 +111,16 @@ } - @{ Html.RenderWidget("content_after"); } + @{ + Html.RenderWidget("content_after"); + }
    - @{ Html.RenderWidget("footer_before"); } + @{ + Html.RenderWidget("footer_before"); + } @if (IsSectionDefined("footer")) { @@ -124,19 +129,28 @@ else {
    - @{ Html.RenderAction("Footer", "Common", new { area = "" }); } + @{ +Html.RenderAction("Footer", "Common", new { area = "" }); + }
    } - @{ Html.RenderZone("page-end"); } + @{ + Html.RenderZone("page-end"); + }
    - - + + -@{ Html.RenderPartial("_Notifications"); } -@{ Html.RenderWidget("body_end_html_tag_before"); } +@{ + Html.RenderPartial("_Notifications"); +} + +@{ + Html.RenderWidget("body_end_html_tag_before"); +} diff --git a/src/Presentation/SmartStore.Web/Views/Shared/Partials/Product.List.FilterSort.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/Partials/Product.List.FilterSort.cshtml index 952f0ebe06..a389a8b8fc 100644 --- a/src/Presentation/SmartStore.Web/Views/Shared/Partials/Product.List.FilterSort.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Shared/Partials/Product.List.FilterSort.cshtml @@ -9,7 +9,7 @@
    @@ -24,7 +24,7 @@ @Model.CurrentSortOrderName @T("Products.SortByX", "" + Model.CurrentSortOrderName + "") - + @{ diff --git a/src/Presentation/SmartStore.Web/Views/Shared/_Layout.cshtml b/src/Presentation/SmartStore.Web/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000..ad42acf451 --- /dev/null +++ b/src/Presentation/SmartStore.Web/Views/Shared/_Layout.cshtml @@ -0,0 +1,40 @@ + + + + + + @ViewBag.Title - My ASP.NET Application + + + + + + + +
    + @RenderBody() +
    +
    +

    © @DateTime.Now.Year - My ASP.NET Application

    +
    +
    + + + + + \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Views/ShoppingCart/Partials/CartItems.cshtml b/src/Presentation/SmartStore.Web/Views/ShoppingCart/Partials/CartItems.cshtml index cc48625fcf..ece6a5a144 100644 --- a/src/Presentation/SmartStore.Web/Views/ShoppingCart/Partials/CartItems.cshtml +++ b/src/Presentation/SmartStore.Web/Views/ShoppingCart/Partials/CartItems.cshtml @@ -131,7 +131,7 @@ data-name="@item.ProductName" data-type="wishlist" data-action="addfromcart"> - + } diff --git a/src/Presentation/SmartStore.Web/Views/ShoppingCart/Partials/OffCanvasShoppingCart.cshtml b/src/Presentation/SmartStore.Web/Views/ShoppingCart/Partials/OffCanvasShoppingCart.cshtml index d0b66eddca..90332d9818 100644 --- a/src/Presentation/SmartStore.Web/Views/ShoppingCart/Partials/OffCanvasShoppingCart.cshtml +++ b/src/Presentation/SmartStore.Web/Views/ShoppingCart/Partials/OffCanvasShoppingCart.cshtml @@ -127,7 +127,7 @@ data-name="@item.ProductName" data-type="cart" data-action="addfromcart"> - + } - + diff --git a/src/Presentation/SmartStore.Web/Views/ShoppingCart/Partials/OffCanvasWishlist.cshtml b/src/Presentation/SmartStore.Web/Views/ShoppingCart/Partials/OffCanvasWishlist.cshtml index 626744fc56..29993c2a65 100644 --- a/src/Presentation/SmartStore.Web/Views/ShoppingCart/Partials/OffCanvasWishlist.cshtml +++ b/src/Presentation/SmartStore.Web/Views/ShoppingCart/Partials/OffCanvasWishlist.cshtml @@ -15,10 +15,10 @@ @T("ShoppingCart.Mini.EmptyWishlist.Title")

    - +

    - @T("ShoppingCart.Mini.EmptyWishlist.Info", "fal fa-lg fa-heart") + @T("ShoppingCart.Mini.EmptyWishlist.Info", "fa fa-lg fa-heart-o")

    } @@ -127,7 +127,7 @@ data-type="wishlist" data-action="remove" title='@T("Common.Remove")'> - + diff --git a/src/Presentation/SmartStore.Web/Views/ShoppingCart/Wishlist.cshtml b/src/Presentation/SmartStore.Web/Views/ShoppingCart/Wishlist.cshtml index c547c7a46a..d7a2bb0a33 100644 --- a/src/Presentation/SmartStore.Web/Views/ShoppingCart/Wishlist.cshtml +++ b/src/Presentation/SmartStore.Web/Views/ShoppingCart/Wishlist.cshtml @@ -83,7 +83,7 @@ {
    or other required elements. + thead: [ 1, "
    ", "
    " ], + col: [ 2, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + + _default: [ 0, "", "" ] +}; + +// Support: IE <=9 only +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); +var documentElement = document.documentElement; + + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 only +// See #13393 for more info +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + // Make a writable jQuery.Event from the native event object + var event = jQuery.event.fix( nativeEvent ); + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + /* eslint-disable max-len */ + + // See https://github.com/eslint/eslint/issues/3229 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, + + /* eslint-enable */ + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), doc, node ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + div.style.position = "absolute"; + scrollboxSizeVal = div.offsetWidth === 36 || "absolute"; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }, + + cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style; + +// Return a css property mapped to a potentially vendor prefixed property +function vendorPropName( name ) { + + // Shortcut for names that are not vendor prefixed + if ( name in emptyStyle ) { + return name; + } + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a property mapped along what jQuery.cssProps suggests or to +// a vendor prefixed property. +function finalPropName( name ) { + var ret = jQuery.cssProps[ name ]; + if ( !ret ) { + ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name; + } + return ret; +} + +function setPositiveNumber( elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + ) ); + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + val = curCSS( elem, dimension, styles ), + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox; + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + // Check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = valueIsBorderBox && + ( support.boxSizingReliable() || val === elem.style[ dimension ] ); + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + if ( val === "auto" || + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) { + + val = elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ]; + + // offsetWidth/offsetHeight provide border-box values + valueIsBorderBox = true; + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + if ( type === "number" ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra && boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ); + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && support.scrollboxSize() === styles.position ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && + ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || + jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = Date.now(); + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match == null ? null : match; + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + + +jQuery._evalUrl = function( url ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + "throws": true + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( "